<commit_before>use std::collections::HashMap;

use ::{Logic,Expect};
use eval::Eval;
use var::Var;

/// delimited by new line
#[derive(Debug,PartialEq)]
pub enum Src {
    Logic(String, Logic), // ex: item_logic has_item

    // references logic in env and emits varkinds;
    // logic must resolve to true
    // ex: if item_logic give_quest
    // Can optionally end execution and begin next node
    If(Expect, Vec<Var>, Option<(String,bool)>),
    Or(Vec<Var>,Option<(String,bool)>), //must follow an previous IF

    Emit(Vec<Var>), //just emits variables

    Composite(String,Expect,Vec<String>),
    Next(String), // ends execution and begins next node

    Await(Option<String>), // pauses execution, saving the iteration, optional next node
}

impl Src {
    pub fn eval<D:Eval> (&self, state: &mut HashMap<String,bool>, data: &D)
                     -> (Vec<Var>,Option<(String,bool)>)
    {
        match self {
            &Src::Next(ref node) => {
                return (vec![],Some((node.clone(),false)))
            },
            &Src::Or(ref vars, ref node) => {
                return (vars.clone(),node.clone())
            },
            &Src::Emit(ref vars) => {
                return (vars.clone(),None)
            },
            &Src::Await(ref nn) => {
                if let &Some(ref node) = nn {
                    return (vec![], Some((node.clone(),true)))
                }

                return (vec![], None)
            },
            &Src::Logic(ref name, ref logic) => { //logic updates state
                let name = name.clone();
                match logic {
                    &Logic::Is(ref lookup) => {
                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {

                                Var::Bool(v) => { state.insert(name,v); },
                                _ => { state.insert(name,true); }, //if exists?
                            }
                        }
                        else { //check state table: some_thing !some_otherthing
                            let mut val = None;
                            if let Some(r) = state.get(lookup) {
                                val = Some(*r);
                            }

                            if let Some(val) = val {
                                state.insert(name,val);
                            }
                        }
                    },
                    &Logic::IsNot(ref lookup) => { //inverse state
                        let r = data.eval(&lookup);

                        if r.is_some() {
                            match r.unwrap() {
                                Var::Bool(v) => {
                                    if !v { state.insert(name,true); }
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                        else {
                            let mut val = None;
                            if let Some(r) = state.get(lookup) {
                                val = Some(!r);
                            }

                            if let Some(val) = val {
                                state.insert(name,val);
                            }
                        }
                    },

                    &Logic::GT(ref left, ref right) => {
                        let right = Var::get_num::<D>(right,data);
                        let left = Var::get_num::<D>(left,data);

                        if left.is_ok() && right.is_ok() {
                            state.insert(name, left.unwrap() > right.unwrap());
                        }
                    },
                    &Logic::LT(ref left, ref right) => {
                        let right = Var::get_num::<D>(right,data);
                        let left = Var::get_num::<D>(left,data);

                        if left.is_ok() && right.is_ok() {
                            state.insert(name, left.unwrap() < right.unwrap());
                        }
                    },
                }

                return (vec![],None) // logic does not return anything
            },
            &Src::Composite(ref name, ref x, ref lookups) => {
                let mut comp_value = false;
                match x {
                    &Expect::All => { // all must pass as true
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = true;
                            }
                            else { comp_value = false; break }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &Expect::Any => { // first truth passes for set
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = true;
                                break;
                            }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &Expect::None => { // inverse of any, none must be true
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = false;
                                break;
                            }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &Expect::Ref(_) => panic!("ERROR: Unexpected parsing") // this should never hit
                }

                return (vec![],None) // composite does not return anything
            },
            &Src::If(ref x, ref v, ref node) => {
                let mut if_value = false;
                match x {
                    &Expect::All => {
                        for n in state.values() {
                            if !n { if_value = false; break }
                            else { if_value = true; }
                        }
                    },
                    &Expect::Any => {
                        for n in state.values() {
                            if *n { if_value = true; break }
                        }
                    },
                    &Expect::None => {
                        for n in state.values() {
                            if !n { if_value = true; }
                            else { if_value = true; break }
                        }
                    },
                    &Expect::Ref(ref lookup) => {
                        let has_val = {
                            let val = state.get(lookup);
                            if let Some(val) = val {
                                if_value = *val;
                            }

                            val.is_some()
                        };

                        if !has_val {
                            if let Some(val) = data.eval(lookup) {
                                match val {
                                    Var::Bool(v) => { if_value = v; },
                                    _ => { if_value = true; }
                                }
                            }
                        }
                    },
                }

                if if_value { return ((*v).clone(),node.clone()) }
                else { return (vec![],None) }
            }
        }
    }

    pub fn parse(mut exp: Vec<String>) -> Src {
        if exp[0] == "if" {
            if exp.len() < 3 { panic!("ERROR: Invalid IF Logic {:?}",exp) }

            let x = exp.remove(1);

            let mut node = None;
            let mut await = false;
            if exp.len() > 2 {
                let next = &exp[exp.len() - 2] == "next";
                await = &exp[exp.len() - 2] == "await";
                if next || await {
                    node = exp.pop();
                    let _ = exp.pop(); // remove next tag
                }
            }

            let v = exp.drain(1..).map(|n| Var::parse(n)).collect();
            if let Some(node) = node {
                return Src::If(Expect::parse(x),
                               v, Some((node,await)))
            }

            Src::If(Expect::parse(x),
                    v, None)
        }
        else if exp[0] == "or" {
            if exp.len() < 2 { panic!("ERROR: Invalid OR Logic {:?}",exp) }

            let mut node = None;
            let mut await = false;
            if exp.len() > 2 {
                let next = &exp[exp.len() - 2] == "next";
                await = &exp[exp.len() - 2] == "await";
                if next || await {
                    node = exp.pop();
                    let _ = exp.pop(); // remove next tag
                }
            }

            let v = exp.drain(1..).map(|n| Var::parse(n)).collect();
            if let Some(node) = node {
                return Src::Or(v, Some((node,await)))
            }

            Src::Or(v,None)
        }
        else if exp[0] == "next" {
            if exp.len() == 2 {
                Src::Next(exp.pop().unwrap())
            }
            else { panic!("ERROR: Uneven NEXT Logic {:?}",exp) }
        }
        else if exp[0] == "await" {
            if exp.len() == 2 {
                Src::Await(Some(exp.pop().unwrap()))
            }
            else {
                Src::Await(None)
            }
        }
        else if exp[0] == "emit" {
            if exp.len() > 1 {
                let mut v = vec![];
                for e in exp.drain(1..) {
                    v.push(Var::parse(e));
                }

                Src::Emit(v)
            }
            else { panic!("ERROR: Missing EMIT Logic {:?}",exp) }
        }
        else {
            let keys = exp.remove(0);
            let mut keys: Vec<&str> = keys.split_terminator(':').collect();

            if keys.len() < 2 { // regular logic
                Src::Logic(keys.pop().unwrap().to_owned(),
                               Logic::parse(exp))
            }
            else { // composite type
                let kind = Expect::parse(keys.pop().unwrap().to_owned());
                match kind { // only formal expected types allowed
                    Expect::Ref(_) => { panic!("ERROR: Informal Expect found {:?}", kind) },
                    _ => {}
                }
                Src::Composite(keys.pop().unwrap().to_owned(),
                                   kind,
                                   exp)
            }
        }
    }
}
<commit_msg>eval data during comp<commit_after>use std::collections::HashMap;

use ::{Logic,Expect};
use eval::Eval;
use var::Var;

/// delimited by new line
#[derive(Debug,PartialEq)]
pub enum Src {
    Logic(String, Logic), // ex: item_logic has_item

    // references logic in env and emits varkinds;
    // logic must resolve to true
    // ex: if item_logic give_quest
    // Can optionally end execution and begin next node
    If(Expect, Vec<Var>, Option<(String,bool)>),
    Or(Vec<Var>,Option<(String,bool)>), //must follow an previous IF

    Emit(Vec<Var>), //just emits variables

    Composite(String,Expect,Vec<String>),
    Next(String), // ends execution and begins next node

    Await(Option<String>), // pauses execution, saving the iteration, optional next node
}

impl Src {
    pub fn eval<D:Eval> (&self, state: &mut HashMap<String,bool>, data: &D)
                     -> (Vec<Var>,Option<(String,bool)>)
    {
        match self {
            &Src::Next(ref node) => {
                return (vec![],Some((node.clone(),false)))
            },
            &Src::Or(ref vars, ref node) => {
                return (vars.clone(),node.clone())
            },
            &Src::Emit(ref vars) => {
                return (vars.clone(),None)
            },
            &Src::Await(ref nn) => {
                if let &Some(ref node) = nn {
                    return (vec![], Some((node.clone(),true)))
                }

                return (vec![], None)
            },
            &Src::Logic(ref name, ref logic) => { //logic updates state
                let name = name.clone();
                match logic {
                    &Logic::Is(ref lookup) => {
                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {

                                Var::Bool(v) => { state.insert(name,v); },
                                _ => { state.insert(name,true); }, //if exists?
                            }
                        }
                        else { //check state table: some_thing !some_otherthing
                            let mut val = None;
                            if let Some(r) = state.get(lookup) {
                                val = Some(*r);
                            }

                            if let Some(val) = val {
                                state.insert(name,val);
                            }
                        }
                    },
                    &Logic::IsNot(ref lookup) => { //inverse state
                        let r = data.eval(&lookup);

                        if r.is_some() {
                            match r.unwrap() {
                                Var::Bool(v) => {
                                    if !v { state.insert(name,true); }
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                        else {
                            let mut val = None;
                            if let Some(r) = state.get(lookup) {
                                val = Some(!r);
                            }

                            if let Some(val) = val {
                                state.insert(name,val);
                            }
                        }
                    },

                    &Logic::GT(ref left, ref right) => {
                        let right = Var::get_num::<D>(right,data);
                        let left = Var::get_num::<D>(left,data);

                        if left.is_ok() && right.is_ok() {
                            state.insert(name, left.unwrap() > right.unwrap());
                        }
                    },
                    &Logic::LT(ref left, ref right) => {
                        let right = Var::get_num::<D>(right,data);
                        let left = Var::get_num::<D>(left,data);

                        if left.is_ok() && right.is_ok() {
                            state.insert(name, left.unwrap() < right.unwrap());
                        }
                    },
                }

                return (vec![],None) // logic does not return anything
            },
            &Src::Composite(ref name, ref x, ref lookups) => {
                // track if any lookups are false or true
                let mut comp_false = false;
                let mut comp_true = false;

                for lookup in lookups.iter() {
                    let val = state.get(lookup);
                    if val.is_some() && *val.unwrap() {
                        comp_true = true;
                    }
                    else {
                        if val.is_some() { //found it but it's false
                            comp_false = true;
                        }
                        else { //check data for delayed reference
                            if let Some(val) = data.eval(lookup) {
                                match val {
                                    Var::Bool(b) => {
                                        if b { comp_true = true; }
                                        else { comp_false = true; }
                                    }
                                    _ => { comp_true = true; } //identity/exists, true
                                }
                            }
                        }
                    }
                }

                match x {
                    &Expect::All => { // all must pass as true
                        if comp_true && !comp_false {
                            state.insert(name.clone(),true);
                        }
                    },
                    &Expect::Any => { // first truth passes for set
                        if comp_true {
                            state.insert(name.clone(),true);
                        }
                    },
                    &Expect::None => { // inverse of any, none must be true
                        if !comp_true && comp_false {
                            state.insert(name.clone(),true);
                        }
                    },
                    &Expect::Ref(_) => panic!("ERROR: Unexpected parsing") // this should never hit
                }

                return (vec![],None) // composite does not return anything
            },
            &Src::If(ref x, ref v, ref node) => {
                let mut if_value = false;
                match x {
                    &Expect::All => {
                        for n in state.values() {
                            if !n { if_value = false; break }
                            else { if_value = true; }
                        }
                    },
                    &Expect::Any => {
                        for n in state.values() {
                            if *n { if_value = true; break }
                        }
                    },
                    &Expect::None => {
                        for n in state.values() {
                            if !n { if_value = true; }
                            else { if_value = true; break }
                        }
                    },
                    &Expect::Ref(ref lookup) => {
                        let has_val = {
                            let val = state.get(lookup);
                            if let Some(val) = val {
                                if_value = *val;
                            }

                            val.is_some()
                        };

                        if !has_val {
                            if let Some(val) = data.eval(lookup) {
                                match val {
                                    Var::Bool(v) => { if_value = v; },
                                    _ => { if_value = true; }
                                }
                            }
                        }
                    },
                }

                if if_value { return ((*v).clone(),node.clone()) }
                else { return (vec![],None) }
            }
        }
    }

    pub fn parse(mut exp: Vec<String>) -> Src {
        if exp[0] == "if" {
            if exp.len() < 3 { panic!("ERROR: Invalid IF Logic {:?}",exp) }

            let x = exp.remove(1);

            let mut node = None;
            let mut await = false;
            if exp.len() > 2 {
                let next = &exp[exp.len() - 2] == "next";
                await = &exp[exp.len() - 2] == "await";
                if next || await {
                    node = exp.pop();
                    let _ = exp.pop(); // remove next tag
                }
            }

            let v = exp.drain(1..).map(|n| Var::parse(n)).collect();
            if let Some(node) = node {
                return Src::If(Expect::parse(x),
                               v, Some((node,await)))
            }

            Src::If(Expect::parse(x),
                    v, None)
        }
        else if exp[0] == "or" {
            if exp.len() < 2 { panic!("ERROR: Invalid OR Logic {:?}",exp) }

            let mut node = None;
            let mut await = false;
            if exp.len() > 2 {
                let next = &exp[exp.len() - 2] == "next";
                await = &exp[exp.len() - 2] == "await";
                if next || await {
                    node = exp.pop();
                    let _ = exp.pop(); // remove next tag
                }
            }

            let v = exp.drain(1..).map(|n| Var::parse(n)).collect();
            if let Some(node) = node {
                return Src::Or(v, Some((node,await)))
            }

            Src::Or(v,None)
        }
        else if exp[0] == "next" {
            if exp.len() == 2 {
                Src::Next(exp.pop().unwrap())
            }
            else { panic!("ERROR: Uneven NEXT Logic {:?}",exp) }
        }
        else if exp[0] == "await" {
            if exp.len() == 2 {
                Src::Await(Some(exp.pop().unwrap()))
            }
            else {
                Src::Await(None)
            }
        }
        else if exp[0] == "emit" {
            if exp.len() > 1 {
                let mut v = vec![];
                for e in exp.drain(1..) {
                    v.push(Var::parse(e));
                }

                Src::Emit(v)
            }
            else { panic!("ERROR: Missing EMIT Logic {:?}",exp) }
        }
        else {
            let keys = exp.remove(0);
            let mut keys: Vec<&str> = keys.split_terminator(':').collect();

            if keys.len() < 2 { // regular logic
                Src::Logic(keys.pop().unwrap().to_owned(),
                               Logic::parse(exp))
            }
            else { // composite type
                let kind = Expect::parse(keys.pop().unwrap().to_owned());
                match kind { // only formal expected types allowed
                    Expect::Ref(_) => { panic!("ERROR: Informal Expect found {:?}", kind) },
                    _ => {}
                }
                Src::Composite(keys.pop().unwrap().to_owned(),
                                   kind,
                                   exp)
            }
        }
    }
}
<|endoftext|>


<commit_before>pub(crate) mod tags;

mod highlights;
mod injector;

mod highlight;
mod format;
mod macro_;
mod inject;

mod html;
#[cfg(test)]
mod tests;

use hir::{InFile, Name, Semantics};
use ide_db::{RootDatabase, SymbolKind};
use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, HasFormatSpecifier},
    match_ast, AstNode, AstToken, Direction, NodeOrToken,
    SyntaxKind::*,
    SyntaxNode, TextRange, WalkEvent, T,
};

use crate::{
    syntax_highlighting::{
        format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter,
        tags::Highlight,
    },
    FileId, HlMod, HlTag,
};

pub(crate) use html::highlight_as_html;

#[derive(Debug, Clone, Copy)]
pub struct HlRange {
    pub range: TextRange,
    pub highlight: Highlight,
    pub binding_hash: Option<u64>,
}

// Feature: Semantic Syntax Highlighting
//
// rust-analyzer highlights the code semantically.
// For example, `Bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
// rust-analyzer does not specify colors directly, instead it assigns a tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
// It's up to the client to map those to specific colors.
//
// The general rule is that a reference to an entity gets colored the same way as the entity itself.
// We also give special modifier for `mut` and `&mut` local variables.
//
//
// .Token Tags
//
// Rust-analyzer currently emits the following token tags:
//
// - For items:
// +
// [horizontal]
// enum:: Emitted for enums.
// function:: Emitted for free-standing functions.
// macro:: Emitted for macros.
// method:: Emitted for associated functions, also knowns as methods.
// namespace:: Emitted for modules.
// struct:: Emitted for structs.
// trait:: Emitted for traits.
// typeAlias:: Emitted for type aliases and `Self` in `impl`s.
// union:: Emitted for unions.
//
// - For literals:
// +
// [horizontal]
// boolean:: Emitted for the boolean literals `true` and `false`.
// character:: Emitted for character literals.
// number:: Emitted for numeric literals.
// string:: Emitted for string literals.
// escapeSequence:: Emitted for escaped sequences inside strings like `\n`.
// formatSpecifier:: Emitted for format specifiers `{:?}` in `format!`-like macros.
//
// - For operators:
// +
// [horizontal]
// operator:: Emitted for general operators.
// arithmetic:: Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.
// bitwise:: Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.
// comparison:: Emitted for the comparison operators `>`, `<`, `==`, `>=`, `<=`, `!=`.
// logical:: Emitted for the logical operators `||`, `&&`, `!`.
//
// - For punctuation:
// +
// [horizontal]
// punctuation:: Emitted for general punctuation.
// angle:: Emitted for `<>` angle brackets.
// brace:: Emitted for `{}` braces.
// bracket:: Emitted for `[]` brackets.
// parenthesis:: Emitted for `()` parentheses.
// colon:: Emitted for the `:` token.
// comma:: Emitted for the `,` token.
// dot:: Emitted for the `.` token.
// Semi:: Emitted for the `;` token.
//
// //-
//
// [horizontal]
// attribute:: Emitted for the `#[` `]` tokens.
// builtinAttribute:: Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.
// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`.
// comment:: Emitted for comments.
// constParameter:: Emitted for const parameters.
// enumMember:: Emitted for enum variants.
// generic:: Emitted for generic tokens that have no mapping.
// keyword:: Emitted for keywords.
// label:: Emitted for labels.
// lifetime:: Emitted for lifetimes.
// parameter:: Emitted for non-self function parameters.
// property:: Emitted for struct and union fields.
// selfKeyword:: Emitted for the self function parameter and self path-specifier.
// typeParameter:: Emitted for type parameters.
// unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of.
// variable:: Emitted for locals, constants and statics.
//
//
// .Token Modifiers
//
// Token modifiers allow to style some elements in the source code more precisely.
//
// Rust-analyzer currently emits the following token modifiers:
//
// [horizontal]
// async:: Emitted for async functions and the `async` and `await` keywords.
// attribute:: Emitted for tokens inside attributes.
// callable:: Emitted for locals whose types implements one of the `Fn*` traits.
// constant:: Emitted for consts.
// consuming:: Emitted for locals that are being consumed when use in a function call.
// controlFlow:: Emitted for control-flow related tokens, this includes the `?` operator.
// declaration:: Emitted for names of definitions, like `foo` in `fn foo() {}`.
// defaultLibrary:: Emitted for items from built-in crates (std, core, alloc, test and proc_macro).
// documentation:: Emitted for documentation comments.
// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation.
// intraDocLink:: Emitted for intra doc links in doc-strings.
// library:: Emitted for items that are defined outside of the current crate.
// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`.
// public:: Emitted for items that are from the current crate and are `pub`.
// reference:: Emitted for locals behind a reference and functions taking `self` by reference.
// static:: Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts.
// trait:: Emitted for associated trait items.
// unsafe:: Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token.
//
//
// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[]
// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
pub(crate) fn highlight(
    db: &RootDatabase,
    file_id: FileId,
    range_to_highlight: Option<TextRange>,
    syntactic_name_ref_highlighting: bool,
) -> Vec<HlRange> {
    let _p = profile::span("highlight");
    let sema = Semantics::new(db);

    // Determine the root based on the given range.
    let (root, range_to_highlight) = {
        let source_file = sema.parse(file_id);
        let source_file = source_file.syntax();
        match range_to_highlight {
            Some(range) => {
                let node = match source_file.covering_element(range) {
                    NodeOrToken::Node(it) => it,
                    NodeOrToken::Token(it) => it.parent().unwrap_or_else(|| source_file.clone()),
                };
                (node, range)
            }
            None => (source_file.clone(), source_file.text_range()),
        }
    };

    let mut hl = highlights::Highlights::new(root.text_range());
    traverse(
        &mut hl,
        &sema,
        InFile::new(file_id.into(), &root),
        sema.scope(&root).krate(),
        range_to_highlight,
        syntactic_name_ref_highlighting,
    );
    hl.to_vec()
}

fn traverse(
    hl: &mut Highlights,
    sema: &Semantics<RootDatabase>,
    root: InFile<&SyntaxNode>,
    krate: Option<hir::Crate>,
    range_to_highlight: TextRange,
    syntactic_name_ref_highlighting: bool,
) {
    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();

    let mut current_macro_call: Option<ast::MacroCall> = None;
    let mut current_attr_macro_call = None;
    let mut current_macro: Option<ast::Macro> = None;
    let mut macro_highlighter = MacroHighlighter::default();
    let mut inside_attribute = false;

    // Walk all nodes, keeping track of whether we are inside a macro or not.
    // If in macro, expand it first and highlight the expanded code.
    for event in root.value.preorder_with_tokens() {
        let event_range = match &event {
            WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(),
        };

        // Element outside of the viewport, no need to highlight
        if range_to_highlight.intersect(event_range).is_none() {
            continue;
        }

        match event.clone() {
            WalkEvent::Enter(NodeOrToken::Node(node)) => {
                match_ast! {
                    match node {
                        ast::MacroCall(mcall) => {
                            if let Some(range) = macro_call_range(&mcall) {
                                hl.add(HlRange {
                                    range,
                                    highlight: HlTag::Symbol(SymbolKind::Macro).into(),
                                    binding_hash: None,
                                });
                            }
                            current_macro_call = Some(mcall);
                            continue;
                        },
                        ast::Macro(mac) => {
                            macro_highlighter.init();
                            current_macro = Some(mac);
                            continue;
                        },
                        ast::Item(item) => {
                            if sema.is_attr_macro_call(&item) {
                                current_attr_macro_call = Some(item);
                            }
                        },
                        ast::Attr(__) => inside_attribute = true,
                        _ => ()
                    }
                }
            }
            WalkEvent::Leave(NodeOrToken::Node(node)) => {
                match_ast! {
                    match node {
                        ast::MacroCall(mcall) => {
                            assert_eq!(current_macro_call, Some(mcall));
                            current_macro_call = None;
                        },
                        ast::Macro(mac) => {
                            assert_eq!(current_macro, Some(mac));
                            current_macro = None;
                            macro_highlighter = MacroHighlighter::default();
                        },
                        ast::Item(item) => {
                            if current_attr_macro_call == Some(item) {
                                current_attr_macro_call = None;
                            }
                        },
                        ast::Attr(__) => inside_attribute = false,
                        _ => ()
                    }
                }
            }
            _ => (),
        }

        let element = match event {
            WalkEvent::Enter(it) => it,
            WalkEvent::Leave(it) => {
                if let Some(node) = it.as_node() {
                    inject::doc_comment(hl, sema, root.with_value(node));
                }
                continue;
            }
        };

        let range = element.text_range();

        if current_macro.is_some() {
            if let Some(tok) = element.as_token() {
                macro_highlighter.advance(tok);
            }
        }

        let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
            // Inside a macro -- expand it first
            let token = match element.clone().into_token() {
                Some(it) if it.parent().map_or(false, |it| it.kind() == TOKEN_TREE) => it,
                _ => continue,
            };
            let token = sema.descend_into_macros(token.clone());
            match token.parent() {
                Some(parent) => {
                    // We only care Name and Name_ref
                    match (token.kind(), parent.kind()) {
                        (IDENT, NAME | NAME_REF) => parent.into(),
                        _ => token.into(),
                    }
                }
                None => token.into(),
            }
        } else if current_attr_macro_call.is_some() {
            let token = match element.clone().into_token() {
                Some(it) => it,
                _ => continue,
            };
            let token = sema.descend_into_macros(token.clone());
            match token.parent() {
                Some(parent) => {
                    // We only care Name and Name_ref
                    match (token.kind(), parent.kind()) {
                        (IDENT, NAME | NAME_REF) => parent.into(),
                        _ => token.into(),
                    }
                }
                None => token.into(),
            }
        } else {
            element.clone()
        };

        if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) {
            if token.is_raw() {
                let expanded = element_to_highlight.as_token().unwrap().clone();
                if inject::ra_fixture(hl, sema, token, expanded).is_some() {
                    continue;
                }
            }
        }

        if let Some(_) = macro_highlighter.highlight(element_to_highlight.clone()) {
            continue;
        }

        if let Some((mut highlight, binding_hash)) = highlight::element(
            sema,
            krate,
            &mut bindings_shadow_count,
            syntactic_name_ref_highlighting,
            element_to_highlight.clone(),
        ) {
            if inside_attribute {
                highlight = highlight | HlMod::Attribute;
            }

            hl.add(HlRange { range, highlight, binding_hash });
        }

        if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) {
            highlight_format_string(hl, &string, range);
            // Highlight escape sequences
            if let Some(char_ranges) = string.char_ranges() {
                for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
                    if string.text()[piece_range.start().into()..].starts_with('\\') {
                        hl.add(HlRange {
                            range: piece_range + range.start(),
                            highlight: HlTag::EscapeSequence.into(),
                            binding_hash: None,
                        });
                    }
                }
            }
        }
    }
}

fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    let range_start = name_ref.syntax().text_range().start();
    let mut range_end = name_ref.syntax().text_range().end();
    for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
        match sibling.kind() {
            T![!] | IDENT => range_end = sibling.text_range().end(),
            _ => (),
        }
    }

    Some(TextRange::new(range_start, range_end))
}
<commit_msg>Simplify<commit_after>pub(crate) mod tags;

mod highlights;
mod injector;

mod highlight;
mod format;
mod macro_;
mod inject;

mod html;
#[cfg(test)]
mod tests;

use hir::{InFile, Name, Semantics};
use ide_db::{RootDatabase, SymbolKind};
use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, HasFormatSpecifier},
    match_ast, AstNode, AstToken, Direction, NodeOrToken,
    SyntaxKind::*,
    SyntaxNode, TextRange, WalkEvent, T,
};

use crate::{
    syntax_highlighting::{
        format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter,
        tags::Highlight,
    },
    FileId, HlMod, HlTag,
};

pub(crate) use html::highlight_as_html;

#[derive(Debug, Clone, Copy)]
pub struct HlRange {
    pub range: TextRange,
    pub highlight: Highlight,
    pub binding_hash: Option<u64>,
}

// Feature: Semantic Syntax Highlighting
//
// rust-analyzer highlights the code semantically.
// For example, `Bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
// rust-analyzer does not specify colors directly, instead it assigns a tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
// It's up to the client to map those to specific colors.
//
// The general rule is that a reference to an entity gets colored the same way as the entity itself.
// We also give special modifier for `mut` and `&mut` local variables.
//
//
// .Token Tags
//
// Rust-analyzer currently emits the following token tags:
//
// - For items:
// +
// [horizontal]
// enum:: Emitted for enums.
// function:: Emitted for free-standing functions.
// macro:: Emitted for macros.
// method:: Emitted for associated functions, also knowns as methods.
// namespace:: Emitted for modules.
// struct:: Emitted for structs.
// trait:: Emitted for traits.
// typeAlias:: Emitted for type aliases and `Self` in `impl`s.
// union:: Emitted for unions.
//
// - For literals:
// +
// [horizontal]
// boolean:: Emitted for the boolean literals `true` and `false`.
// character:: Emitted for character literals.
// number:: Emitted for numeric literals.
// string:: Emitted for string literals.
// escapeSequence:: Emitted for escaped sequences inside strings like `\n`.
// formatSpecifier:: Emitted for format specifiers `{:?}` in `format!`-like macros.
//
// - For operators:
// +
// [horizontal]
// operator:: Emitted for general operators.
// arithmetic:: Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.
// bitwise:: Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.
// comparison:: Emitted for the comparison operators `>`, `<`, `==`, `>=`, `<=`, `!=`.
// logical:: Emitted for the logical operators `||`, `&&`, `!`.
//
// - For punctuation:
// +
// [horizontal]
// punctuation:: Emitted for general punctuation.
// angle:: Emitted for `<>` angle brackets.
// brace:: Emitted for `{}` braces.
// bracket:: Emitted for `[]` brackets.
// parenthesis:: Emitted for `()` parentheses.
// colon:: Emitted for the `:` token.
// comma:: Emitted for the `,` token.
// dot:: Emitted for the `.` token.
// Semi:: Emitted for the `;` token.
//
// //-
//
// [horizontal]
// attribute:: Emitted for the `#[` `]` tokens.
// builtinAttribute:: Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.
// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`.
// comment:: Emitted for comments.
// constParameter:: Emitted for const parameters.
// enumMember:: Emitted for enum variants.
// generic:: Emitted for generic tokens that have no mapping.
// keyword:: Emitted for keywords.
// label:: Emitted for labels.
// lifetime:: Emitted for lifetimes.
// parameter:: Emitted for non-self function parameters.
// property:: Emitted for struct and union fields.
// selfKeyword:: Emitted for the self function parameter and self path-specifier.
// typeParameter:: Emitted for type parameters.
// unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of.
// variable:: Emitted for locals, constants and statics.
//
//
// .Token Modifiers
//
// Token modifiers allow to style some elements in the source code more precisely.
//
// Rust-analyzer currently emits the following token modifiers:
//
// [horizontal]
// async:: Emitted for async functions and the `async` and `await` keywords.
// attribute:: Emitted for tokens inside attributes.
// callable:: Emitted for locals whose types implements one of the `Fn*` traits.
// constant:: Emitted for consts.
// consuming:: Emitted for locals that are being consumed when use in a function call.
// controlFlow:: Emitted for control-flow related tokens, this includes the `?` operator.
// declaration:: Emitted for names of definitions, like `foo` in `fn foo() {}`.
// defaultLibrary:: Emitted for items from built-in crates (std, core, alloc, test and proc_macro).
// documentation:: Emitted for documentation comments.
// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation.
// intraDocLink:: Emitted for intra doc links in doc-strings.
// library:: Emitted for items that are defined outside of the current crate.
// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`.
// public:: Emitted for items that are from the current crate and are `pub`.
// reference:: Emitted for locals behind a reference and functions taking `self` by reference.
// static:: Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts.
// trait:: Emitted for associated trait items.
// unsafe:: Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token.
//
//
// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[]
// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
pub(crate) fn highlight(
    db: &RootDatabase,
    file_id: FileId,
    range_to_highlight: Option<TextRange>,
    syntactic_name_ref_highlighting: bool,
) -> Vec<HlRange> {
    let _p = profile::span("highlight");
    let sema = Semantics::new(db);

    // Determine the root based on the given range.
    let (root, range_to_highlight) = {
        let source_file = sema.parse(file_id);
        let source_file = source_file.syntax();
        match range_to_highlight {
            Some(range) => {
                let node = match source_file.covering_element(range) {
                    NodeOrToken::Node(it) => it,
                    NodeOrToken::Token(it) => it.parent().unwrap_or_else(|| source_file.clone()),
                };
                (node, range)
            }
            None => (source_file.clone(), source_file.text_range()),
        }
    };

    let mut hl = highlights::Highlights::new(root.text_range());
    traverse(
        &mut hl,
        &sema,
        InFile::new(file_id.into(), &root),
        sema.scope(&root).krate(),
        range_to_highlight,
        syntactic_name_ref_highlighting,
    );
    hl.to_vec()
}

fn traverse(
    hl: &mut Highlights,
    sema: &Semantics<RootDatabase>,
    root: InFile<&SyntaxNode>,
    krate: Option<hir::Crate>,
    range_to_highlight: TextRange,
    syntactic_name_ref_highlighting: bool,
) {
    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();

    let mut current_macro_call: Option<ast::MacroCall> = None;
    let mut current_attr_call = None;
    let mut current_macro: Option<ast::Macro> = None;
    let mut macro_highlighter = MacroHighlighter::default();
    let mut inside_attribute = false;

    // Walk all nodes, keeping track of whether we are inside a macro or not.
    // If in macro, expand it first and highlight the expanded code.
    for event in root.value.preorder_with_tokens() {
        let event_range = match &event {
            WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(),
        };

        // Element outside of the viewport, no need to highlight
        if range_to_highlight.intersect(event_range).is_none() {
            continue;
        }

        match event.clone() {
            WalkEvent::Enter(NodeOrToken::Node(node)) => {
                match_ast! {
                    match node {
                        ast::MacroCall(mcall) => {
                            if let Some(range) = macro_call_range(&mcall) {
                                hl.add(HlRange {
                                    range,
                                    highlight: HlTag::Symbol(SymbolKind::Macro).into(),
                                    binding_hash: None,
                                });
                            }
                            current_macro_call = Some(mcall);
                            continue;
                        },
                        ast::Macro(mac) => {
                            macro_highlighter.init();
                            current_macro = Some(mac);
                            continue;
                        },
                        ast::Item(item) => {
                            if sema.is_attr_macro_call(&item) {
                                current_attr_call = Some(item);
                            }
                        },
                        ast::Attr(__) => inside_attribute = true,
                        _ => ()
                    }
                }
            }
            WalkEvent::Leave(NodeOrToken::Node(node)) => {
                match_ast! {
                    match node {
                        ast::MacroCall(mcall) => {
                            assert_eq!(current_macro_call, Some(mcall));
                            current_macro_call = None;
                        },
                        ast::Macro(mac) => {
                            assert_eq!(current_macro, Some(mac));
                            current_macro = None;
                            macro_highlighter = MacroHighlighter::default();
                        },
                        ast::Item(item) => {
                            if current_attr_call == Some(item) {
                                current_attr_call = None;
                            }
                        },
                        ast::Attr(__) => inside_attribute = false,
                        _ => ()
                    }
                }
            }
            _ => (),
        }

        let element = match event {
            WalkEvent::Enter(it) => it,
            WalkEvent::Leave(it) => {
                if let Some(node) = it.as_node() {
                    inject::doc_comment(hl, sema, root.with_value(node));
                }
                continue;
            }
        };

        let range = element.text_range();

        if current_macro.is_some() {
            if let Some(tok) = element.as_token() {
                macro_highlighter.advance(tok);
            }
        }

        let descend_token = (current_macro_call.is_some() || current_attr_call.is_some())
            && element.kind() != COMMENT;
        let element_to_highlight = if descend_token {
            // Inside a macro -- expand it first
            let token = match element.clone().into_token() {
                Some(it) if current_macro_call.is_some() => {
                    let not_in_tt = it.parent().map_or(true, |it| it.kind() != TOKEN_TREE);
                    if not_in_tt {
                        continue;
                    }
                    it
                }
                Some(it) => it,
                _ => continue,
            };
            let token = sema.descend_into_macros(token);
            match token.parent() {
                Some(parent) => {
                    // We only care Name and Name_ref
                    match (token.kind(), parent.kind()) {
                        (T![ident], NAME | NAME_REF) => parent.into(),
                        (T![self] | T![super] | T![crate], NAME_REF) => parent.into(),
                        _ => token.into(),
                    }
                }
                None => token.into(),
            }
        } else {
            element.clone()
        };

        if let Some(token) = element.into_token().and_then(ast::String::cast) {
            if token.is_raw() {
                if let Some(expanded) = element_to_highlight.as_token() {
                    if inject::ra_fixture(hl, sema, token, expanded.clone()).is_some() {
                        continue;
                    }
                }
            }
        }

        if let Some(_) = macro_highlighter.highlight(element_to_highlight.clone()) {
            continue;
        }

        if let Some((mut highlight, binding_hash)) = highlight::element(
            sema,
            krate,
            &mut bindings_shadow_count,
            syntactic_name_ref_highlighting,
            element_to_highlight.clone(),
        ) {
            if inside_attribute {
                highlight = highlight | HlMod::Attribute;
            }

            hl.add(HlRange { range, highlight, binding_hash });
        }

        if let Some(string) = element_to_highlight.into_token().and_then(ast::String::cast) {
            highlight_format_string(hl, &string, range);
            // Highlight escape sequences
            if let Some(char_ranges) = string.char_ranges() {
                for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
                    if string.text()[piece_range.start().into()..].starts_with('\\') {
                        hl.add(HlRange {
                            range: piece_range + range.start(),
                            highlight: HlTag::EscapeSequence.into(),
                            binding_hash: None,
                        });
                    }
                }
            }
        }
    }
}

fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    let range_start = name_ref.syntax().text_range().start();
    let mut range_end = name_ref.syntax().text_range().end();
    for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
        if let T![!] | T![ident] = sibling.kind() {
            range_end = sibling.text_range().end();
        }
    }

    Some(TextRange::new(range_start, range_end))
}
<|endoftext|>


<commit_before>use serde_derive::{Deserialize, Serialize};
use std::env;
use std::path::PathBuf;
use types::ChainSpec;

/// The core configuration of a Lighthouse beacon node.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Eth2Config {
    pub spec_constants: String,
    pub spec: ChainSpec,
}

impl Default for Eth2Config {
    fn default() -> Self {
        Self {
            spec_constants: "minimal".to_string(),
            spec: ChainSpec::minimal(),
        }
    }
}

impl Eth2Config {
    pub fn mainnet() -> Self {
        Self {
            spec_constants: "mainnet".to_string(),
            spec: ChainSpec::mainnet(),
        }
    }

    pub fn minimal() -> Self {
        Self {
            spec_constants: "minimal".to_string(),
            spec: ChainSpec::minimal(),
        }
    }

    pub fn interop() -> Self {
        Self {
            spec_constants: "interop".to_string(),
            spec: ChainSpec::interop(),
        }
    }
}

/// A directory that can be built by downloading files via HTTP.
///
/// Used by the `eth2_testnet_config` crate to initialize testnet directories during build and
/// access them at runtime.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Eth2NetDirectory<'a> {
    pub name: &'a str,
    pub unique_id: &'a str,
    pub commit: &'a str,
    pub url_template: &'a str,
    pub genesis_is_known: bool,
}

impl<'a> Eth2NetDirectory<'a> {
    /// The directory that should be used to store files downloaded for this net.
    pub fn dir(&self) -> PathBuf {
        env::var("CARGO_MANIFEST_DIR")
            .expect("should know manifest dir")
            .parse::<PathBuf>()
            .expect("should parse manifest dir as path")
            .join(self.unique_id)
    }
}

#[macro_export]
macro_rules! unique_id {
    ($name: tt, $commit: tt, $genesis_is_known: tt) => {
        concat!("testnet_", $name, "_", $commit, "_", $genesis_is_known);
    };
}

macro_rules! define_net {
    ($title: ident, $macro_title: tt, $name: tt, $commit: tt, $url_template: tt, $genesis_is_known: tt) => {
        #[macro_use]
        pub mod $title {
            use super::*;

            pub const ETH2_NET_DIR: Eth2NetDirectory = Eth2NetDirectory {
                name: $name,
                unique_id: unique_id!($name, $commit, $genesis_is_known),
                commit: $commit,
                url_template: $url_template,
                genesis_is_known: $genesis_is_known,
            };

            // A wrapper around `std::include_bytes` which includes a file from a specific testnet
            // directory. Used by upstream crates to import files at compile time.
            #[macro_export]
            macro_rules! $macro_title {
                ($base_dir: tt, $filename: tt) => {
                    include_bytes!(concat!(
                        $base_dir,
                        unique_id!($name, $commit, $genesis_is_known),
                        "/",
                        $filename
                    ))
                };
            }
        }
    };
}

define_net!(
    altona,
    include_altona_file,
    "altona",
    "a94e00c1a03df851f960fcf44a79f2a6b1d29af1",
    "https://raw.githubusercontent.com/sigp/witti/{{ commit }}/altona/lighthouse/{{ file }}",
    true
);

define_net!(
    medalla,
    include_medalla_file,
    "medalla",
    "b21fef76ddf472c6cea62d5c98b678033a9b195a",
    "https://raw.githubusercontent.com/sigp/witti/{{ commit }}/medalla/{{ file }}",
    false
);

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

    #[test]
    fn serde_serialize() {
        let _ =
            toml::to_string(&Eth2Config::default()).expect("Should serde encode default config");
    }
}
<commit_msg>Add Medalla genesis state, more boot enr (#1438)<commit_after>use serde_derive::{Deserialize, Serialize};
use std::env;
use std::path::PathBuf;
use types::ChainSpec;

/// The core configuration of a Lighthouse beacon node.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Eth2Config {
    pub spec_constants: String,
    pub spec: ChainSpec,
}

impl Default for Eth2Config {
    fn default() -> Self {
        Self {
            spec_constants: "minimal".to_string(),
            spec: ChainSpec::minimal(),
        }
    }
}

impl Eth2Config {
    pub fn mainnet() -> Self {
        Self {
            spec_constants: "mainnet".to_string(),
            spec: ChainSpec::mainnet(),
        }
    }

    pub fn minimal() -> Self {
        Self {
            spec_constants: "minimal".to_string(),
            spec: ChainSpec::minimal(),
        }
    }

    pub fn interop() -> Self {
        Self {
            spec_constants: "interop".to_string(),
            spec: ChainSpec::interop(),
        }
    }
}

/// A directory that can be built by downloading files via HTTP.
///
/// Used by the `eth2_testnet_config` crate to initialize testnet directories during build and
/// access them at runtime.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Eth2NetDirectory<'a> {
    pub name: &'a str,
    pub unique_id: &'a str,
    pub commit: &'a str,
    pub url_template: &'a str,
    pub genesis_is_known: bool,
}

impl<'a> Eth2NetDirectory<'a> {
    /// The directory that should be used to store files downloaded for this net.
    pub fn dir(&self) -> PathBuf {
        env::var("CARGO_MANIFEST_DIR")
            .expect("should know manifest dir")
            .parse::<PathBuf>()
            .expect("should parse manifest dir as path")
            .join(self.unique_id)
    }
}

#[macro_export]
macro_rules! unique_id {
    ($name: tt, $commit: tt, $genesis_is_known: tt) => {
        concat!("testnet_", $name, "_", $commit, "_", $genesis_is_known);
    };
}

macro_rules! define_net {
    ($title: ident, $macro_title: tt, $name: tt, $commit: tt, $url_template: tt, $genesis_is_known: tt) => {
        #[macro_use]
        pub mod $title {
            use super::*;

            pub const ETH2_NET_DIR: Eth2NetDirectory = Eth2NetDirectory {
                name: $name,
                unique_id: unique_id!($name, $commit, $genesis_is_known),
                commit: $commit,
                url_template: $url_template,
                genesis_is_known: $genesis_is_known,
            };

            // A wrapper around `std::include_bytes` which includes a file from a specific testnet
            // directory. Used by upstream crates to import files at compile time.
            #[macro_export]
            macro_rules! $macro_title {
                ($base_dir: tt, $filename: tt) => {
                    include_bytes!(concat!(
                        $base_dir,
                        unique_id!($name, $commit, $genesis_is_known),
                        "/",
                        $filename
                    ))
                };
            }
        }
    };
}

define_net!(
    altona,
    include_altona_file,
    "altona",
    "a94e00c1a03df851f960fcf44a79f2a6b1d29af1",
    "https://raw.githubusercontent.com/sigp/witti/{{ commit }}/altona/lighthouse/{{ file }}",
    true
);

define_net!(
    medalla,
    include_medalla_file,
    "medalla",
    "09bbf2c9d108944ac934f94ec6a1d0684ca062a5",
    "https://raw.githubusercontent.com/sigp/witti/{{ commit }}/medalla/{{ file }}",
    true
);

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

    #[test]
    fn serde_serialize() {
        let _ =
            toml::to_string(&Eth2Config::default()).expect("Should serde encode default config");
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use ast::Expression;
use error::{Error, ErrorKind, ErrorLevel};
use to_javascript::ToJavaScript;

type BuiltinMap = HashMap<&'static str, fn(&Vec<Box<Expression>>) -> Result<String, Error>>;

lazy_static! {
    pub static ref BUILTINS: BuiltinMap = {
        // The type definition is required to prevent compile errors
        let mut map: BuiltinMap = HashMap::new();

        map.insert("if", builtin_if);

        map
    };
}

pub fn builtin_if(args: &Vec<Box<Expression>>) -> Result<String, Error> {
    // TODO: Remove unwraps
    match args.len() {
        0 => {
            Err(Error(ErrorLevel::Error,
                      ErrorKind::TooFewArguments,
                      "Too few Arguments applied for if"))
        }
        1 => {
            Err(Error(ErrorLevel::Error,
                      ErrorKind::TooFewArguments,
                      "No expression applied for condition"))
        }
        2 => {
            Ok(format!("(if ({}) {{ {} }})",
                       args[0].eval().unwrap(),
                       args[1].eval().unwrap()))
        }
        3 => {
            Ok(format!("(if ({}) {{ {} }} else {{ {} }})",
                       args[0].eval().unwrap(),
                       args[1].eval().unwrap(),
                       args[2].eval().unwrap()))
        }
        _ => panic!("Unknown number of arguments supplied to if-statement"),
    }
}
<commit_msg>In JS, brackets around an if are invalid code. I removed them.<commit_after>use std::collections::HashMap;

use ast::Expression;
use error::{Error, ErrorKind, ErrorLevel};
use to_javascript::ToJavaScript;

type BuiltinMap = HashMap<&'static str, fn(&Vec<Box<Expression>>) -> Result<String, Error>>;

lazy_static! {
    pub static ref BUILTINS: BuiltinMap = {
        // The type definition is required to prevent compile errors
        let mut map: BuiltinMap = HashMap::new();

        map.insert("if", builtin_if);

        map
    };
}

pub fn builtin_if(args: &Vec<Box<Expression>>) -> Result<String, Error> {
    // TODO: Remove unwraps
    match args.len() {
        0 => {
            Err(Error(ErrorLevel::Error,
                      ErrorKind::TooFewArguments,
                      "Too few Arguments applied for if"))
        }
        1 => {
            Err(Error(ErrorLevel::Error,
                      ErrorKind::TooFewArguments,
                      "No expression applied for condition"))
        }
        2 => {
            Ok(format!("if ({}) {{ {} }}",
                       args[0].eval().unwrap(),
                       args[1].eval().unwrap()))
        }
        3 => {
            Ok(format!("if ({}) {{ {} }} else {{ {} }}",
                       args[0].eval().unwrap(),
                       args[1].eval().unwrap(),
                       args[2].eval().unwrap()))
        }
        _ => panic!("Unknown number of arguments supplied to if-statement"),
    }
}
<|endoftext|>


<commit_before>use nom::{Err, IResult};

use metadata;
use frame;
use subframe;

use metadata::{Metadata, StreamInfo, metadata_parser};
use frame::frame_parser;
use utility::{ErrorKind, ByteStream, ReadStream, StreamProducer};

use std::io;
use std::usize;
use std::fs::File;

/// FLAC stream that decodes and hold file information.
pub struct Stream
 {
  info: StreamInfo,
  metadata: Vec<Metadata>,
  producer: P,
  output: Vec<i32>,
}

impl
 Stream
 where P: StreamProducer {
  /// Constructor for the default state of a FLAC stream.
  #[inline]
  pub fn new<R: io::Read>(reader: R) -> io::Result<Stream<ReadStream<R>>> {
    let producer  = ReadStream::new(reader);
    let error_str = "parser: couldn't parse the reader";

    Stream::from_stream_producer(producer, error_str)
  }

  /// Returns information for the current stream.
  #[inline]
  pub fn info(&self) -> StreamInfo {
    self.info
  }

  /// Returns a slice of `Metadata`
  ///
  /// This slice excludes `StreamInfo`, which is located in `Stream::info`.
  /// Everything else is related to metadata for the FLAC stream is in the
  /// slice.
  #[inline]
  pub fn metadata(&self) -> &[Metadata] {
    &self.metadata
  }

  /// Constructs a decoder with the given file name.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::NotFound` is returned when the given filename isn't found.
  /// * `ErrorKind::InvalidData` is returned when the data within the file
  ///   isn't valid FLAC data.
  #[inline]
  pub fn from_file(filename: &str) -> io::Result<Stream<ReadStream<File>>> {
    File::open(filename).and_then(|file| {
      let producer  = ReadStream::new(file);
      let error_str = format!("parser: couldn't parse the given file {}",
                              filename);

      Stream::from_stream_producer(producer, &error_str)
    })
  }

  /// Constructs a decoder with the given buffer.
  ///
  /// This constructor assumes that an entire FLAC file is in the buffer.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::InvalidData` is returned when the data within the buffer
  ///   isn't valid FLAC data.
  #[inline]
  pub fn from_buffer(buffer: &[u8]) -> io::Result<Stream<ByteStream>> {
    let producer  = ByteStream::new(buffer);
    let error_str = "parser: couldn't parse the buffer";

    Stream::from_stream_producer(producer, error_str)
  }

  fn from_stream_producer(mut producer: P, error_str: &str)
                          -> io::Result<Self> {
    let mut is_start    = true;
    let mut is_error    = false;
    let mut stream_info = StreamInfo::new();
    let mut metadata    = Vec::new();

    loop {
      match producer.parse(|i| parser(i, &mut is_start)) {
        Ok(block)                  => {
          let is_last = block.is_last;

          if let metadata::Data::StreamInfo(info) = block.data {
            stream_info = info;
          } else {
            metadata.push(block);
          }

          if is_last {
            break;
          }
        }
        Err(ErrorKind::EndOfInput) => break,
        Err(ErrorKind::Continue)   => continue,
        Err(_)                     => {
          is_error = true;

          break;
        }
      }
    }

    if !is_error {
      Ok(Stream {
        info: stream_info,
        metadata: metadata,
        producer: producer,
        output: Vec::new(),
      })
    } else {
      Err(io::Error::new(io::ErrorKind::InvalidData, error_str))
    }
  }

  /// Returns an iterator over the decoded samples.
  #[inline]
  pub fn iter(&mut self) -> Iter
 {
    let samples_left = self.info.total_samples;

    if self.output.is_empty() {
      let channels    = self.info.channels as usize;
      let block_size  = self.info.max_block_size as usize;
      let output_size = block_size * channels;

      self.output.reserve_exact(output_size);

      unsafe { self.output.set_len(output_size) }
    }

    Iter {
      stream: self,
      channel: 0,
      block_size: 0,
      sample_index: 0,
      samples_left: samples_left,
    }
  }

  fn next_frame<'a>(&'a mut self) -> Option<usize> {
    let stream_info = self.info();

    loop {
      match self.producer.parse(|i| frame_parser(i, &stream_info)) {
        Ok(frame)                  => {
          let channels    = frame.header.channels as usize;
          let block_size  = frame.header.block_size as usize;
          let mut channel = 0;

          for subframe in &frame.subframes[0..channels] {
            let start  = channel * block_size;
            let end    = (channel + 1) * block_size;
            let output = &mut self.output[start..end];

            subframe::decode(&subframe, output);

            channel += 1;
          }

          frame::decode(frame.header.channel_assignment, &mut self.output);

          return Some(block_size);
        }
        Err(ErrorKind::EndOfInput) => return None,
        Err(ErrorKind::Continue)   => continue,
        Err(_)                     => return None,
      }
    }
  }
}

/// An iterator over a reference of the decoded FLAC stream.
pub struct Iter<'a, P> where P: 'a + StreamProducer {
  stream: &'a mut Stream
,
  channel: usize,
  block_size: usize,
  sample_index: usize,
  samples_left: u64,
}

impl<'a, P> Iterator for Iter<'a, P> where P: StreamProducer {
  type Item = i32;

  fn next(&mut self) -> Option<Self::Item> {
    if self.sample_index == self.block_size {
      if let Some(block_size) = self.stream.next_frame() {
        self.sample_index = 0;
        self.block_size   = block_size;
      } else {
        return None;
      }
    }

    let channels = self.stream.info.channels as usize;
    let index    = self.sample_index + (self.channel * self.block_size);
    let sample   = self.stream.output[index];

    self.channel += 1;

    // Reset current channel
    if self.channel == channels {
      self.channel       = 0;
      self.sample_index += 1;
      self.samples_left -= 1;
    }

    Some(sample)
  }

  fn size_hint(&self) -> (usize, Option<usize>) {
    let samples_left = self.samples_left as usize;
    let max_value    = usize::max_value() as u64;

    // There is a chance that samples_left will be larger than a usize since
    // it is a u64. Make the upper bound None when it is.
    if self.samples_left > max_value {
      (samples_left, None)
    } else {
      (samples_left, Some(samples_left))
    }
  }
}

impl<'a, P> IntoIterator for &'a mut Stream

 where P: StreamProducer {
  type Item     = i32;
  type IntoIter = Iter<'a, P>;

  #[inline]
  fn into_iter(self) -> Self::IntoIter {
    self.iter()
  }
}
<commit_msg>Change from_stream_producer to use many_metadata<commit_after>use nom::{Err, IResult};

use metadata;
use frame;
use subframe;

use metadata::{Metadata, StreamInfo, metadata_parser};
use frame::frame_parser;
use utility::{
  ErrorKind, ByteStream, ReadStream, StreamProducer, many_metadata,
};

use std::io;
use std::usize;
use std::fs::File;

/// FLAC stream that decodes and hold file information.
pub struct Stream
 {
  info: StreamInfo,
  metadata: Vec<Metadata>,
  producer: P,
  output: Vec<i32>,
}

impl
 Stream
 where P: StreamProducer {
  /// Constructor for the default state of a FLAC stream.
  #[inline]
  pub fn new<R: io::Read>(reader: R) -> io::Result<Stream<ReadStream<R>>> {
    let producer  = ReadStream::new(reader);
    let error_str = "parser: couldn't parse the reader";

    Stream::from_stream_producer(producer, error_str)
  }

  /// Returns information for the current stream.
  #[inline]
  pub fn info(&self) -> StreamInfo {
    self.info
  }

  /// Returns a slice of `Metadata`
  ///
  /// This slice excludes `StreamInfo`, which is located in `Stream::info`.
  /// Everything else is related to metadata for the FLAC stream is in the
  /// slice.
  #[inline]
  pub fn metadata(&self) -> &[Metadata] {
    &self.metadata
  }

  /// Constructs a decoder with the given file name.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::NotFound` is returned when the given filename isn't found.
  /// * `ErrorKind::InvalidData` is returned when the data within the file
  ///   isn't valid FLAC data.
  #[inline]
  pub fn from_file(filename: &str) -> io::Result<Stream<ReadStream<File>>> {
    File::open(filename).and_then(|file| {
      let producer  = ReadStream::new(file);
      let error_str = format!("parser: couldn't parse the given file {}",
                              filename);

      Stream::from_stream_producer(producer, &error_str)
    })
  }

  /// Constructs a decoder with the given buffer.
  ///
  /// This constructor assumes that an entire FLAC file is in the buffer.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::InvalidData` is returned when the data within the buffer
  ///   isn't valid FLAC data.
  #[inline]
  pub fn from_buffer(buffer: &[u8]) -> io::Result<Stream<ByteStream>> {
    let producer  = ByteStream::new(buffer);
    let error_str = "parser: couldn't parse the buffer";

    Stream::from_stream_producer(producer, error_str)
  }

  fn from_stream_producer(mut producer: P, error_str: &str)
                          -> io::Result<Self> {
    let mut stream_info = StreamInfo::new();
    let mut metadata    = Vec::new();

    let is_error = many_metadata(&mut producer, |block| {
      if let metadata::Data::StreamInfo(info) = block.data {
        stream_info = info;
      } else {
        metadata.push(block);
      }
    });

    if !is_error {
      Ok(Stream {
        info: stream_info,
        metadata: metadata,
        producer: producer,
        output: Vec::new(),
      })
    } else {
      Err(io::Error::new(io::ErrorKind::InvalidData, error_str))
    }
  }

  /// Returns an iterator over the decoded samples.
  #[inline]
  pub fn iter(&mut self) -> Iter
 {
    let samples_left = self.info.total_samples;

    if self.output.is_empty() {
      let channels    = self.info.channels as usize;
      let block_size  = self.info.max_block_size as usize;
      let output_size = block_size * channels;

      self.output.reserve_exact(output_size);

      unsafe { self.output.set_len(output_size) }
    }

    Iter {
      stream: self,
      channel: 0,
      block_size: 0,
      sample_index: 0,
      samples_left: samples_left,
    }
  }

  fn next_frame<'a>(&'a mut self) -> Option<usize> {
    let stream_info = self.info();

    loop {
      match self.producer.parse(|i| frame_parser(i, &stream_info)) {
        Ok(frame)                  => {
          let channels    = frame.header.channels as usize;
          let block_size  = frame.header.block_size as usize;
          let mut channel = 0;

          for subframe in &frame.subframes[0..channels] {
            let start  = channel * block_size;
            let end    = (channel + 1) * block_size;
            let output = &mut self.output[start..end];

            subframe::decode(&subframe, output);

            channel += 1;
          }

          frame::decode(frame.header.channel_assignment, &mut self.output);

          return Some(block_size);
        }
        Err(ErrorKind::EndOfInput) => return None,
        Err(ErrorKind::Continue)   => continue,
        Err(_)                     => return None,
      }
    }
  }
}

/// An iterator over a reference of the decoded FLAC stream.
pub struct Iter<'a, P> where P: 'a + StreamProducer {
  stream: &'a mut Stream
,
  channel: usize,
  block_size: usize,
  sample_index: usize,
  samples_left: u64,
}

impl<'a, P> Iterator for Iter<'a, P> where P: StreamProducer {
  type Item = i32;

  fn next(&mut self) -> Option<Self::Item> {
    if self.sample_index == self.block_size {
      if let Some(block_size) = self.stream.next_frame() {
        self.sample_index = 0;
        self.block_size   = block_size;
      } else {
        return None;
      }
    }

    let channels = self.stream.info.channels as usize;
    let index    = self.sample_index + (self.channel * self.block_size);
    let sample   = self.stream.output[index];

    self.channel += 1;

    // Reset current channel
    if self.channel == channels {
      self.channel       = 0;
      self.sample_index += 1;
      self.samples_left -= 1;
    }

    Some(sample)
  }

  fn size_hint(&self) -> (usize, Option<usize>) {
    let samples_left = self.samples_left as usize;
    let max_value    = usize::max_value() as u64;

    // There is a chance that samples_left will be larger than a usize since
    // it is a u64. Make the upper bound None when it is.
    if self.samples_left > max_value {
      (samples_left, None)
    } else {
      (samples_left, Some(samples_left))
    }
  }
}

impl<'a, P> IntoIterator for &'a mut Stream

 where P: StreamProducer {
  type Item     = i32;
  type IntoIter = Iter<'a, P>;

  #[inline]
  fn into_iter(self) -> Self::IntoIter {
    self.iter()
  }
}
<|endoftext|>


<commit_before>use super::Core;
use std::num::Wrapping;
use ram::ADDRBUS_MASK;

pub fn absolute_word(core: &mut Core) -> u32 {
	let ea = core.read_imm_i16() as u32;
	ea & ADDRBUS_MASK
}
pub fn absolute_long(core: &mut Core) -> u32 {
	let ea = core.read_imm_u32();
	ea & ADDRBUS_MASK
}
pub fn predecrement_ay_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	predecrement_8(core, reg_ndx)
}
pub fn postincrement_ay_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	postincrement_8(core, reg_ndx)
}
pub fn predecrement_ay_16(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	predecrement_16(core, reg_ndx)
}
pub fn postincrement_ay_16(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	postincrement_16(core, reg_ndx)
}
pub fn address_indirect_ay(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	core.dar[reg_ndx] & ADDRBUS_MASK
}
pub fn displacement_ay(core: &mut Core) -> u32 {
	let reg_val = core.dar[ir_ay!(core)];
	displacement(core, reg_val)
}
pub fn displacement_pc(core: &mut Core) -> u32 {
	let old_pc = core.pc;
	displacement(core, old_pc)
}
pub fn index_ay(core: &mut Core) -> u32 {
	let reg_val = core.dar[ir_ay!(core)];
	index(core, reg_val)
}
pub fn index_pc(core: &mut Core) -> u32 {
	let pc = core.pc;
	index(core, pc)
}
pub fn predecrement_ax_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ax!(core);
	predecrement_8(core, reg_ndx)
}
pub fn predecrement_ax_16(core: &mut Core) -> u32 {
	let reg_ndx = ir_ax!(core);
	predecrement_16(core, reg_ndx)
}

fn predecrement_8(core: &mut Core, reg_ndx: usize) -> u32 {
	// pre-decrement
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) - match reg_ndx {
		15 => Wrapping(2), // A7 is kept even
		 _ => Wrapping(1)
	}).0;
	core.dar[reg_ndx] & ADDRBUS_MASK
}
fn postincrement_8(core: &mut Core, reg_ndx: usize) -> u32 {
	// post-increment
	let ea = core.dar[reg_ndx];
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) + match reg_ndx {
		15 => Wrapping(2), // A7 is kept even
		 _ => Wrapping(1)
	}).0;
	ea & ADDRBUS_MASK
}
fn predecrement_16(core: &mut Core, reg_ndx: usize) -> u32 {
	// pre-decrement
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) - Wrapping(2)).0;
	core.dar[reg_ndx] & ADDRBUS_MASK
}
fn postincrement_16(core: &mut Core, reg_ndx: usize) -> u32 {
	// post-increment
	let ea = core.dar[reg_ndx];
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) + Wrapping(2)).0;
	ea & ADDRBUS_MASK
}
fn displacement(core: &mut Core, reg_val: u32) -> u32 {
	let displacement = core.read_imm_i16();
	let ea = (Wrapping(reg_val) + Wrapping(displacement as u32)).0;
	ea & ADDRBUS_MASK
}
// Brief Extension Word format (see M68000 PRM section 2.1)
const LONG_INDEX_MASK: u16 = 0x0800;
fn index(core: &mut Core, reg_val: u32) -> u32 {
	let extension = core.read_imm_u16();
	let xreg_ndx = (extension>>12) as usize;
	let xn = core.dar[xreg_ndx];
	let xn = if (extension & LONG_INDEX_MASK) > 0 {xn} else {(xn as i16) as u32};

  	let index = extension as i8;
	let ea = (Wrapping(reg_val) + Wrapping(xn) + Wrapping(index as u32)).0;
	ea & ADDRBUS_MASK
}

#[cfg(test)]
mod tests {
	use super::super::Core;
	use super::super::effective_address::{predecrement_8, postincrement_8};
	use ram::{ADDRBUS_MASK};

	#[test]
	fn predecrement_wraps() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0;
		}
		let ea = predecrement_8(&mut core, 8+0);
		assert_eq!(0x00FFFFFF, ea);
	}
	#[test]
	fn predecrement_wraps_a7_by_two() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0;
		}
		let ea = predecrement_8(&mut core, 8+7);
		// a7 is kept even
		assert_eq!(0x00FFFFFE, ea);
	}
	#[test]
	fn postincrement_wraps() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0xFFFFFFFF;
		}
		let ea = postincrement_8(&mut core, 8+0);
		assert_eq!(0xFFFFFFFF & ADDRBUS_MASK, ea);
		assert_eq!(0x0, core.dar[8+0]);
	}
	#[test]
	fn postincrement_wraps_a7_by_two() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0xFFFFFFFE;
		}
		let ea = postincrement_8(&mut core, 8+7);
		// a7 is kept even
		assert_eq!(0xFFFFFFFE & ADDRBUS_MASK, ea);
		assert_eq!(0x0, core.dar[8+7]);
	}
}
<commit_msg>Improved test names<commit_after>use super::Core;
use std::num::Wrapping;
use ram::ADDRBUS_MASK;

pub fn absolute_word(core: &mut Core) -> u32 {
	let ea = core.read_imm_i16() as u32;
	ea & ADDRBUS_MASK
}
pub fn absolute_long(core: &mut Core) -> u32 {
	let ea = core.read_imm_u32();
	ea & ADDRBUS_MASK
}
pub fn predecrement_ay_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	predecrement_8(core, reg_ndx)
}
pub fn postincrement_ay_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	postincrement_8(core, reg_ndx)
}
pub fn predecrement_ay_16(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	predecrement_16(core, reg_ndx)
}
pub fn postincrement_ay_16(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	postincrement_16(core, reg_ndx)
}
pub fn address_indirect_ay(core: &mut Core) -> u32 {
	let reg_ndx = ir_ay!(core);
	core.dar[reg_ndx] & ADDRBUS_MASK
}
pub fn displacement_ay(core: &mut Core) -> u32 {
	let reg_val = core.dar[ir_ay!(core)];
	displacement(core, reg_val)
}
pub fn displacement_pc(core: &mut Core) -> u32 {
	let old_pc = core.pc;
	displacement(core, old_pc)
}
pub fn index_ay(core: &mut Core) -> u32 {
	let reg_val = core.dar[ir_ay!(core)];
	index(core, reg_val)
}
pub fn index_pc(core: &mut Core) -> u32 {
	let pc = core.pc;
	index(core, pc)
}
pub fn predecrement_ax_8(core: &mut Core) -> u32 {
	let reg_ndx = ir_ax!(core);
	predecrement_8(core, reg_ndx)
}
pub fn predecrement_ax_16(core: &mut Core) -> u32 {
	let reg_ndx = ir_ax!(core);
	predecrement_16(core, reg_ndx)
}

fn predecrement_8(core: &mut Core, reg_ndx: usize) -> u32 {
	// pre-decrement
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) - match reg_ndx {
		15 => Wrapping(2), // A7 is kept even
		 _ => Wrapping(1)
	}).0;
	core.dar[reg_ndx] & ADDRBUS_MASK
}
fn postincrement_8(core: &mut Core, reg_ndx: usize) -> u32 {
	// post-increment
	let ea = core.dar[reg_ndx];
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) + match reg_ndx {
		15 => Wrapping(2), // A7 is kept even
		 _ => Wrapping(1)
	}).0;
	ea & ADDRBUS_MASK
}
fn predecrement_16(core: &mut Core, reg_ndx: usize) -> u32 {
	// pre-decrement
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) - Wrapping(2)).0;
	core.dar[reg_ndx] & ADDRBUS_MASK
}
fn postincrement_16(core: &mut Core, reg_ndx: usize) -> u32 {
	// post-increment
	let ea = core.dar[reg_ndx];
	core.dar[reg_ndx] = (Wrapping(core.dar[reg_ndx]) + Wrapping(2)).0;
	ea & ADDRBUS_MASK
}
fn displacement(core: &mut Core, reg_val: u32) -> u32 {
	let displacement = core.read_imm_i16();
	let ea = (Wrapping(reg_val) + Wrapping(displacement as u32)).0;
	ea & ADDRBUS_MASK
}
// Brief Extension Word format (see M68000 PRM section 2.1)
const LONG_INDEX_MASK: u16 = 0x0800;
fn index(core: &mut Core, reg_val: u32) -> u32 {
	let extension = core.read_imm_u16();
	let xreg_ndx = (extension>>12) as usize;
	let xn = core.dar[xreg_ndx];
	let xn = if (extension & LONG_INDEX_MASK) > 0 {xn} else {(xn as i16) as u32};

  	let index = extension as i8;
	let ea = (Wrapping(reg_val) + Wrapping(xn) + Wrapping(index as u32)).0;
	ea & ADDRBUS_MASK
}

#[cfg(test)]
mod tests {
	use super::super::Core;
	use super::super::effective_address::{predecrement_8, postincrement_8};
	use ram::{ADDRBUS_MASK};

	#[test]
	fn predecrement_wraps() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0;
		}
		let ea = predecrement_8(&mut core, 8+0);
		assert_eq!(0x00FFFFFF, ea);
	}
	#[test]
	fn predecrement_8_wraps_a7_by_two() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0;
		}
		let ea = predecrement_8(&mut core, 8+7);
		// a7 is kept even
		assert_eq!(0x00FFFFFE, ea);
	}
	#[test]
	fn postincrement_wraps() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0xFFFFFFFF;
		}
		let ea = postincrement_8(&mut core, 8+0);
		assert_eq!(0xFFFFFFFF & ADDRBUS_MASK, ea);
		assert_eq!(0x0, core.dar[8+0]);
	}
	#[test]
	fn postincrement_8_wraps_a7_by_two() {
		let mut core = Core::new(0x40);
		for i in 0..8 {
			// pre-decrement should wrap to 0xFFFFFFFF
			// but the following read should be from address 0x00FFFFFF
			// i.e. limited by the 24-bit address bus width
			core.dar[8+i as usize] = 0xFFFFFFFE;
		}
		let ea = postincrement_8(&mut core, 8+7);
		// a7 is kept even
		assert_eq!(0xFFFFFFFE & ADDRBUS_MASK, ea);
		assert_eq!(0x0, core.dar[8+7]);
	}
}
<|endoftext|>


<commit_before>use std::io::{self, ErrorKind, Read, Write};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::rc::Rc;
use std::time::Duration;

use bytes::BytesMut;
use futures::{Async, Poll};
use rand;
use tokio_core::net::UdpSocket;
use tokio_core::reactor::{Handle, PollEvented};
use tokio_io::{AsyncRead, AsyncWrite};

use config::KcpConfig;
use kcp_io::{ClientKcpIo, ServerKcpIo};
use session::KcpSessionUpdater;
use skcp::SharedKcp;

/// KCP client for interacting with server
pub struct KcpStream {
    udp: Rc<UdpSocket>,
    io: ClientKcpIo,
    buf: BytesMut,
}

impl KcpStream {
    #[doc(hidden)]
    pub fn new(udp: Rc<UdpSocket>, io: ClientKcpIo) -> KcpStream {
        let mut buf = BytesMut::with_capacity(io.mtu());
        unsafe {
            buf.set_len(io.mtu());
        }
        KcpStream {
            udp: udp,
            io: io,
            buf: buf,
        }
    }

    /// Opens a KCP connection to a remote host.
    pub fn connect(addr: &SocketAddr, handle: &Handle) -> io::Result<KcpStream> {
        KcpStream::connect_with_config(addr, handle, &KcpConfig::default())
    }

    /// Opens a KCP connection to a remote host.
    pub fn connect_with_config(addr: &SocketAddr, handle: &Handle, config: &KcpConfig) -> io::Result<KcpStream> {
        let local = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0);

        let udp = UdpSocket::bind(&local, &handle)?;
        let udp = Rc::new(udp);

        let kcp = SharedKcp::new_with_config(config, rand::random::<u32>(), udp.clone(), *addr, handle);

        let sess_exp = match config.session_expire {
            Some(dur) => dur,
            None => Duration::from_secs(90),
        };

        let io = ClientKcpIo::new(kcp, *addr, sess_exp, &handle)?;
        Ok(KcpStream::new(udp, io))
    }
}

impl Read for KcpStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        for lp in 1.. {
            match self.udp.recv_from(&mut self.buf) {
                Ok((n, addr)) => {
                    trace!("[RECV] UDP {} size={} {:?}", addr, n, ::debug::BsDebug(&self.buf[..n]));
                    self.io.input(&self.buf[..n])?;
                }
                Err(err) => {
                    if err.kind() == ErrorKind::WouldBlock {
                        if lp > 1 {
                            return Err(err);
                        }
                    } else {
                        return Err(err);
                    }
                }
            }

            let n = match self.io.read(buf) {
                Ok(n) => {
                    trace!("[RECV] Evented.read size={}", n);
                    n
                }
                // Loop continue, maybe we received an ACK packet
                Err(ref err) if err.kind() == ErrorKind::WouldBlock => {
                    trace!("[RECV] Evented.read not ready yet");
                    continue;
                }
                Err(err) => return Err(err),
            };

            return Ok(n);
        }

        unreachable!()
    }
}

impl AsyncRead for KcpStream {}

impl AsyncWrite for KcpStream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        Ok(().into())
    }
}

impl Write for KcpStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.io.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.io.flush()
    }
}

/// KCP client between a local and remote socket
///
/// After creating a `KcpStream` by either connecting to a remote host or accepting a connection on a `KcpListener`,
/// data can be transmitted by reading and writing to it.
pub struct ServerKcpStream {
    io: PollEvented<ServerKcpIo>,
}

impl ServerKcpStream {
    #[doc(hidden)]
    pub fn new_with_config(conv: u32,
                           udp: Rc<UdpSocket>,
                           addr: &SocketAddr,
                           handle: &Handle,
                           u: &mut KcpSessionUpdater,
                           config: &KcpConfig)
                           -> io::Result<ServerKcpStream> {
        let kcp = SharedKcp::new_with_config(&config, conv, udp.clone(), *addr, handle);

        let sess_exp = match config.session_expire {
            Some(dur) => dur,
            None => Duration::from_secs(90),
        };

        let io = ServerKcpIo::new(kcp, *addr, sess_exp, handle, u)?;
        let io = PollEvented::new(io, handle)?;
        Ok(ServerKcpStream { io: io })

    }

    #[doc(hidden)]
    pub fn input(&mut self, buf: &[u8]) -> io::Result<()> {
        let io = self.io.get_mut();
        io.input(buf)
    }
}

impl Read for ServerKcpStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.io.read(buf)
    }
}

impl Write for ServerKcpStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        // FIXME: Write does not have events yet
        self.io.get_mut().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        // FIXME: Write does not have events yet
        self.io.get_mut().flush()
    }
}

impl AsyncRead for ServerKcpStream {}

impl AsyncWrite for ServerKcpStream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        self.io.get_mut().shutdown()?;
        Ok(Async::Ready(()))
    }
}
<commit_msg>Stream.read processes kcp before udp<commit_after>use std::io::{self, ErrorKind, Read, Write};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::rc::Rc;
use std::time::Duration;

use bytes::BytesMut;
use futures::{Async, Poll};
use rand;
use tokio_core::net::UdpSocket;
use tokio_core::reactor::{Handle, PollEvented};
use tokio_io::{AsyncRead, AsyncWrite};

use config::KcpConfig;
use kcp_io::{ClientKcpIo, ServerKcpIo};
use session::KcpSessionUpdater;
use skcp::SharedKcp;

/// KCP client for interacting with server
pub struct KcpStream {
    udp: Rc<UdpSocket>,
    io: ClientKcpIo,
    buf: BytesMut,
}

impl KcpStream {
    #[doc(hidden)]
    pub fn new(udp: Rc<UdpSocket>, io: ClientKcpIo) -> KcpStream {
        let mut buf = BytesMut::with_capacity(io.mtu());
        unsafe {
            buf.set_len(io.mtu());
        }
        KcpStream {
            udp: udp,
            io: io,
            buf: buf,
        }
    }

    /// Opens a KCP connection to a remote host.
    pub fn connect(addr: &SocketAddr, handle: &Handle) -> io::Result<KcpStream> {
        KcpStream::connect_with_config(addr, handle, &KcpConfig::default())
    }

    /// Opens a KCP connection to a remote host.
    pub fn connect_with_config(addr: &SocketAddr, handle: &Handle, config: &KcpConfig) -> io::Result<KcpStream> {
        let local = SocketAddr::new(IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)), 0);

        let udp = UdpSocket::bind(&local, &handle)?;
        let udp = Rc::new(udp);

        let kcp = SharedKcp::new_with_config(config, rand::random::<u32>(), udp.clone(), *addr, handle);

        let sess_exp = match config.session_expire {
            Some(dur) => dur,
            None => Duration::from_secs(90),
        };

        let io = ClientKcpIo::new(kcp, *addr, sess_exp, &handle)?;
        Ok(KcpStream::new(udp, io))
    }

    fn recv_from(&mut self) -> io::Result<()> {
        match self.udp.recv_from(&mut self.buf) {
            Ok((n, addr)) => {
                trace!("[RECV] UDP {} size={} {:?}", addr, n, ::debug::BsDebug(&self.buf[..n]));
                self.io.input(&self.buf[..n])?;
                Ok(())
            }
            Err(err) => Err(err),
        }
    }

    fn io_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.io.read(buf) {
            Ok(n) => {
                trace!("[RECV] Evented.read size={}", n);
                Ok(n)
            }
            Err(err) => Err(err),
        }
    }
}

impl Read for KcpStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        // loop until we got something
        loop {
            match self.io_read(buf) {
                Ok(n) => return Ok(n),
                // Loop continue, maybe we have received an ACK packet
                Err(ref err) if err.kind() == ErrorKind::WouldBlock => {}
                Err(err) => return Err(err),
            }

            self.recv_from()?;
        }
    }
}

impl AsyncRead for KcpStream {}

impl AsyncWrite for KcpStream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        Ok(().into())
    }
}

impl Write for KcpStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.io.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.io.flush()
    }
}

/// KCP client between a local and remote socket
///
/// After creating a `KcpStream` by either connecting to a remote host or accepting a connection on a `KcpListener`,
/// data can be transmitted by reading and writing to it.
pub struct ServerKcpStream {
    io: PollEvented<ServerKcpIo>,
}

impl ServerKcpStream {
    #[doc(hidden)]
    pub fn new_with_config(conv: u32,
                           udp: Rc<UdpSocket>,
                           addr: &SocketAddr,
                           handle: &Handle,
                           u: &mut KcpSessionUpdater,
                           config: &KcpConfig)
                           -> io::Result<ServerKcpStream> {
        let kcp = SharedKcp::new_with_config(&config, conv, udp.clone(), *addr, handle);

        let sess_exp = match config.session_expire {
            Some(dur) => dur,
            None => Duration::from_secs(90),
        };

        let io = ServerKcpIo::new(kcp, *addr, sess_exp, handle, u)?;
        let io = PollEvented::new(io, handle)?;
        Ok(ServerKcpStream { io: io })

    }

    #[doc(hidden)]
    pub fn input(&mut self, buf: &[u8]) -> io::Result<()> {
        let io = self.io.get_mut();
        io.input(buf)
    }
}

impl Read for ServerKcpStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.io.read(buf)
    }
}

impl Write for ServerKcpStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        // FIXME: Write does not have events yet
        self.io.get_mut().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        // FIXME: Write does not have events yet
        self.io.get_mut().flush()
    }
}

impl AsyncRead for ServerKcpStream {}

impl AsyncWrite for ServerKcpStream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        self.io.get_mut().shutdown()?;
        Ok(Async::Ready(()))
    }
}
<|endoftext|>