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

extern crate combine;
extern crate edn;
extern crate itertools;

/// A `ValueParseError` is a `combine::primitives::ParseError`-alike that implements the `Debug`,
/// `Display`, and `std::error::Error` traits.  In addition, it doesn't capture references, making
/// it possible to store `ValueParseError` instances in local links with the `error-chain` crate.
///
/// This is achieved by wrapping slices of type `&'a [edn::Value]` in an owning type that implements
/// `Display`; rather than introducing a newtype like `DisplayVec`, we re-use `edn::Value::Vector`.
#[derive(PartialEq)]
pub struct ValueParseError {
    pub position: edn::Span,
    // Think of this as `Vec<Error<edn::Value, DisplayVec<edn::Value>>>`; see above.
    pub errors: Vec<combine::primitives::Error<edn::ValueAndSpan, edn::ValueAndSpan>>,
}

#[macro_use]
pub mod macros;

pub use macros::{
    KeywordMapParser,
    ResultParser,
};

pub mod log;
pub mod value_and_span;
pub use value_and_span::{
    Stream,
};

impl std::fmt::Debug for ValueParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f,
               "ParseError {{ position: {:?}, errors: {:?} }}",
               self.position,
               self.errors)
    }
}

impl std::fmt::Display for ValueParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        try!(writeln!(f, "Parse error at {:?}", self.position));
        combine::primitives::Error::fmt_errors(&self.errors, f)
    }
}

impl std::error::Error for ValueParseError {
    fn description(&self) -> &str {
        "parse error parsing EDN values"
    }
}

impl<'a> From<combine::primitives::ParseError<Stream<'a>>> for ValueParseError {
    fn from(e: combine::primitives::ParseError<Stream<'a>>) -> ValueParseError {
        ValueParseError {
            position: e.position.0,
            errors: e.errors.into_iter()
                .map(|e| e.map_token(|t| t.clone()).map_range(|r| r.clone()))
                .collect(),
        }
    }
}
<commit_msg>Post: Fix comment referring to error-chain.<commit_after>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

extern crate combine;
extern crate edn;
extern crate itertools;

/// A `ValueParseError` is a `combine::primitives::ParseError`-alike that implements the `Debug`,
/// `Display`, and `std::error::Error` traits.  In addition, it doesn't capture references.
///
/// This is achieved by wrapping slices of type `&'a [edn::Value]` in an owning type that implements
/// `Display`; rather than introducing a newtype like `DisplayVec`, we re-use `edn::Value::Vector`.
#[derive(PartialEq)]
pub struct ValueParseError {
    pub position: edn::Span,
    // Think of this as `Vec<Error<edn::Value, DisplayVec<edn::Value>>>`; see above.
    pub errors: Vec<combine::primitives::Error<edn::ValueAndSpan, edn::ValueAndSpan>>,
}

#[macro_use]
pub mod macros;

pub use macros::{
    KeywordMapParser,
    ResultParser,
};

pub mod log;
pub mod value_and_span;
pub use value_and_span::{
    Stream,
};

impl std::fmt::Debug for ValueParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f,
               "ParseError {{ position: {:?}, errors: {:?} }}",
               self.position,
               self.errors)
    }
}

impl std::fmt::Display for ValueParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        try!(writeln!(f, "Parse error at {:?}", self.position));
        combine::primitives::Error::fmt_errors(&self.errors, f)
    }
}

impl std::error::Error for ValueParseError {
    fn description(&self) -> &str {
        "parse error parsing EDN values"
    }
}

impl<'a> From<combine::primitives::ParseError<Stream<'a>>> for ValueParseError {
    fn from(e: combine::primitives::ParseError<Stream<'a>>) -> ValueParseError {
        ValueParseError {
            position: e.position.0,
            errors: e.errors.into_iter()
                .map(|e| e.map_token(|t| t.clone()).map_range(|r| r.clone()))
                .collect(),
        }
    }
}
<|endoftext|>


<commit_before>use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// A command
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();

        commands.push(Command {
            name: "cat",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read: {}", path),
                    }
                } else {
                    println!("Failed to open file: {}", path);
                }
            },
        });

        commands.push(Command {
            name: "cd",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            },
        });

        commands.push(Command {
            name: "echo",
            main: box |args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            },
        });

        commands.push(Command {
            name: "else",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "exec",
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    File::exec(arg);
                }
            },
        });

        commands.push(Command {
            name: "exit",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "fi",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "if",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "ls",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(dir) = read_dir(&path) {
                    for entry in dir {
                        println!("{}", entry.path());
                    }
                } else {
                    println!("Failed to open directory: {}", path);
                }
            }
        });

        commands.push(Command {
            name: "pwd",
            main: box |_: &Vec<String>| {
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        println!("Could not get the path");
                    }
                } else {
                    println!("Could not open the working directory");
                }
            },
        });

        commands.push(Command {
            name: "read",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "run",
            main: box |args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            },
        });

        commands.push(Command {
            name: "sleep",
            main: box |args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            },
        });

        commands.push(Command {
            name: "send",
            main: box |args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "touch",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => if File::create(file_name).is_none() {
                        println!("Failed to create: {}", file_name);
                    },
                    None => println!("No name provided")
                }
            }
        });

        commands.push(Command {
            name: "url_hex",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "wget",
            main: box |args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            },
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name);

        commands.push(Command {
            name: "help",
            main: box move |_: &Vec<String>| {
                println!("Commands:{}", command_list);
            },
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            for variable in self.variables.iter() {
                println!("{}={}", variable.name, variable.value);
            }
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        } else {
                            println!("No right hand side");
                        }
                    } else {
                        println!("No comparison operator");
                    }
                } else {
                    println!("No left hand side");
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            if cmd == "read" {
                for i in 1..args.len() {
                    if let Some(arg_original) = args.get(i) {
                        let arg = arg_original.trim();
                        print!("{}=", arg);
                        if let Some(value_original) = readln!() {
                            let value = value_original.trim();
                            self.set_var(arg, value);
                        }
                    }
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].trim();
                let mut value = cmd[i + 1 .. cmd.len()].trim().to_string();

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                self.set_var(name, &value);
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str){
        if name.is_empty() {
            return;
        }

        if value.is_empty() {
            let mut remove = -1;
            for i in 0..self.variables.len() {
                match self.variables.get(i) {
                    Some(variable) => if variable.name == name {
                        remove = i as isize;
                        break;
                    },
                    None => break,
                }
            }

            if remove >= 0 {
                self.variables.remove(remove as usize);
            }
        } else {
            for variable in self.variables.iter_mut() {
                if variable.name == name {
                    variable.value = value.to_string();
                    return;
                }
            }

            self.variables.push(Variable {
                name: name.to_string(),
                value: value.to_string(),
            });
        }
    }

    pub fn get_current_directory(&mut self) -> String {
        //Print user directory
        if let Some(file) = File::open("") {
            if let Some(path) = file.path() {
                return path
            }
            else {
                return "?".to_string()
            }
        }
        else {
            return "?".to_string()
        }
    }

    /// Run the application
    pub fn main(&mut self) {
        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("user@redox:{}# {}", self.get_current_directory(), command);
            self.on_command(&command);
        }

        loop {
            for mode in self.modes.iter().rev() {
                if mode.value {
                    print!("+ ");
                } else {
                    print!("- ");
                }
            }
            print!("user@redox:{}# ", self.get_current_directory());
            if let Some(command_original) = readln!() {
                let command = command_original.trim();
                if command == "exit" {
                    println!("Exit temporarily blocked (due to using terminal as init)")
                    //break;
                } else if !command.is_empty() {
                    self.on_command(&command);
                }
            } else {
                println!("Failed to read from stdin");
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<commit_msg>Documentation added for get_current_directory()<commit_after>use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// A command
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();

        commands.push(Command {
            name: "cat",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read: {}", path),
                    }
                } else {
                    println!("Failed to open file: {}", path);
                }
            },
        });

        commands.push(Command {
            name: "cd",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            },
        });

        commands.push(Command {
            name: "echo",
            main: box |args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            },
        });

        commands.push(Command {
            name: "else",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "exec",
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    File::exec(arg);
                }
            },
        });

        commands.push(Command {
            name: "exit",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "fi",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "if",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "ls",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(dir) = read_dir(&path) {
                    for entry in dir {
                        println!("{}", entry.path());
                    }
                } else {
                    println!("Failed to open directory: {}", path);
                }
            }
        });

        commands.push(Command {
            name: "pwd",
            main: box |_: &Vec<String>| {
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        println!("Could not get the path");
                    }
                } else {
                    println!("Could not open the working directory");
                }
            },
        });

        commands.push(Command {
            name: "read",
            main: box |_: &Vec<String>| {},
        });

        commands.push(Command {
            name: "run",
            main: box |args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            },
        });

        commands.push(Command {
            name: "sleep",
            main: box |args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            },
        });

        commands.push(Command {
            name: "send",
            main: box |args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "touch",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => if File::create(file_name).is_none() {
                        println!("Failed to create: {}", file_name);
                    },
                    None => println!("No name provided")
                }
            }
        });

        commands.push(Command {
            name: "url_hex",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "wget",
            main: box |args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            },
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name);

        commands.push(Command {
            name: "help",
            main: box move |_: &Vec<String>| {
                println!("Commands:{}", command_list);
            },
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            for variable in self.variables.iter() {
                println!("{}={}", variable.name, variable.value);
            }
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        } else {
                            println!("No right hand side");
                        }
                    } else {
                        println!("No comparison operator");
                    }
                } else {
                    println!("No left hand side");
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            if cmd == "read" {
                for i in 1..args.len() {
                    if let Some(arg_original) = args.get(i) {
                        let arg = arg_original.trim();
                        print!("{}=", arg);
                        if let Some(value_original) = readln!() {
                            let value = value_original.trim();
                            self.set_var(arg, value);
                        }
                    }
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].trim();
                let mut value = cmd[i + 1 .. cmd.len()].trim().to_string();

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                self.set_var(name, &value);
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str){
        if name.is_empty() {
            return;
        }

        if value.is_empty() {
            let mut remove = -1;
            for i in 0..self.variables.len() {
                match self.variables.get(i) {
                    Some(variable) => if variable.name == name {
                        remove = i as isize;
                        break;
                    },
                    None => break,
                }
            }

            if remove >= 0 {
                self.variables.remove(remove as usize);
            }
        } else {
            for variable in self.variables.iter_mut() {
                if variable.name == name {
                    variable.value = value.to_string();
                    return;
                }
            }

            self.variables.push(Variable {
                name: name.to_string(),
                value: value.to_string(),
            });
        }
    }

    /// Method to return the current directory
    /// If the current directory canno't be find, a default string ("?") will be returned
    pub fn get_current_directory(&mut self) -> String {
        if let Some(file) = File::open("") {
            if let Some(path) = file.path() {
                // Return the current path
                return path
            }
        // Return a default string if the path canno't be find
            else {
                return "?".to_string()
            }
        }
        else {
            return "?".to_string()
        }
    }

    /// Run the application
    pub fn main(&mut self) {
        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("user@redox:{}# {}", self.get_current_directory(), command);
            self.on_command(&command);
        }

        loop {
            for mode in self.modes.iter().rev() {
                if mode.value {
                    print!("+ ");
                } else {
                    print!("- ");
                }
            }
            print!("user@redox:{}# ", self.get_current_directory());
            if let Some(command_original) = readln!() {
                let command = command_original.trim();
                if command == "exit" {
                    println!("Exit temporarily blocked (due to using terminal as init)")
                    //break;
                } else if !command.is_empty() {
                    self.on_command(&command);
                }
            } else {
                println!("Failed to read from stdin");
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<|endoftext|>


<commit_before>use sass::color_value::ColorValue;
use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::op::Op;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Option<ValuePart<'a>> {
        self.skip_leading_whitespace();

        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        if start == limit {
            None
        } else if is_operator(self.bytes[start]) {
            self.offset = start + 1;
            Some(ValuePart::Operator(self.value_str[start..start + 1].parse().unwrap_or(Op::Plus)))
        } else if is_number(self.bytes[start]) {
            i += self.scan_while(&self.value_str[i..limit], is_number);
            self.offset = i;
            if i < limit && valid_unit_char(self.bytes[i]) {
                let unit_start = i;
                i += self.scan_while(&self.value_str[i..limit], valid_unit_char);
                self.offset = i;
                Some(ValuePart::Number(NumberValue::with_units(
                    self.value_str[start..unit_start].parse().unwrap_or(0.0),
                    Borrowed(&self.value_str[unit_start..i]),
                )))
            }
            else {
                Some(ValuePart::Number(NumberValue::from_scalar(
                    self.value_str[start..i].parse().unwrap_or(0.0)
                )))
            }
        } else if self.bytes[start] == b'$' {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else if self.bytes[start] == b'#' {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::Color(ColorValue::from_hex(Borrowed(&self.value_str[start..i]))))
        } else {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        }
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
       let mut i = self.offset;
       let limit = self.value_str.len();

       while i < limit {
           let c = self.bytes[i];
           if is_space(c) {
               i += self.scan_while(&self.value_str[i..limit], is_space);
           } else {
               self.offset = i;
               return
           }
       }
       self.offset = limit;
   }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = ValuePart<'a>;

    fn next(&mut self) -> Option<ValuePart<'a>> {
        if self.offset < self.value_str.len() {
            return self.parse()
        }
        None
    }
}

fn is_space(c: u8) -> bool {
    c == b' '
}

fn isnt_space(c: u8) -> bool {
    !is_space(c)
}

fn valid_unit_char(c: u8) -> bool {
    c == b'%' || (!is_space(c) && !is_operator(c))
}

fn is_number(c: u8) -> bool {
    let result = match c {
        b'0' ... b'9' | b'.' => true,
        _ => false,
    };
    result
}

fn is_operator(c: u8) -> bool {
    match c {
        b'+' | b'-' | b'*' | b'/' | b'%' | b'(' | b')' | b',' => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::color_value::ColorValue;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::op::Op;
    use std::borrow::Cow::Borrowed;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"bar"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$bar"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"baz"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$quux"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_number() {
        let mut vt = ValueTokenizer::new("3");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_two_numbers() {
        let mut vt = ValueTokenizer::new("3 8.9");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(8.9))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units() {
        let mut vt = ValueTokenizer::new("3px");
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units_but_not_parens() {
        let mut vt = ValueTokenizer::new("(3px)");
        assert_eq!(Some(ValuePart::Operator(Op::LeftParen)), vt.next());
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_percents_as_units() {
        let mut vt = ValueTokenizer::new("10px 8%");
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(10.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(8.0, Borrowed(&"%")))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_operator() {
        let mut vt = ValueTokenizer::new("+");
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators() {
        let mut vt = ValueTokenizer::new("6 + 75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators_without_spaces() {
        let mut vt = ValueTokenizer::new("6+75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_parens() {
        let mut vt = ValueTokenizer::new("2+(3 4)");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(2.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::LeftParen)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(4.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_slash_separators() {
        let mut vt = ValueTokenizer::new("15 / 3 / 5");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(15.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(5.0))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_hex() {
        let mut vt = ValueTokenizer::new("#aabbcc");
        assert_eq!(
            Some(ValuePart::Color(ColorValue {
                red: 170, green: 187, blue: 204, original: Borrowed("#aabbcc"),
            })),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_short_hex() {
        let mut vt = ValueTokenizer::new("#cba");
        assert_eq!(
            Some(ValuePart::Color(ColorValue {
                red: 204, green: 187, blue: 170, original: Borrowed("#cba"),
            })),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }
}
<commit_msg>Cmon. Parens are not valid hex. Do I have to teach you everything??<commit_after>use sass::color_value::ColorValue;
use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::op::Op;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Option<ValuePart<'a>> {
        self.skip_leading_whitespace();

        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        if start == limit {
            None
        } else if is_operator(self.bytes[start]) {
            self.offset = start + 1;
            Some(ValuePart::Operator(self.value_str[start..start + 1].parse().unwrap_or(Op::Plus)))
        } else if is_number(self.bytes[start]) {
            i += self.scan_while(&self.value_str[i..limit], is_number);
            self.offset = i;
            if i < limit && valid_unit_char(self.bytes[i]) {
                let unit_start = i;
                i += self.scan_while(&self.value_str[i..limit], valid_unit_char);
                self.offset = i;
                Some(ValuePart::Number(NumberValue::with_units(
                    self.value_str[start..unit_start].parse().unwrap_or(0.0),
                    Borrowed(&self.value_str[unit_start..i]),
                )))
            }
            else {
                Some(ValuePart::Number(NumberValue::from_scalar(
                    self.value_str[start..i].parse().unwrap_or(0.0)
                )))
            }
        } else if self.bytes[start] == b'$' {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else if self.bytes[start] == b'#' {
            i += 1;
            i += self.scan_while(&self.value_str[i..limit], valid_hex_char);
            self.offset = i;
            Some(ValuePart::Color(ColorValue::from_hex(Borrowed(&self.value_str[start..i]))))
        } else {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        }
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
       let mut i = self.offset;
       let limit = self.value_str.len();

       while i < limit {
           let c = self.bytes[i];
           if is_space(c) {
               i += self.scan_while(&self.value_str[i..limit], is_space);
           } else {
               self.offset = i;
               return
           }
       }
       self.offset = limit;
   }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = ValuePart<'a>;

    fn next(&mut self) -> Option<ValuePart<'a>> {
        if self.offset < self.value_str.len() {
            return self.parse()
        }
        None
    }
}

fn is_space(c: u8) -> bool {
    c == b' '
}

fn isnt_space(c: u8) -> bool {
    !is_space(c)
}

fn valid_unit_char(c: u8) -> bool {
    c == b'%' || (!is_space(c) && !is_operator(c))
}

fn valid_hex_char(c: u8) -> bool {
    match c {
        b'0' ... b'9' | b'a' ... b'f' => true,
        _ => false,
    }
}

fn is_number(c: u8) -> bool {
    let result = match c {
        b'0' ... b'9' | b'.' => true,
        _ => false,
    };
    result
}

fn is_operator(c: u8) -> bool {
    match c {
        b'+' | b'-' | b'*' | b'/' | b'%' | b'(' | b')' | b',' => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::color_value::ColorValue;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::op::Op;
    use std::borrow::Cow::Borrowed;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"bar"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$bar"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"baz"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$quux"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_number() {
        let mut vt = ValueTokenizer::new("3");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_two_numbers() {
        let mut vt = ValueTokenizer::new("3 8.9");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(8.9))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units() {
        let mut vt = ValueTokenizer::new("3px");
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units_but_not_parens() {
        let mut vt = ValueTokenizer::new("(3px)");
        assert_eq!(Some(ValuePart::Operator(Op::LeftParen)), vt.next());
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_percents_as_units() {
        let mut vt = ValueTokenizer::new("10px 8%");
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(10.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(8.0, Borrowed(&"%")))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_operator() {
        let mut vt = ValueTokenizer::new("+");
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators() {
        let mut vt = ValueTokenizer::new("6 + 75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators_without_spaces() {
        let mut vt = ValueTokenizer::new("6+75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_parens() {
        let mut vt = ValueTokenizer::new("2+(3 4)");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(2.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::LeftParen)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(4.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_slash_separators() {
        let mut vt = ValueTokenizer::new("15 / 3 / 5");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(15.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(5.0))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_hex() {
        let mut vt = ValueTokenizer::new("#aabbcc");
        assert_eq!(
            Some(ValuePart::Color(ColorValue {
                red: 170, green: 187, blue: 204, original: Borrowed("#aabbcc"),
            })),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_short_hex() {
        let mut vt = ValueTokenizer::new("#cba");
        assert_eq!(
            Some(ValuePart::Color(ColorValue {
                red: 204, green: 187, blue: 170, original: Borrowed("#cba"),
            })),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_hex_is_separate_from_parens() {
        let mut vt = ValueTokenizer::new("#cba)");
        assert_eq!(
            Some(ValuePart::Color(ColorValue {
                red: 204, green: 187, blue: 170, original: Borrowed("#cba"),
            })),
            vt.next()
        );
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }
}
<|endoftext|>


<commit_before>use crate::prelude::*;
use crate::tyhandlers::ModelTypeSystem;
use syn::*;

use super::*;
use proc_macro2::Span;

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum InterfaceType
{
    Trait,
    Struct,
}

pub type InterfaceData<'a> = (
    Path,
    Vec<&'a Signature>,
    InterfaceType,
    Option<Token!(unsafe)>,
);

pub fn get_ident_and_fns(item: &Item) -> Option<InterfaceData>
{
    match *item {
        Item::Impl(ItemImpl {
            ref unsafety,
            ref trait_,
            ref self_ty,
            ref items,
            ..
        }) => {
            let (_, struct_ident, items) = get_impl_data_raw(trait_, self_ty, items);
            Some((struct_ident, items, InterfaceType::Struct, *unsafety))
        }
        Item::Trait(ItemTrait {
            ref ident,
            unsafety,
            ref items,
            ..
        }) => {
            let methods: Option<Vec<&Signature>> =
                items.iter().map(|i| get_trait_method(i)).collect();
            let path = syn::Path::from(ident.clone());

            match methods {
                Some(m) => Some((path, m, InterfaceType::Trait, unsafety)),
                None => None,
            }
        }
        _ => None,
    }
}

pub type ImplData<'a> = (Option<Path>, Path, Vec<&'a Signature>);

fn get_impl_data_raw<'a>(
    trait_ref: &'a Option<(Option<Token!(!)>, Path, Token!(for))>,
    struct_ty: &'a Type,
    items: &'a [ImplItem],
) -> ImplData<'a>
{
    let struct_path = match struct_ty {
        syn::Type::Path(typepath) => {
            if typepath.qself.is_some() {
                panic!("#[com_interface] cannot use associated types");
            }
            typepath.path.clone()
        }
        _ => panic!("#[com_interface] must be defined for Path"),
    };

    let trait_path = trait_ref.as_ref().map(|(_, path, _)| path.clone());

    let methods_opt: Option<Vec<&Signature>> = items.iter().map(|i| get_impl_method(i)).collect();
    let methods = methods_opt.unwrap_or_else(|| vec![]);

    (trait_path, struct_path, methods)
}

pub fn get_impl_method(i: &ImplItem) -> Option<&Signature>
{
    match *i {
        ImplItem::Method(ref itm) => Some(&itm.sig),
        _ => None,
    }
}

pub fn get_trait_method(i: &TraitItem) -> Option<&Signature>
{
    match *i {
        TraitItem::Method(ref tim) => Some(&tim.sig),
        _ => None,
    }
}

const AUTO_GUID_BASE: guid::GUID = guid::GUID {
    data1: 0x4449_494C,
    data2: 0xDE1F,
    data3: 0x4525,
    data4: [0xB9, 0x57, 0x89, 0xD6, 0x0C, 0xE9, 0x34, 0x77],
};

pub fn generate_iid(crate_name: &str, item_name: &str, type_system: ModelTypeSystem) -> guid::GUID
{
    generate_guid(
        &[
            "IID",
            crate_name,
            item_name,
            match type_system {
                ModelTypeSystem::Automation => "automation",
                ModelTypeSystem::Raw => "raw",
            },
        ]
        .join(":"),
    )
}

pub fn generate_libid(crate_name: &str) -> guid::GUID
{
    generate_guid(&["LIBID", crate_name].join(":"))
}

pub fn generate_clsid(crate_name: &str, item_name: &str) -> guid::GUID
{
    generate_guid(&["CLSID", crate_name, item_name].join(":"))
}

pub fn generate_guid(key: &str) -> guid::GUID
{
    // Hash the name. The name will be hashed in a form similar to:
    // AUTO_GUID_BASE + "CLSID:random_rust_crate:FooBar"
    let mut hash = sha1::Sha1::new();
    hash.update(AUTO_GUID_BASE.as_bytes());
    hash.update(key.as_bytes());

    let digest = hash.digest();
    let bytes = digest.bytes();

    // Set the GUID bytes according to RFC-4122, section 4.3.
    let time_low: u32 = (u32::from(bytes[0]) << 24)
        + (u32::from(bytes[1]) << 16)
        + (u32::from(bytes[2]) << 8)
        + u32::from(bytes[3]);
    let time_mid: u16 = (u16::from(bytes[4]) << 8) + (u16::from(bytes[5]));
    let time_hi_and_version: u16 =
        (((u16::from(bytes[6]) << 8) + u16::from(bytes[7])) & 0x0fff) | 0x3000;
    let clk_seq_hi_res: u8 = (bytes[8] & 0b0011_1111) | 0b0100_0000;
    let clk_seq_low: u8 = bytes[9];

    guid::GUID {
        data1: time_low,
        data2: time_mid,
        data3: time_hi_and_version,
        data4: [
            clk_seq_hi_res,
            clk_seq_low,
            bytes[10],
            bytes[11],
            bytes[12],
            bytes[13],
            bytes[14],
            bytes[15],
        ],
    }
}

pub fn ty_to_string(ty: &syn::Type) -> String
{
    quote!( #ty )
        .to_string()
        .replace(" ", "")
        .replace(",", ", ")
}

pub fn is_unit(tk: &Type) -> bool
{
    if let Type::Tuple(ref t) = *tk {
        t.elems.is_empty()
    } else {
        false
    }
}

pub fn unit_ty(span: Span) -> Type
{
    syn::parse2(quote_spanned!(span => ())).unwrap()
}

pub fn get_guid_tokens(g: &guid::GUID, span: Span) -> TokenStream
{
    let d1 = g.data1;
    let d2 = g.data2;
    let d3 = g.data3;
    let d4_0 = g.data4[0];
    let d4_1 = g.data4[1];
    let d4_2 = g.data4[2];
    let d4_3 = g.data4[3];
    let d4_4 = g.data4[4];
    let d4_5 = g.data4[5];
    let d4_6 = g.data4[6];
    let d4_7 = g.data4[7];
    quote_spanned!(span =>
        intercom::GUID {
            data1: #d1, data2: #d2, data3: #d3,
            data4: [ #d4_0, #d4_1, #d4_2, #d4_3, #d4_4, #d4_5, #d4_6, #d4_7 ]
        }
    )
}

/// Convert the Rust identifier from `snake_case` to `PascalCase`
pub fn pascal_case<T: AsRef<str>>(input: T) -> String
{
    let input = input.as_ref();

    // Allocate the output string. We'll never increase the amount of
    // characters so we can reserve string buffer using the input string length.
    let mut output = String::new();
    output.reserve(input.len());

    // Process each character from the input.
    let mut capitalize = true;
    for c in input.chars() {
        // Check the capitalization requirement.
        if c == '_' {
            // Skip '_' but capitalize the following character.
            capitalize = true;
        } else if capitalize {
            // Capitalize. Add the uppercase characters.
            for c_up in c.to_uppercase() {
                output.push(c_up)
            }

            // No need to capitalize any more.
            capitalize = false;
        } else {
            // No need to capitalize. Just add the character as is.
            output.push(c);
        }
    }
    output
}

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

    /// Tests the `ty_to_string` by converting parameter to Type and back to
    /// String to ensure they equal.
    fn test_ty(ty_str: &str)
    {
        let ty = parse_str(ty_str).unwrap();
        let as_string = ty_to_string(&ty);
        assert_eq!(ty_str, as_string);
    }

    #[test]
    fn path_to_test()
    {
        test_ty("::path::Foo")
    }
    #[test]
    fn generics_to_test()
    {
        test_ty("Result<Foo, Bar>")
    }
    #[test]
    fn unit_to_test()
    {
        test_ty("()")
    }
}
<commit_msg>Fix clippy lint<commit_after>use crate::prelude::*;
use crate::tyhandlers::ModelTypeSystem;
use syn::*;

use super::*;
use proc_macro2::Span;

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum InterfaceType
{
    Trait,
    Struct,
}

pub type InterfaceData<'a> = (
    Path,
    Vec<&'a Signature>,
    InterfaceType,
    Option<Token!(unsafe)>,
);

pub fn get_ident_and_fns(item: &Item) -> Option<InterfaceData>
{
    match *item {
        Item::Impl(ItemImpl {
            ref unsafety,
            ref trait_,
            ref self_ty,
            ref items,
            ..
        }) => {
            let (_, struct_ident, items) = get_impl_data_raw(trait_, self_ty, items);
            Some((struct_ident, items, InterfaceType::Struct, *unsafety))
        }
        Item::Trait(ItemTrait {
            ref ident,
            unsafety,
            ref items,
            ..
        }) => {
            let methods: Option<Vec<&Signature>> =
                items.iter().map(|i| get_trait_method(i)).collect();
            let path = syn::Path::from(ident.clone());

            match methods {
                Some(m) => Some((path, m, InterfaceType::Trait, unsafety)),
                None => None,
            }
        }
        _ => None,
    }
}

pub type ImplData<'a> = (Option<Path>, Path, Vec<&'a Signature>);

fn get_impl_data_raw<'a>(
    trait_ref: &'a Option<(Option<Token!(!)>, Path, Token!(for))>,
    struct_ty: &'a Type,
    items: &'a [ImplItem],
) -> ImplData<'a>
{
    let struct_path = match struct_ty {
        syn::Type::Path(typepath) => {
            if typepath.qself.is_some() {
                panic!("#[com_interface] cannot use associated types");
            }
            typepath.path.clone()
        }
        _ => panic!("#[com_interface] must be defined for Path"),
    };

    let trait_path = trait_ref.as_ref().map(|(_, path, _)| path.clone());

    let methods_opt: Option<Vec<&Signature>> = items.iter().map(|i| get_impl_method(i)).collect();
    let methods = methods_opt.unwrap_or_else(Vec::new);

    (trait_path, struct_path, methods)
}

pub fn get_impl_method(i: &ImplItem) -> Option<&Signature>
{
    match *i {
        ImplItem::Method(ref itm) => Some(&itm.sig),
        _ => None,
    }
}

pub fn get_trait_method(i: &TraitItem) -> Option<&Signature>
{
    match *i {
        TraitItem::Method(ref tim) => Some(&tim.sig),
        _ => None,
    }
}

const AUTO_GUID_BASE: guid::GUID = guid::GUID {
    data1: 0x4449_494C,
    data2: 0xDE1F,
    data3: 0x4525,
    data4: [0xB9, 0x57, 0x89, 0xD6, 0x0C, 0xE9, 0x34, 0x77],
};

pub fn generate_iid(crate_name: &str, item_name: &str, type_system: ModelTypeSystem) -> guid::GUID
{
    generate_guid(
        &[
            "IID",
            crate_name,
            item_name,
            match type_system {
                ModelTypeSystem::Automation => "automation",
                ModelTypeSystem::Raw => "raw",
            },
        ]
        .join(":"),
    )
}

pub fn generate_libid(crate_name: &str) -> guid::GUID
{
    generate_guid(&["LIBID", crate_name].join(":"))
}

pub fn generate_clsid(crate_name: &str, item_name: &str) -> guid::GUID
{
    generate_guid(&["CLSID", crate_name, item_name].join(":"))
}

pub fn generate_guid(key: &str) -> guid::GUID
{
    // Hash the name. The name will be hashed in a form similar to:
    // AUTO_GUID_BASE + "CLSID:random_rust_crate:FooBar"
    let mut hash = sha1::Sha1::new();
    hash.update(AUTO_GUID_BASE.as_bytes());
    hash.update(key.as_bytes());

    let digest = hash.digest();
    let bytes = digest.bytes();

    // Set the GUID bytes according to RFC-4122, section 4.3.
    let time_low: u32 = (u32::from(bytes[0]) << 24)
        + (u32::from(bytes[1]) << 16)
        + (u32::from(bytes[2]) << 8)
        + u32::from(bytes[3]);
    let time_mid: u16 = (u16::from(bytes[4]) << 8) + (u16::from(bytes[5]));
    let time_hi_and_version: u16 =
        (((u16::from(bytes[6]) << 8) + u16::from(bytes[7])) & 0x0fff) | 0x3000;
    let clk_seq_hi_res: u8 = (bytes[8] & 0b0011_1111) | 0b0100_0000;
    let clk_seq_low: u8 = bytes[9];

    guid::GUID {
        data1: time_low,
        data2: time_mid,
        data3: time_hi_and_version,
        data4: [
            clk_seq_hi_res,
            clk_seq_low,
            bytes[10],
            bytes[11],
            bytes[12],
            bytes[13],
            bytes[14],
            bytes[15],
        ],
    }
}

pub fn ty_to_string(ty: &syn::Type) -> String
{
    quote!( #ty )
        .to_string()
        .replace(" ", "")
        .replace(",", ", ")
}

pub fn is_unit(tk: &Type) -> bool
{
    if let Type::Tuple(ref t) = *tk {
        t.elems.is_empty()
    } else {
        false
    }
}

pub fn unit_ty(span: Span) -> Type
{
    syn::parse2(quote_spanned!(span => ())).unwrap()
}

pub fn get_guid_tokens(g: &guid::GUID, span: Span) -> TokenStream
{
    let d1 = g.data1;
    let d2 = g.data2;
    let d3 = g.data3;
    let d4_0 = g.data4[0];
    let d4_1 = g.data4[1];
    let d4_2 = g.data4[2];
    let d4_3 = g.data4[3];
    let d4_4 = g.data4[4];
    let d4_5 = g.data4[5];
    let d4_6 = g.data4[6];
    let d4_7 = g.data4[7];
    quote_spanned!(span =>
        intercom::GUID {
            data1: #d1, data2: #d2, data3: #d3,
            data4: [ #d4_0, #d4_1, #d4_2, #d4_3, #d4_4, #d4_5, #d4_6, #d4_7 ]
        }
    )
}

/// Convert the Rust identifier from `snake_case` to `PascalCase`
pub fn pascal_case<T: AsRef<str>>(input: T) -> String
{
    let input = input.as_ref();

    // Allocate the output string. We'll never increase the amount of
    // characters so we can reserve string buffer using the input string length.
    let mut output = String::new();
    output.reserve(input.len());

    // Process each character from the input.
    let mut capitalize = true;
    for c in input.chars() {
        // Check the capitalization requirement.
        if c == '_' {
            // Skip '_' but capitalize the following character.
            capitalize = true;
        } else if capitalize {
            // Capitalize. Add the uppercase characters.
            for c_up in c.to_uppercase() {
                output.push(c_up)
            }

            // No need to capitalize any more.
            capitalize = false;
        } else {
            // No need to capitalize. Just add the character as is.
            output.push(c);
        }
    }
    output
}

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

    /// Tests the `ty_to_string` by converting parameter to Type and back to
    /// String to ensure they equal.
    fn test_ty(ty_str: &str)
    {
        let ty = parse_str(ty_str).unwrap();
        let as_string = ty_to_string(&ty);
        assert_eq!(ty_str, as_string);
    }

    #[test]
    fn path_to_test()
    {
        test_ty("::path::Foo")
    }
    #[test]
    fn generics_to_test()
    {
        test_ty("Result<Foo, Bar>")
    }
    #[test]
    fn unit_to_test()
    {
        test_ty("()")
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::HashMap;
use std::mem::size_of;

use dora_parser::lexer::position::Position;

use crate::baseline::fct::{JitBaselineFct, JitDescriptor, JitFct, JitFctId};
use crate::baseline::map::CodeDescriptor;
use crate::cpu::{Mem, FREG_PARAMS, REG_FP, REG_PARAMS, REG_RESULT, REG_SP, REG_THREAD, REG_TMP1};
use crate::exception::DoraToNativeInfo;
use crate::gc::Address;
use crate::masm::MacroAssembler;
use crate::mem;
use crate::threads::{ThreadLocalData, THREAD};
use crate::ty::{BuiltinType, MachineMode};
use crate::vm::FctId;
use crate::vm::VM;

pub struct NativeThunks {
    map: HashMap<Address, JitFctId>,
}

impl NativeThunks {
    pub fn new() -> NativeThunks {
        NativeThunks {
            map: HashMap::new(),
        }
    }

    pub fn find_fct(&self, ptr: Address) -> Option<JitFctId> {
        self.map.get(&ptr).map(|&jit_fct_id| jit_fct_id)
    }

    pub fn insert_fct(&mut self, ptr: Address, fct: JitFctId) {
        self.map.entry(ptr).or_insert(fct);
    }
}

#[derive(Clone)]
pub enum InternalFctDescriptor {
    NativeThunk(FctId),
    AllocThunk,
    VerifyThunk,
    TrapThunk,
}

pub struct InternalFct<'a> {
    pub ptr: Address,
    pub args: &'a [BuiltinType],
    pub return_type: BuiltinType,
    pub throws: bool,
    pub desc: InternalFctDescriptor,
}

pub fn generate<'a, 'ast: 'a>(vm: &'a VM<'ast>, fct: InternalFct, dbg: bool) -> JitFctId {
    let fct_desc = fct.desc.clone();

    let ngen = NativeGen {
        vm,
        masm: MacroAssembler::new(),
        fct,
        dbg,
    };

    let jit_fct = ngen.generate();
    let jit_start = jit_fct.ptr_start();
    let jit_end = jit_fct.ptr_end();
    let jit_fct_id: JitFctId = vm.jit_fcts.push(JitFct::Base(jit_fct)).into();

    let code_desc = match fct_desc {
        InternalFctDescriptor::NativeThunk(_) => CodeDescriptor::NativeThunk(jit_fct_id),
        InternalFctDescriptor::TrapThunk => CodeDescriptor::TrapThunk,
        InternalFctDescriptor::VerifyThunk => CodeDescriptor::VerifyThunk,
        InternalFctDescriptor::AllocThunk => CodeDescriptor::AllocThunk,
    };

    vm.insert_code_map(jit_start, jit_end, code_desc);

    jit_fct_id
}

struct NativeGen<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    masm: MacroAssembler,

    fct: InternalFct<'a>,
    dbg: bool,
}

impl<'a, 'ast> NativeGen<'a, 'ast>
where
    'ast: 'a,
{
    pub fn generate(mut self) -> JitBaselineFct {
        let save_return = self.fct.return_type != BuiltinType::Unit;
        let args = self.fct.args.len();

        let dtn_size = size_of::<DoraToNativeInfo>() as i32;

        let offset_dtn = 0;
        let offset_return = dtn_size;
        let offset_args = offset_return + if save_return { mem::ptr_width() } else { 0 };
        let framesize = mem::align_i32(offset_args + args as i32 * mem::ptr_width(), 16);

        // `start_native_call` assumes that offset_dtn is on top of current stack frame.

        if self.dbg {
            self.masm.debug();
        }

        self.masm.prolog_size(framesize);

        save_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::fp_offset()),
            REG_FP.into(),
        );

        self.masm.copy_pc(REG_TMP1);

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::pc_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_SP.into(),
        );

        self.masm.raw_call(start_native_call as *const u8);

        restore_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.raw_call(self.fct.ptr.to_ptr());

        if save_return {
            self.masm.store_mem(
                MachineMode::Ptr,
                Mem::Base(REG_SP, offset_return),
                REG_RESULT.into(),
            );
        }

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_TMP1.into(),
        );

        self.masm.raw_call(finish_native_call as *const u8);

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_RESULT.into(),
            Mem::Base(REG_THREAD, ThreadLocalData::exception_object_offset()),
        );

        let lbl_exception = self.masm.test_if_not_nil(REG_RESULT);

        if save_return {
            self.masm.load_mem(
                MachineMode::Ptr,
                REG_RESULT.into(),
                Mem::Base(REG_SP, offset_return),
            );
        }

        self.masm.safepoint(self.vm.polling_page.addr());
        self.masm.epilog();

        self.masm.bind_label(lbl_exception);
        self.masm.throw(REG_RESULT, Position::new(1, 1));
        self.masm.nop();

        let desc = match self.fct.desc {
            InternalFctDescriptor::NativeThunk(fid) => JitDescriptor::NativeThunk(fid),
            InternalFctDescriptor::AllocThunk => JitDescriptor::AllocThunk,
            InternalFctDescriptor::VerifyThunk => JitDescriptor::VerifyThunk,
            InternalFctDescriptor::TrapThunk => JitDescriptor::TrapThunk,
        };

        self.masm.jit(self.vm, framesize, desc, self.fct.throws)
    }
}

fn save_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), freg);
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), reg);
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

fn restore_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.load_mem(mode, freg, Mem::Base(REG_SP, offset));
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.load_mem(mode, reg, Mem::Base(REG_SP, offset));
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

pub fn start_native_call() {
    THREAD.with(|thread| {
        thread.borrow().handles.push_border();
    });
}

pub fn finish_native_call() {
    THREAD.with(|thread| {
        thread.borrow().handles.pop_border();
    });
}
<commit_msg>dora_native: remove start/finish_native_call calls<commit_after>use std::collections::hash_map::HashMap;
use std::mem::size_of;

use dora_parser::lexer::position::Position;

use crate::baseline::fct::{JitBaselineFct, JitDescriptor, JitFct, JitFctId};
use crate::baseline::map::CodeDescriptor;
use crate::cpu::{Mem, FREG_PARAMS, REG_FP, REG_PARAMS, REG_SP, REG_THREAD, REG_TMP1};
use crate::exception::DoraToNativeInfo;
use crate::gc::Address;
use crate::masm::MacroAssembler;
use crate::mem;
use crate::threads::{ThreadLocalData, THREAD};
use crate::ty::{BuiltinType, MachineMode};
use crate::vm::FctId;
use crate::vm::VM;

pub struct NativeThunks {
    map: HashMap<Address, JitFctId>,
}

impl NativeThunks {
    pub fn new() -> NativeThunks {
        NativeThunks {
            map: HashMap::new(),
        }
    }

    pub fn find_fct(&self, ptr: Address) -> Option<JitFctId> {
        self.map.get(&ptr).map(|&jit_fct_id| jit_fct_id)
    }

    pub fn insert_fct(&mut self, ptr: Address, fct: JitFctId) {
        self.map.entry(ptr).or_insert(fct);
    }
}

#[derive(Clone)]
pub enum InternalFctDescriptor {
    NativeThunk(FctId),
    AllocThunk,
    VerifyThunk,
    TrapThunk,
}

pub struct InternalFct<'a> {
    pub ptr: Address,
    pub args: &'a [BuiltinType],
    pub return_type: BuiltinType,
    pub throws: bool,
    pub desc: InternalFctDescriptor,
}

pub fn generate<'a, 'ast: 'a>(vm: &'a VM<'ast>, fct: InternalFct, dbg: bool) -> JitFctId {
    let fct_desc = fct.desc.clone();

    let ngen = NativeGen {
        vm,
        masm: MacroAssembler::new(),
        fct,
        dbg,
    };

    let jit_fct = ngen.generate();
    let jit_start = jit_fct.ptr_start();
    let jit_end = jit_fct.ptr_end();
    let jit_fct_id: JitFctId = vm.jit_fcts.push(JitFct::Base(jit_fct)).into();

    let code_desc = match fct_desc {
        InternalFctDescriptor::NativeThunk(_) => CodeDescriptor::NativeThunk(jit_fct_id),
        InternalFctDescriptor::TrapThunk => CodeDescriptor::TrapThunk,
        InternalFctDescriptor::VerifyThunk => CodeDescriptor::VerifyThunk,
        InternalFctDescriptor::AllocThunk => CodeDescriptor::AllocThunk,
    };

    vm.insert_code_map(jit_start, jit_end, code_desc);

    jit_fct_id
}

struct NativeGen<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    masm: MacroAssembler,

    fct: InternalFct<'a>,
    dbg: bool,
}

impl<'a, 'ast> NativeGen<'a, 'ast>
where
    'ast: 'a,
{
    pub fn generate(mut self) -> JitBaselineFct {
        let save_return = self.fct.return_type != BuiltinType::Unit;
        let args = self.fct.args.len();

        let dtn_size = size_of::<DoraToNativeInfo>() as i32;

        let offset_dtn = 0;
        let offset_return = dtn_size;
        let offset_args = offset_return + if save_return { mem::ptr_width() } else { 0 };
        let framesize = mem::align_i32(offset_args + args as i32 * mem::ptr_width(), 16);

        // `start_native_call` assumes that offset_dtn is on top of current stack frame.

        if self.dbg {
            self.masm.debug();
        }

        self.masm.prolog_size(framesize);

        save_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::fp_offset()),
            REG_FP.into(),
        );

        self.masm.copy_pc(REG_TMP1);

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::pc_offset()),
            REG_TMP1.into(),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_SP.into(),
        );

        restore_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.raw_call(self.fct.ptr.to_ptr());

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_SP, offset_dtn + DoraToNativeInfo::last_offset()),
        );

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_THREAD, ThreadLocalData::dtn_offset()),
            REG_TMP1.into(),
        );

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_TMP1.into(),
            Mem::Base(REG_THREAD, ThreadLocalData::exception_object_offset()),
        );

        let lbl_exception = self.masm.test_if_not_nil(REG_TMP1);

        self.masm.safepoint(self.vm.polling_page.addr());
        self.masm.epilog();

        self.masm.bind_label(lbl_exception);
        self.masm.throw(REG_TMP1, Position::new(1, 1));
        self.masm.nop();

        let desc = match self.fct.desc {
            InternalFctDescriptor::NativeThunk(fid) => JitDescriptor::NativeThunk(fid),
            InternalFctDescriptor::AllocThunk => JitDescriptor::AllocThunk,
            InternalFctDescriptor::VerifyThunk => JitDescriptor::VerifyThunk,
            InternalFctDescriptor::TrapThunk => JitDescriptor::TrapThunk,
        };

        self.masm.jit(self.vm, framesize, desc, self.fct.throws)
    }
}

fn save_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), freg);
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), reg);
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

fn restore_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.load_mem(mode, freg, Mem::Base(REG_SP, offset));
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.load_mem(mode, reg, Mem::Base(REG_SP, offset));
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

pub fn start_native_call() {
    THREAD.with(|thread| {
        thread.borrow().handles.push_border();
    });
}

pub fn finish_native_call() {
    THREAD.with(|thread| {
        thread.borrow().handles.pop_border();
    });
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![no_std]
#![plugin(macro_zinc)]

extern crate zinc;

use zinc::hal::timer::Timer;
use zinc::hal::pin::Gpio;
use zinc::hal::stm32f4::{pin, timer};

#[zinc_main]
pub fn main() {
  zinc::hal::mem_init::init_stack();
  zinc::hal::mem_init::init_data();

  let led1 = pin::Pin {
    port: pin::Port::PortD,
    pin: 13u8,
    function: pin::Function::GPIOOut
  };
  let led2 = pin::Pin {
    port: pin::Port::PortD,
    pin: 14u8,
    function: pin::Function::GPIOOut
  };
  led1.setup();
  led2.setup();

  let timer = timer::Timer::new(timer::TimerPeripheral::Timer2, 25u32);

  loop {
    led1.set_high();
    led2.set_low();
    timer.wait_ms(300);
    led1.set_low();
    led2.set_high();
    timer.wait_ms(300);
  }
}
<commit_msg>blink_stm32f4: Fix timer<commit_after>#![feature(plugin)]
#![no_std]
#![plugin(macro_zinc)]

extern crate zinc;

use zinc::hal::timer::Timer;
use zinc::hal::pin::Gpio;
use zinc::hal::stm32f4::{pin, timer};

#[zinc_main]
pub fn main() {
  zinc::hal::mem_init::init_stack();
  zinc::hal::mem_init::init_data();

  let led1 = pin::Pin {
    port: pin::Port::PortD,
    pin: 13u8,
    function: pin::Function::GPIOOut
  };
  let led2 = pin::Pin {
    port: pin::Port::PortD,
    pin: 14u8,
    function: pin::Function::GPIOOut
  };
  led1.setup();
  led2.setup();

  let timer = timer::Timer::new(timer::TimerPeripheral::Timer2, 16u32);

  loop {
    led1.set_high();
    led2.set_low();
    timer.wait_ms(300);
    led1.set_low();
    led2.set_high();
    timer.wait_ms(300);
  }
}
<|endoftext|>


<commit_before>//! Run-time feature detection for the Rust standard library.
//!
//! To detect whether a feature is enabled in the system running the binary
//! use one of the appropriate macro for the target:
//!
//! * `x86` and `x86_64`: [`is_x86_feature_detected`]
//! * `arm`: [`is_arm_feature_detected`]
//! * `aarch64`: [`is_aarch64_feature_detected`]
//! * `mips`: [`is_mips_feature_detected`]
//! * `mips64`: [`is_mips64_feature_detected`]
//! * `powerpc`: [`is_powerpc_feature_detected`]
//! * `powerpc64`: [`is_powerpc64_feature_detected`]

#![unstable(feature = "stdsimd", issue = "27731")]
#![feature(const_fn, staged_api, stdsimd, doc_cfg, allow_internal_unstable)]
#![allow(clippy::shadow_reuse)]
#![deny(clippy::missing_inline_in_public_items)]
#![cfg_attr(target_os = "linux", feature(linkage))]
#![cfg_attr(all(target_os = "freebsd", target_arch = "aarch64"), feature(asm))]
#![cfg_attr(test, allow(unused_imports))]
#![no_std]

#[macro_use]
extern crate cfg_if;

cfg_if! {
    if #[cfg(all(feature = "std_detect_file_io", feature = "std_detect_env_override"))] {
        #[cfg_attr(test, macro_use(println))]
        extern crate std;

        #[allow(unused_imports)]
        use std::{arch, env, fs, io, mem, sync};
    } else if #[cfg(feature = "std_detect_file_io")] {
        #[cfg_attr(test, macro_use(println))]
        extern crate std;

        #[allow(unused_imports)]
        use std::{arch, fs, io, mem, sync};
    } else if #[cfg(feature = "std_detect_env_override")] {
        #[cfg_attr(test, macro_use(println))]
        extern crate std;

        use std::env;
    } else {
        #[cfg(test)]
        #[macro_use(println)]
        extern crate std;

        #[allow(unused_imports)]
        use core::{arch, mem, sync};
    }
}

#[cfg(feature = "std_detect_dlsym_getauxval")]
extern crate libc;

#[doc(hidden)]
#[unstable(feature = "stdsimd", issue = "27731")]
pub mod detect;
<commit_msg>Simplify the std imports<commit_after>//! Run-time feature detection for the Rust standard library.
//!
//! To detect whether a feature is enabled in the system running the binary
//! use one of the appropriate macro for the target:
//!
//! * `x86` and `x86_64`: [`is_x86_feature_detected`]
//! * `arm`: [`is_arm_feature_detected`]
//! * `aarch64`: [`is_aarch64_feature_detected`]
//! * `mips`: [`is_mips_feature_detected`]
//! * `mips64`: [`is_mips64_feature_detected`]
//! * `powerpc`: [`is_powerpc_feature_detected`]
//! * `powerpc64`: [`is_powerpc64_feature_detected`]

#![unstable(feature = "stdsimd", issue = "27731")]
#![feature(const_fn, staged_api, stdsimd, doc_cfg, allow_internal_unstable)]
#![allow(clippy::shadow_reuse)]
#![deny(clippy::missing_inline_in_public_items)]
#![cfg_attr(target_os = "linux", feature(linkage))]
#![cfg_attr(all(target_os = "freebsd", target_arch = "aarch64"), feature(asm))]
#![cfg_attr(test, allow(unused_imports))]
#![no_std]

#[macro_use]
extern crate cfg_if;

cfg_if! {
    if #[cfg(any(feature = "std_detect_file_io", feature = "std_detect_env_override"))] {
        #[cfg_attr(test, macro_use(println))]
        extern crate std;

        #[allow(unused_imports)]
        use std::{arch, env, fs, io, mem, sync};
    } else {
        #[cfg(test)]
        #[macro_use(println)]
        extern crate std;

        #[allow(unused_imports)]
        use core::{arch, mem, sync};
    }
}

#[cfg(feature = "std_detect_dlsym_getauxval")]
extern crate libc;

#[doc(hidden)]
#[unstable(feature = "stdsimd", issue = "27731")]
pub mod detect;
<|endoftext|>