<commit_before>use crate::errors::OscError;

use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_a, is_not, tag, take, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, opt, recognize, verify};
use nom::error::{ErrorKind, ParseError};
use nom::multi::{many1, separated_list1};
use nom::sequence::{delimited, pair, separated_pair};
use nom::{IResult, Parser};

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => parts,
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid"),
        };

        Ok(Matcher {
            pattern: pattern.into(),
            pattern_parts,
        })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?; // TODO: Create an address struct so we don't have to re-check addresses every time we match

        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => {
                                match_wildcard(remainder, *l, None)
                            }
                            _ => match_wildcard(remainder, *l, Some(next)),
                        }
                    } else {
                        match_wildcard(remainder, *l, None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false), // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        Ok(remainder.is_empty())
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F',
    }
}

/// Parser to turn a choice like '{foo,bar}' into a vector containing the choices, like ["foo", "bar"]
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>> {
    delimited(
        char('{'),
        separated_list1(tag(","), take_while1(is_address_character)),
        char('}'),
    )(input)
}

/// Parser to recognize a character class like [!a-zA-Z] and return '!a-zA-Z'
fn pattern_character_class(input: &str) -> IResult<&str, &str> {
    let inner = pair(
        // It is important to read the leading negating '!' to make sure the rest of the parsed
        // character class isn't empty. E.g. '[!]' is not a valid character class.
        recognize(opt(tag("!"))),
        take_while1(is_address_character),
    );

    delimited(char('['), recognize(inner), char(']'))(input)
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range = if start >= end {
        end..=start
    } else {
        start..=end
    };

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

/// Removes all duplicates from a string of characters
fn deduplicate_characters(input: String) -> String {
    let mut out: String = String::from("");
    for char in input.chars() {
        if !out.contains(char) {
            out.push(char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false,
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(
                satisfy(is_address_character),
                char('-'),
                satisfy(is_address_character),
            )
            .map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| String::from("-")),
        ))))(input);

        match characters {
            Ok((_, o)) => CharacterClass {
                negated,
                characters: deduplicate_characters(o.concat()),
            },
            _ => {
                panic!("Invalid character class formatting {}", s)
            }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent> {
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character)
            .map(|s: &str| AddressPatternComponent::Tag(String::from(s))),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| AddressPatternComponent::Tag(c.to_string())),
        tag("?").map(|_| AddressPatternComponent::WildcardSingle),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| AddressPatternComponent::Wildcard(x.matches('?').count())),
        pattern_choice.map(|choices: Vec<&str>| {
            AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect())
        }),
        pattern_character_class
            .map(|s: &str| AddressPatternComponent::CharacterClass(CharacterClass::new(s))),
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str> {
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str> {
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(
    input: &'a str,
    character_class: &'a CharacterClass,
) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &[String]) -> IResult<&'a str, &'a str> {
    for choice in choices {
        if let Ok((i, o)) = tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            return Ok((i, o));
        }
    }
    Err(nom::Err::Error(nom::error::Error::from_error_kind(
        input,
        ErrorKind::Tag,
    )))
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(
    input: &'a str,
    minimum_length: usize,
    next: Option<&AddressPatternComponent>,
) -> IResult<&'a str, &'a str> {
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| {
            s.len() >= minimum_length
        })(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once('/') {
                Some((p, _)) => p,
                None => input,
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => {
                        match_character_class(substring, cc)
                    }
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => {
                        panic!("Single wildcard ('?') must not follow wildcard ('*')")
                    }
                    AddressPatternComponent::Wildcard(_) => {
                        panic!("Double wildcards must be condensed into one")
                    }
                };

                if result.is_ok() {
                    longest = i
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError> {
    match all_consuming::<_, _, nom::error::Error<&str>, _>(many1(pair(
        tag("/"),
        take_while1(is_address_character),
    )))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string())),
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(alt((
        take_while1(is_address_character),
        tag("?"),
        tag("*"),
        recognize(pattern_choice),
        pattern_character_class,
    )))(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError> {
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(tag("/"), address_pattern_part_parser.map(|x| x.concat())),
    ))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string())),
    }
}
<commit_msg>Replace deduplication function with hashset<commit_after>use crate::errors::OscError;

use alloc::string::{String, ToString};
use alloc::vec::Vec;
use std::collections::HashSet;
use std::iter::FromIterator;
use nom::branch::alt;
use nom::bytes::complete::{is_a, is_not, tag, take, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, opt, recognize, verify};
use nom::error::{ErrorKind, ParseError};
use nom::multi::{many1, separated_list1};
use nom::sequence::{delimited, pair, separated_pair};
use nom::{IResult, Parser};

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => parts,
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid"),
        };

        Ok(Matcher {
            pattern: pattern.into(),
            pattern_parts,
        })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?; // TODO: Create an address struct so we don't have to re-check addresses every time we match

        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => {
                                match_wildcard(remainder, *l, None)
                            }
                            _ => match_wildcard(remainder, *l, Some(next)),
                        }
                    } else {
                        match_wildcard(remainder, *l, None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false), // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        Ok(remainder.is_empty())
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F',
    }
}

/// Parser to turn a choice like '{foo,bar}' into a vector containing the choices, like ["foo", "bar"]
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>> {
    delimited(
        char('{'),
        separated_list1(tag(","), take_while1(is_address_character)),
        char('}'),
    )(input)
}

/// Parser to recognize a character class like [!a-zA-Z] and return '!a-zA-Z'
fn pattern_character_class(input: &str) -> IResult<&str, &str> {
    let inner = pair(
        // It is important to read the leading negating '!' to make sure the rest of the parsed
        // character class isn't empty. E.g. '[!]' is not a valid character class.
        recognize(opt(tag("!"))),
        take_while1(is_address_character),
    );

    delimited(char('['), recognize(inner), char(']'))(input)
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range = if start >= end {
        end..=start
    } else {
        start..=end
    };

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false,
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(
                satisfy(is_address_character),
                char('-'),
                satisfy(is_address_character),
            )
            .map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| String::from("-")),
        ))))(input);

        match characters {
            Ok((_, o)) => CharacterClass {
                negated,
                characters: HashSet::<char>::from_iter(o.concat().chars()).iter().collect(),
            },
            _ => {
                panic!("Invalid character class formatting {}", s)
            }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent> {
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character)
            .map(|s: &str| AddressPatternComponent::Tag(String::from(s))),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| AddressPatternComponent::Tag(c.to_string())),
        tag("?").map(|_| AddressPatternComponent::WildcardSingle),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| AddressPatternComponent::Wildcard(x.matches('?').count())),
        pattern_choice.map(|choices: Vec<&str>| {
            AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect())
        }),
        pattern_character_class
            .map(|s: &str| AddressPatternComponent::CharacterClass(CharacterClass::new(s))),
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str> {
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str> {
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(
    input: &'a str,
    character_class: &'a CharacterClass,
) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &[String]) -> IResult<&'a str, &'a str> {
    for choice in choices {
        if let Ok((i, o)) = tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            return Ok((i, o));
        }
    }
    Err(nom::Err::Error(nom::error::Error::from_error_kind(
        input,
        ErrorKind::Tag,
    )))
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(
    input: &'a str,
    minimum_length: usize,
    next: Option<&AddressPatternComponent>,
) -> IResult<&'a str, &'a str> {
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| {
            s.len() >= minimum_length
        })(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once('/') {
                Some((p, _)) => p,
                None => input,
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => {
                        match_character_class(substring, cc)
                    }
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => {
                        panic!("Single wildcard ('?') must not follow wildcard ('*')")
                    }
                    AddressPatternComponent::Wildcard(_) => {
                        panic!("Double wildcards must be condensed into one")
                    }
                };

                if result.is_ok() {
                    longest = i
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError> {
    match all_consuming::<_, _, nom::error::Error<&str>, _>(many1(pair(
        tag("/"),
        take_while1(is_address_character),
    )))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string())),
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(alt((
        take_while1(is_address_character),
        tag("?"),
        tag("*"),
        recognize(pattern_choice),
        pattern_character_class,
    )))(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError> {
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(tag("/"), address_pattern_part_parser.map(|x| x.concat())),
    ))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string())),
    }
}
<|endoftext|>


<commit_before>#![crate_type = "staticlib"]
#![allow(unused_variables)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
#![allow(improper_ctypes)]

use util::*;
use types::*;
use std::slice;
use std::os::raw::c_void;

mod util;
mod types;

static mut this_module: *mut module = 0 as *mut module;

#[no_mangle]
pub unsafe extern fn analyse(ast_ptr: *mut mpc_ast_t)
{
    this_module = &mut module::new() as *mut module;

    process(ast_ptr, 0);
}

pub unsafe fn process(ast_ptr: *mut mpc_ast_t, level: usize)
{
    let ast = &*ast_ptr;
    let children = slice::from_raw_parts(ast.children, ast.children_num as usize);

    for i in 0 .. ast.children_num as usize
    {
        let tag = read_string( (*children[i]).tag );
        let ref child = *children[i];

        match tag.as_ref()
        {
            "func" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag" + &RESET, YELLOW.to_string() + &tag + &RESET);
                read_func(children[i] as *mut mpc_ast_t);
            },
            "module" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag", YELLOW.to_string() + &tag + &RESET);
                println!("{} {}:", GREEN.to_string() + &"module" + &RESET, read_string((*child.get_children()[0]).contents));
                (*this_module).name = (*child.get_children()[0]).contents;
            },

            "statement" => println!("statement"),
            _ => {},
        }
        process(children[i] as *mut mpc_ast_t, level + 1);
    }

}

pub unsafe fn read_func(ast_ptr: *mut mpc_ast_t)
{
    let ref ast = *ast_ptr;
    let mut base_index = 0;
    let mut func: function = function::new();

    if read_string((*ast.get_children()[0]).tag) == "public" { func.public = true; base_index = 1}

    func.name = (*ast.get_children()[base_index + 2]).contents;
    let ref child = *ast.get_children()[base_index + 1];
    let tag = read_string( child.tag );

    match tag.as_ref()
    {
        "natives" =>
        {
            println!("it's a native {}", read_string(child.contents) );
            let mut return_type: symbol_type = symbol_type::new();
            return_type.name = child.contents;
            func.ret_type = return_type;
        },
        "type" =>
        {
            let mut index = 0;
            let mut is_const = false;
            let mut is_vol = false;

            if read_string( (*child.get_children()[0]).contents ) == "const"
            {
                is_const = true;
                index += 1;
            }
            if read_string( (*child.get_children()[0]).contents ) == "volatile"
            {
                is_vol = true;
                index += 1;
            }

            let mut return_type: symbol_type = symbol_type::new();
            return_type.name = (*child.get_children()[index]).contents;
            return_type.constant = is_const;
            return_type.volatile = is_vol;

            for i in 0..child.children_num as usize
            {
                if read_string( (*child.get_children()[i]).tag ) == "ptrop"
                || read_string( (*child.get_children()[i]).tag ) == "index"
                {
                    return_type.indirection += 1;
                }
            }

            func.ret_type = return_type;
        },
        _ => {}
    }

	func.params = read_params(ast.get_children()[base_index + 3]);

	(*(*this_module).functions).add(&mut func as *mut c_void);
    println!("{}type {} with indirection {}", if func.ret_type.constant { "constant " }
                                               else if func.ret_type.volatile { "volatile " } else { "" },
                                               read_string(func.ret_type.name), func.ret_type.indirection);
}

pub unsafe fn read_params(ast_ptr: *const mpc_ast_t) -> *mut vector
{
	let ref ast = *ast_ptr;
	let params:*mut vector = vector::new();
	println!("read_params: {}",  read_string(ast.tag));

	params
}<commit_msg>implement reading and displaying for funcs, types and modules<commit_after>#![crate_type = "staticlib"]
#![allow(unused_variables)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
#![allow(improper_ctypes)]

use util::*;
use types::*;
use std::slice;
use std::mem;
use std::os::raw::c_void;
//use std::os::raw::c_char;

mod util;
mod types;

static mut this_module: *mut module = 0 as *mut module;

#[no_mangle]
pub unsafe extern fn analyse(ast_ptr: *mut mpc_ast_t)
{
    this_module = &mut module::new() as *mut module;

	let v = vector::new();
	(*v).add(mem::transmute( make_string("potato".to_string()) ));
	//println!("test: {}", read_string((*v).get(0) as *mut c_char));

    process(ast_ptr, 0);
    print_module();
}

pub unsafe fn process(ast_ptr: *mut mpc_ast_t, level: usize)
{
    let ast = &*ast_ptr;
    let children = slice::from_raw_parts(ast.children, ast.children_num as usize);

    for i in 0 .. ast.children_num as usize
    {
        let tag = read_string( (*children[i]).tag );
        let ref child = *children[i];

        match tag.as_ref()
        {
            "func" => read_func(children[i] as *mut mpc_ast_t),
            "module" => (*this_module).name = (*child.get_children()[0]).contents,
            "import" => read_import(children[i] as *mut mpc_ast_t),
            "usertype" => read_usertype(children[i] as *mut mpc_ast_t),
            "statement" => println!("statement"),
            _ => {},
        }
        process(children[i] as *mut mpc_ast_t, level + 1);
    }

}

pub unsafe fn read_import(ast_ptr: *mut mpc_ast_t)
{
	let ref ast = *ast_ptr;
	let ref mut imp = *Box::into_raw(Box::new(import::new()));;
	imp.name = (*ast.get_children()[0]).contents;

	for i in 1..ast.children_num as usize
	{
		let content = read_string((*ast.get_children()[i]).contents);
		match content.as_ref()
		{
			"local" => imp.local = true,
			_ =>
			{
				//println!("test: {}", read_string((*ast.get_children()[i]).contents));
				imp.w_alias = true;
				imp.alias = (*ast.get_children()[i]).contents;
			},
		}
	}

	(*(*this_module).imports).add(
		mem::transmute(imp as *mut import)
	);
}

pub unsafe fn read_usertype(ast_ptr: *mut mpc_ast_t)
{
	let ref ast = *ast_ptr;
	let mut index = 0;
	let ref mut t: user_type = *Box::into_raw(Box::new(user_type::new()));

	if read_string((*ast.get_children()[0]).tag) == "public" { t.public = true; index = 1; }

	let ref child = *ast.get_children()[index];
	let tag = read_string( (*ast.get_children()[index]).tag );

	t.name = (*child.get_children()[0]).contents;

	match tag.as_ref()
	{
		"globalunion" => t.t_type = type_kind::UNION,
		"structure" => t.t_type = type_kind::STRUCT,
		"enumtype" => t.t_type = type_kind::ENUM,
		"functype" => t.t_type = type_kind::FUNC,
		"alias" => t.t_type = type_kind::ALIAS,
		_ => {}
	}

	(*(*this_module).types).add(
		mem::transmute(t as *mut user_type)
	);
}

pub unsafe fn read_func(ast_ptr: *mut mpc_ast_t)
{
    let ref ast = *ast_ptr;
    let mut base_index = 0;
    let ref mut func: function = *Box::into_raw(Box::new(function::new()));;

    if read_string((*ast.get_children()[0]).tag) == "public" { func.public = true; base_index = 1; }

    func.name = (*ast.get_children()[base_index + 2]).contents;
    let ref child = *ast.get_children()[base_index + 1];
    let tag = read_string( child.tag );

    func.ret_type = read_type(child);

	func.params = read_params(ast.get_children()[base_index + 3]);

	(*(*this_module).functions).add(mem::transmute(func as *mut function));
}

pub unsafe fn read_params(ast_ptr: *const mpc_ast_t) -> *mut vector
{
	let ref ast = *ast_ptr;
	let params:*mut vector = vector::new();
	for i in 0..ast.children_num as usize
	{
		let ref child = *ast.get_children()[i];
		let ref mut p = *Box::into_raw(Box::new(param::new()));
		p.p_type = read_type( &(*child.get_children()[0]) );
		p.name = (*child.get_children()[1]).contents;

		vector_add(
			params,
			(p as *mut param) as *mut c_void
		);

		let p:*mut param = (*params).get(i) as *mut param;
	}

	/*//read example
	for i in 0..(*params).total()
	{
		let d = (*params).get(i) as *mut param;
		println!(
			"test param: {} {}",
			read_string( (*d).name ),
			read_string( (*d).p_type.name )
		);
	}*/

	params
}

pub unsafe fn read_param(ast_ptr: *const mpc_ast_t) -> param
{
	let ref ast = *ast_ptr;
	let mut p = param::new();
	p.p_type = read_type( &(*ast.get_children()[0]) );
	if ast.children_num != 1 {p.name = (*ast.get_children()[1]).contents };
	println!("{}", read_string(p.name));
	p
}

pub unsafe fn read_type(ast: &mpc_ast_t) -> symbol_type
{
	if read_string(ast.tag) == "natives" || ast.children_num == 0
	{
		let mut s: symbol_type = symbol_type::new();
		s.name = ast.contents;
		return s;
	}

    let mut index = 0;
    let mut is_const = false;
    let mut is_vol = false;

   	if read_string( (*ast.get_children()[0]).contents ) == "const"
   	{
       	is_const = true;
       	index += 1;
   	}
   	if read_string( (*ast.get_children()[0]).contents ) == "volatile"
   	{
       	is_vol = true;
       	index += 1;
   	}

	let mut s: symbol_type = symbol_type::new();
	s.name = (*ast.get_children()[index]).contents;
	s.constant = is_const;
	s.volatile = is_vol;

	for i in 0..ast.children_num as usize
	{
	    if read_string( (*ast.get_children()[i]).tag ) == "ptrop"
	    || read_string( (*ast.get_children()[i]).tag ) == "index"
	    {
	        s.indirection += 1;
	    }
	}
	s
}

pub unsafe fn print_module()
{
	let ref module = *this_module;
	let ref mut imports = *module.imports;
	let ref mut funcs = *module.functions;
	let ref mut types = *module.types;

	println!("{} {}:",
		GREEN.to_string() + &"module" + &RESET,
		read_string((*this_module).name)
	);

	for i in 0..imports.total()
	{
		print!("  {}imports module{} ", YELLOW, RESET);
		print!("{}'{}'{}",
			RED,
			read_string(
			(*(imports.get(i) as *mut import)).name
			),
			RESET
		);
		if (*(imports.get(i) as *mut import)).local
		{
			print!("{}{}{}",
				MAGENTA,
				" locally",
				RESET
			);
		}
		if (*(imports.get(i) as *mut import)).w_alias
		{
			print!(" {}with alias{} ", CYAN, RESET);
			print!("{}'{}'{}",
				YELLOW,
				read_string(
					(*(imports.get(i) as *mut import)).alias
				),
				RESET
			);
		}
		print!("\n");
	}

	println!("\n  {}functions:{}", MAGENTA, RESET);
	for i in 0..funcs.total()
	{
		print_func( (funcs.get(i) as *mut function) );
	}

	println!("\n  {}types:{}", MAGENTA, RESET);
	for i in 0..types.total()
	{
		print_usertype( (types.get(i) as *mut user_type) );
	}
}

pub unsafe fn print_func(func_ptr: *mut function)
{
	let ref func = *func_ptr;
	let ref mut params = *func.params;
	if func.public { print!("    {}public {}function{} ", GREEN, CYAN, RESET); }
	else { print!("    {}function{} ", CYAN, RESET); }

	print!("{}{}{}", YELLOW, read_string(func.name), RESET);
	print!(" {}returning{} ", MAGENTA, RESET);
	print_type(&func.ret_type);

	print!("\n");
	for i in 0..params.total()
	{
		let ref p = *(params.get(i) as *mut param);
		print!("      parameter{} {}{}: ",
			MAGENTA,
			read_string(p.name),
			RESET
		);
		print_type(&p.p_type);
		print!("\n");
	}
}

pub unsafe fn print_usertype(type_ptr: *mut user_type)
{
	let ref t = *type_ptr;

	if t.public { print!("    {}public{} ", GREEN, CYAN); }
		   else { print!("    "); }

	match t.t_type
	{
		type_kind::ALIAS => print!("{}{}{}", YELLOW, "alias type", RESET),
		type_kind::STRUCT => print!("{}{}{}", YELLOW, "structure", RESET),
		type_kind::UNION => print!("{}{}{}", YELLOW, "union", RESET),
		type_kind::ENUM => print!("{}{}{}", YELLOW, "enumeration", RESET),
		type_kind::FUNC => print!("{}{}{}", YELLOW, "function type", RESET),
		type_kind::TEMP => {}
	}

	print!(" {}{}{}\n", CYAN, read_string(t.name), RESET);
}

pub unsafe fn print_type(s_type: &symbol_type)
{
	if s_type.constant { print!("constant "); }
	if s_type.volatile { print!("volatile "); }

	print!("{}{}{} ",
		RED,
		read_string(s_type.name),
		RESET
	);

	print!("{}[i:{}]{}",
		GREEN,
		s_type.indirection,
		RESET
	);
}<|endoftext|>


<commit_before>#![crate_name = "uu_cat"]

// This file is part of the uutils coreutils package.
//
// (c) Jordi Boggiano <j.boggiano@seld.be>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

// last synced with: cat (GNU coreutils) 8.13

#[macro_use]
extern crate uucore;

use std::fs::File;
use std::intrinsics::{copy_nonoverlapping};
use std::io::{stdout, stdin, stderr, Write, Read, Result};
use uucore::fs::is_stdin_interactive;

static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Concatenate FILE(s), or standard input, to standard output
 With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &'static str = "";

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
        .optflag("A", "show-all", "equivalent to -vET")
        .optflag("b",
                 "number-nonblank",
                 "number nonempty output lines, overrides -n")
        .optflag("e", "", "equivalent to -vE")
        .optflag("E", "show-ends", "display $ at end of each line")
        .optflag("n", "number", "number all output lines")
        .optflag("s", "squeeze-blank", "suppress repeated empty output lines")
        .optflag("t", "", "equivalent to -vT")
        .optflag("T", "show-tabs", "display TAB characters as ^I")
        .optflag("v",
                 "show-nonprinting",
                 "use ^ and M- notation, except for LF (\\n) and TAB (\\t)")
        .parse(args);

    let number_mode = if matches.opt_present("b") {
        NumberingMode::NumberNonEmpty
    } else if matches.opt_present("n") {
        NumberingMode::NumberAll
    } else {
        NumberingMode::NumberNone
    };
    let show_nonprint =
        matches.opts_present(&["A".to_owned(), "e".to_owned(), "t".to_owned(), "v".to_owned()]);
    let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]);
    let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
    let squeeze_blank = matches.opt_present("s");
    let mut files = matches.free;
    if files.is_empty() {
        files.push("-".to_owned());
    }

    exec(files,
         number_mode,
         show_nonprint,
         show_ends,
         show_tabs,
         squeeze_blank);

    0
}

#[derive(Eq, PartialEq)]
enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

fn write_lines(files: Vec<String>, number: NumberingMode, squeeze_blank: bool, show_ends: bool) {
    // initialize end of line
    let end_of_line = if show_ends {
        "$\n".as_bytes()
    } else {
        "\n".as_bytes()
    };

    let mut line_counter: usize = 1;

    for (mut reader, interactive) in files.iter().filter_map(|p| open(&p[..])) {

        let mut in_buf = [0; 1024 * 31];
        let mut out_buf = [0; 1024 * 64];
        let mut writer = UnsafeWriter::new(&mut out_buf[..], stdout());
        let mut at_line_start = true;
        let mut one_blank_kept = false;
        while let Ok(n) = reader.read(&mut in_buf) {
            if n == 0 {
                break;
            }

            let in_buf = &in_buf[..n];
            let mut pos = 0;
            while pos < n {
                writer.possibly_flush();
                if in_buf[pos] == '\n' as u8 {
                    if !at_line_start || !squeeze_blank || !one_blank_kept {
                        one_blank_kept = true;
                        if at_line_start && number == NumberingMode::NumberAll {
                            (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                            line_counter += 1;
                        }
                        writer.write_all(end_of_line).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                    }
                    at_line_start = true;
                    pos += 1;
                    continue;
                } else if one_blank_kept {
                    one_blank_kept = false;
                }
                if at_line_start && number != NumberingMode::NumberNone {
                    (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                    line_counter += 1;
                }
                match in_buf[pos..].iter().position(|c| *c == '\n' as u8) {
                    Some(p) => {
                        writer.write_all(&in_buf[pos..pos + p]).unwrap();
                        writer.write_all(end_of_line).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                        pos += p + 1;
                        at_line_start = true;
                    }
                    None => {
                        writer.write_all(&in_buf[pos..]).unwrap();
                        at_line_start = false;
                        break;
                    }
                };
            }
        }
    }
}

fn write_bytes(files: Vec<String>,
               number: NumberingMode,
               squeeze_blank: bool,
               show_ends: bool,
               show_tabs: bool,
               show_nonprint: bool) {
    // initialize end of line
    let end_of_line = if show_ends {
        "$\n".as_bytes()
    } else {
        "\n".as_bytes()
    };
    // initialize tab simbol
    let tab = if show_tabs {
        "^I".as_bytes()
    } else {
        "\t".as_bytes()
    };

    let mut line_counter: usize = 1;

    for (mut reader, interactive) in files.iter().filter_map(|p| open(&p[..])) {

        // Flush all 1024 iterations.
        let mut flush_counter = 0usize..1024;

        let mut in_buf = [0; 1024 * 32];
        let mut out_buf = [0; 1024 * 64];
        let mut writer = UnsafeWriter::new(&mut out_buf[..], stdout());
        let mut at_line_start = true;
        while let Ok(n) = reader.read(&mut in_buf) {
            if n == 0 {
                break;
            }

            for &byte in in_buf[..n].iter() {
                if flush_counter.next().is_none() {
                    writer.possibly_flush();
                    flush_counter = 0usize..1024;
                }
                if byte == '\n' as u8 {
                    if !at_line_start || !squeeze_blank {
                        if at_line_start && number == NumberingMode::NumberAll {
                            (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                            line_counter += 1;
                        }
                        writer.write_all(end_of_line).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                    }
                    at_line_start = true;
                    continue;
                }
                if at_line_start && number != NumberingMode::NumberNone {
                    (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                    line_counter += 1;
                    at_line_start = false;
                }

                // This code is slow because of the many branches. cat in glibc avoids
                // this by having the whole loop inside show_nonprint.
                if byte == '\t' as u8 {
                        writer.write_all(tab)
                    } else if show_nonprint {
                        let byte = match byte {
                            128...255 => {
                                writer.write_all("M-".as_bytes()).unwrap();
                                byte - 128
                            }
                            _ => byte,
                        };
                        match byte {
                            0...31 => writer.write_all(&['^' as u8, byte + 64]),
                            127 => writer.write_all(&['^' as u8, byte - 64]),
                            _ => writer.write_all(&[byte]),
                        }
                    } else {
                        writer.write_all(&[byte])
                    }
                    .unwrap();
            }
        }
    }
}

fn write_fast(files: Vec<String>) {
    let mut writer = stdout();
    let mut in_buf = [0; 1024 * 64];

    for (mut reader, _) in files.iter().filter_map(|p| open(&p[..])) {
        while let Ok(n) = reader.read(&mut in_buf) {
            if n == 0 {
                break;
            }
            // This interface is completely broken.
            writer.write_all(&in_buf[..n]).unwrap();
        }
    }
}

fn exec(files: Vec<String>,
        number: NumberingMode,
        show_nonprint: bool,
        show_ends: bool,
        show_tabs: bool,
        squeeze_blank: bool) {

    if show_nonprint || show_tabs {
        write_bytes(files,
                    number,
                    squeeze_blank,
                    show_ends,
                    show_tabs,
                    show_nonprint);
    } else if number != NumberingMode::NumberNone || squeeze_blank || show_ends {
        write_lines(files, number, squeeze_blank, show_ends);
    } else {
        write_fast(files);
    }
    pipe_flush!();
}

fn open(path: &str) -> Option<(Box<Read>, bool)> {
    if path == "-" {
        let stdin = stdin();
        return Some((Box::new(stdin) as Box<Read>, is_stdin_interactive()));
    }

    match File::open(path) {
        Ok(f) => Some((Box::new(f) as Box<Read>, false)),
        Err(e) => {
            (writeln!(&mut stderr(), "cat: {0}: {1}", path, e.to_string())).unwrap();
            None
        }
    }
}

struct UnsafeWriter<'a, W: Write> {
    inner: W,
    buf: &'a mut [u8],
    pos: usize,
    threshold: usize,
}

impl<'a, W: Write> UnsafeWriter<'a, W> {
    fn new(buf: &'a mut [u8], inner: W) -> UnsafeWriter<'a, W> {
        let threshold = buf.len() / 2;
        UnsafeWriter {
            inner: inner,
            buf: buf,
            pos: 0,
            threshold: threshold,
        }
    }

    fn flush_buf(&mut self) -> Result<()> {
        if self.pos != 0 {
            let ret = self.inner.write(&self.buf[..self.pos]);
            self.pos = 0;
            match ret {
                Ok(_) => Ok(()),
                Err(e) => Err(e),
            }
        } else {
            Ok(())
        }
    }

    fn possibly_flush(&mut self) {
        if self.pos > self.threshold {
            self.inner.write_all(&self.buf[..self.pos]).unwrap();
            self.pos = 0;
        }
    }
}

// This method was used in Write::write only. It must not be called in the correct cat code
// and is placed in the bottleneck place, so it is not used anymore.
// Uncomment this and code in Write::write for more convenient testing of a new cat version.
//#[inline(never)]
//fn fail() -> ! {
//    panic!("assertion failed");
//}

impl<'a, W: Write> Write for UnsafeWriter<'a, W> {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        //let dst = &mut self.buf[self.pos..];
        let len = buf.len();
        // the condition is false for current code and every correct code
        // see fail method comment for more information
        // if len > dst.len() {
        //     fail();
        // }
        unsafe { copy_nonoverlapping(buf.as_ptr(), self.buf[self.pos..].as_mut_ptr(), len) }
        self.pos += len;
        Ok(len)
    }

    fn flush(&mut self) -> Result<()> {
        self.flush_buf().and_then(|()| self.inner.flush())
    }
}

impl<'a, W: Write> Drop for UnsafeWriter<'a, W> {
    fn drop(&mut self) {
        let _ = self.flush_buf();
    }
}
<commit_msg>cat: conditional compilation added<commit_after>#![crate_name = "uu_cat"]

// This file is part of the uutils coreutils package.
//
// (c) Jordi Boggiano <j.boggiano@seld.be>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

// last synced with: cat (GNU coreutils) 8.13

#[macro_use]
extern crate uucore;

use std::fs::File;
use std::intrinsics::{copy_nonoverlapping};
use std::io::{stdout, stdin, stderr, Write, Read, Result};
use uucore::fs::is_stdin_interactive;

static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Concatenate FILE(s), or standard input, to standard output
 With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &'static str = "";

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
        .optflag("A", "show-all", "equivalent to -vET")
        .optflag("b",
                 "number-nonblank",
                 "number nonempty output lines, overrides -n")
        .optflag("e", "", "equivalent to -vE")
        .optflag("E", "show-ends", "display $ at end of each line")
        .optflag("n", "number", "number all output lines")
        .optflag("s", "squeeze-blank", "suppress repeated empty output lines")
        .optflag("t", "", "equivalent to -vT")
        .optflag("T", "show-tabs", "display TAB characters as ^I")
        .optflag("v",
                 "show-nonprinting",
                 "use ^ and M- notation, except for LF (\\n) and TAB (\\t)")
        .parse(args);

    let number_mode = if matches.opt_present("b") {
        NumberingMode::NumberNonEmpty
    } else if matches.opt_present("n") {
        NumberingMode::NumberAll
    } else {
        NumberingMode::NumberNone
    };
    let show_nonprint =
        matches.opts_present(&["A".to_owned(), "e".to_owned(), "t".to_owned(), "v".to_owned()]);
    let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]);
    let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
    let squeeze_blank = matches.opt_present("s");
    let mut files = matches.free;
    if files.is_empty() {
        files.push("-".to_owned());
    }

    exec(files,
         number_mode,
         show_nonprint,
         show_ends,
         show_tabs,
         squeeze_blank);

    0
}

#[derive(Eq, PartialEq)]
enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

fn write_lines(files: Vec<String>, number: NumberingMode, squeeze_blank: bool, show_ends: bool) {
    // initialize end of line
    let end_of_line = if show_ends {
        "$\n".as_bytes()
    } else {
        "\n".as_bytes()
    };

    let mut line_counter: usize = 1;

    for (mut reader, interactive) in files.iter().filter_map(|p| open(&p[..])) {

        let mut in_buf = [0; 1024 * 31];
        let mut out_buf = [0; 1024 * 64];
        let mut writer = UnsafeWriter::new(&mut out_buf[..], stdout());
        let mut at_line_start = true;
        let mut one_blank_kept = false;
        while let Ok(n) = reader.read(&mut in_buf) {
            if n == 0 {
                break;
            }

            let in_buf = &in_buf[..n];
            let mut pos = 0;
            while pos < n {
                writer.possibly_flush();
                if in_buf[pos] == '\n' as u8 {
                    if !at_line_start || !squeeze_blank || !one_blank_kept {
                        one_blank_kept = true;
                        if at_line_start && number == NumberingMode::NumberAll {
                            (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                            line_counter += 1;
                        }
                        writer.write_all(end_of_line).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                    }
                    at_line_start = true;
                    pos += 1;
                    continue;
                } else if one_blank_kept {
                    one_blank_kept = false;
                }
                if at_line_start && number != NumberingMode::NumberNone {
                    (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                    line_counter += 1;
                }
                match in_buf[pos..].iter().position(|c| *c == '\n' as u8) {
                    Some(p) => {
                        writer.write_all(&in_buf[pos..pos + p]).unwrap();
                        writer.write_all(end_of_line).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                        pos += p + 1;
                        at_line_start = true;
                    }
                    None => {
                        writer.write_all(&in_buf[pos..]).unwrap();
                        at_line_start = false;
                        break;
                    }
                };
            }
        }
    }
}

fn write_bytes(files: Vec<String>,
               number: NumberingMode,
               squeeze_blank: bool,
               show_ends: bool,
               show_tabs: bool,
               show_nonprint: bool) {
    // initialize end of line
    let end_of_line = if show_ends {
        "$\n".as_bytes()
    } else {
        "\n".as_bytes()
    };
    // initialize tab simbol
    let tab = if show_tabs {
        "^I".as_bytes()
    } else {
        "\t".as_bytes()
    };

    let mut line_counter: usize = 1;

    for (mut reader, interactive) in files.iter().filter_map(|p| open(&p[..])) {

        // Flush all 1024 iterations.
        let mut flush_counter = 0usize..1024;

        let mut in_buf = [0; 1024 * 32];
        let mut out_buf = [0; 1024 * 64];
        let mut writer = UnsafeWriter::new(&mut out_buf[..], stdout());
        let mut at_line_start = true;
        while let Ok(n) = reader.read(&mut in_buf) {
            if n == 0 {
                break;
            }

            for &byte in in_buf[..n].iter() {
                if flush_counter.next().is_none() {
                    writer.possibly_flush();
                    flush_counter = 0usize..1024;
                }
                if byte == '\n' as u8 {
                    if !at_line_start || !squeeze_blank {
                        if at_line_start && number == NumberingMode::NumberAll {
                            (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                            line_counter += 1;
                        }
                        writer.write_all(end_of_line).unwrap();
                        if interactive {
                            writer.flush().unwrap();
                        }
                    }
                    at_line_start = true;
                    continue;
                }
                if at_line_start && number != NumberingMode::NumberNone {
                    (write!(&mut writer, "{0:6}\t", line_counter)).unwrap();
                    line_counter += 1;
                    at_line_start = false;
                }

                // This code is slow because of the many branches. cat in glibc avoids
                // this by having the whole loop inside show_nonprint.
                if byte == '\t' as u8 {
                        writer.write_all(tab)
                    } else if show_nonprint {
                        let byte = match byte {
                            128...255 => {
                                writer.write_all("M-".as_bytes()).unwrap();
                                byte - 128
                            }
                            _ => byte,
                        };
                        match byte {
                            0...31 => writer.write_all(&['^' as u8, byte + 64]),
                            127 => writer.write_all(&['^' as u8, byte - 64]),
                            _ => writer.write_all(&[byte]),
                        }
                    } else {
                        writer.write_all(&[byte])
                    }
                    .unwrap();
            }
        }
    }
}

fn write_fast(files: Vec<String>) {
    let mut writer = stdout();
    let mut in_buf = [0; 1024 * 64];

    for (mut reader, _) in files.iter().filter_map(|p| open(&p[..])) {
        while let Ok(n) = reader.read(&mut in_buf) {
            if n == 0 {
                break;
            }
            // This interface is completely broken.
            writer.write_all(&in_buf[..n]).unwrap();
        }
    }
}

fn exec(files: Vec<String>,
        number: NumberingMode,
        show_nonprint: bool,
        show_ends: bool,
        show_tabs: bool,
        squeeze_blank: bool) {

    if show_nonprint || show_tabs {
        write_bytes(files,
                    number,
                    squeeze_blank,
                    show_ends,
                    show_tabs,
                    show_nonprint);
    } else if number != NumberingMode::NumberNone || squeeze_blank || show_ends {
        write_lines(files, number, squeeze_blank, show_ends);
    } else {
        write_fast(files);
    }
    pipe_flush!();
}

fn open(path: &str) -> Option<(Box<Read>, bool)> {
    if path == "-" {
        let stdin = stdin();
        return Some((Box::new(stdin) as Box<Read>, is_stdin_interactive()));
    }

    match File::open(path) {
        Ok(f) => Some((Box::new(f) as Box<Read>, false)),
        Err(e) => {
            (writeln!(&mut stderr(), "cat: {0}: {1}", path, e.to_string())).unwrap();
            None
        }
    }
}

struct UnsafeWriter<'a, W: Write> {
    inner: W,
    buf: &'a mut [u8],
    pos: usize,
    threshold: usize,
}

impl<'a, W: Write> UnsafeWriter<'a, W> {
    fn new(buf: &'a mut [u8], inner: W) -> UnsafeWriter<'a, W> {
        let threshold = buf.len() / 2;
        UnsafeWriter {
            inner: inner,
            buf: buf,
            pos: 0,
            threshold: threshold,
        }
    }

    fn flush_buf(&mut self) -> Result<()> {
        if self.pos != 0 {
            let ret = self.inner.write(&self.buf[..self.pos]);
            self.pos = 0;
            match ret {
                Ok(_) => Ok(()),
                Err(e) => Err(e),
            }
        } else {
            Ok(())
        }
    }

    fn possibly_flush(&mut self) {
        if self.pos > self.threshold {
            self.inner.write_all(&self.buf[..self.pos]).unwrap();
            self.pos = 0;
        }
    }
}

// This method was used in Write::write only. It must not be called in the correct cat code
// and is placed in the bottleneck place, so it is not used anymore in release build.
// In debug buld it is used to support bug finding
#[cfg(debug_assertions)]
#[inline(never)]
fn fail() -> ! {
    panic!("assertion failed");
}

impl<'a, W: Write> Write for UnsafeWriter<'a, W> {
    #[cfg(debug_assertions)]
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        let dst = &mut self.buf[self.pos..];
        let len = buf.len();
        if len > dst.len() {
            fail();
        }
        unsafe { copy_nonoverlapping(buf.as_ptr(), dst.as_mut_ptr(), len) }
        self.pos += len;
        Ok(len)
    }

    // a condition check excluded because it is false for current and every correct code
    // see fail method comment for more information
    #[cfg(not(debug_assertions))]
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        let len = buf.len();
        unsafe { copy_nonoverlapping(buf.as_ptr(), self.buf[self.pos..].as_mut_ptr(), len) }
        self.pos += len;
        Ok(len)
    }

    fn flush(&mut self) -> Result<()> {
        self.flush_buf().and_then(|()| self.inner.flush())
    }
}

impl<'a, W: Write> Drop for UnsafeWriter<'a, W> {
    fn drop(&mut self) {
        let _ = self.flush_buf();
    }
}
<|endoftext|>


<commit_before>//! If you use enough force, you can actually use this GC to implement a toy VM.

use builtins;
use cell_gc::{GcHeapSession, GcLeaf};
use cell_gc::collections::VecRef;
use compile::{self, Expr};
use parser;
use value::{BuiltinFnPtr, InternedString, Pair, Value};
use value::Value::*;

/// A potentially partially evaluated value.
pub enum Trampoline<'h> {
    /// A completely evaluated value.
    Value(Value<'h>),
    /// The continuation of a partial evaluation in tail position. The stack
    /// should be unwound before resumption of its evaluation.
    TailCall {
        func: Value<'h>,
        args: Vec<Value<'h>>,
    },
}

impl<'h> Trampoline<'h> {
    /// Complete the evaluation of this value. Avoids recursion to implement
    /// proper tail calls and keep from blowing the stack.
    pub fn eval(mut self, hs: &mut GcHeapSession<'h>) -> Result<Value<'h>, String> {
        while let Trampoline::TailCall { func, args } = self {
            self = apply(hs, func, args)?;
        }
        match self {
            Trampoline::Value(v) => Ok(v),
            Trampoline::TailCall { .. } => unreachable!(),
        }
    }
}

#[derive(Clone, Debug, PartialEq, IntoHeap)]
pub struct Environment<'h> {
    parent: Option<EnvironmentRef<'h>>,
    names: VecRef<'h, GcLeaf<InternedString>>,
    values: VecRef<'h, Value<'h>>,
}

impl<'h> Environment<'h> {
    pub fn default_env(hs: &mut GcHeapSession<'h>) -> EnvironmentRef<'h> {
        let env = Environment {
            parent: None,
            names: hs.alloc(vec![]),
            values: hs.alloc(vec![]),
        };

        macro_rules! builtin {
            ( $lisp_ident:expr , $func:expr ) => {
                env.names.push(GcLeaf::new(InternedString::get($lisp_ident)));
                env.values.push(Builtin(GcLeaf::new(BuiltinFnPtr($func))));
            }
        }

        builtin!("+", builtins::add);
        builtin!("-", builtins::sub);
        builtin!("*", builtins::mul);
        builtin!("=", builtins::numeric_eq);
        builtin!("<", builtins::numeric_lt);
        builtin!(">", builtins::numeric_gt);
        builtin!("<=", builtins::numeric_le);
        builtin!(">=", builtins::numeric_ge);
        builtin!("apply", builtins::apply);
        builtin!("assert", builtins::assert);
        builtin!("car", builtins::car);
        builtin!("cdr", builtins::cdr);
        builtin!("cons", builtins::cons);
        builtin!("eqv?", builtins::eqv_question);
        builtin!("eq?", builtins::eq_question);
        builtin!("print", builtins::print);
        builtin!("boolean?", builtins::boolean_question);
        builtin!("null?", builtins::null_question);
        builtin!("pair?", builtins::pair_question);
        builtin!("procedure?", builtins::procedure_question);
        builtin!("vector?", builtins::vector_question);
        builtin!("vector", builtins::vector);
        builtin!("vector-length", builtins::vector_length);
        builtin!("vector-ref", builtins::vector_ref);

        const PRELUDE: &'static str = include_str!("prelude.sch");
        let prelude = match parser::parse(hs, PRELUDE) {
            Ok(forms) => forms,
            Err(err) => panic!("unexpected error parsing prelude: {}", err),
        };

        let env = hs.alloc(env);
        for expr in prelude {
            if let Err(err) = eval(hs, expr, env.clone()) {
                panic!("unexpected error evaluating prelude: {}", err);
            }
        }
        env
    }
}

impl<'h> EnvironmentRef<'h> {
    pub fn push(&self, key: InternedString, value: Value<'h>) {
        self.names().push(GcLeaf::new(key));
        self.values().push(value);
    }

    pub fn lookup(&self, name: InternedString) -> Result<(EnvironmentRef<'h>, usize), String> {
        let mut next = Some(self.clone());
        while let Some(env) = next {
            let names = env.names();
            for i in (0..names.len()).rev() {
                if name == *names.get(i) {
                    return Ok((env, i));
                }
            }
            next = env.parent();
        }
        Err(format!("undefined symbol: {:?}", name.as_str()))
    }

    pub fn get(&self, name: InternedString) -> Result<Value<'h>, String> {
        let (env, i) = self.lookup(name)?;
        Ok(env.values().get(i))
    }

    pub fn set(&self, name: InternedString, value: Value<'h>) -> Result<(), String> {
        let (env, i) = self.lookup(name)?;
        env.values().set(i, value);
        Ok(())
    }
}

#[macro_export]
macro_rules! lisp {
    { ( ) , $_hs:expr } => {
        Nil
    };
    { ( $h:tt $($t:tt)* ) , $hs:expr } => {
        {
            let h = lisp!($h, $hs);
            let t = lisp!(($($t)*), $hs);
            Cons($hs.alloc(Pair { car: h, cdr: t }))
        }
    };
    { $s:tt , $_hs:expr } => {
        {
            let s = stringify!($s);  // lame, but nothing else matches after an `ident` match fails
            if s.starts_with(|c: char| c.is_digit(10)) {
                Int(s.parse().expect("invalid numeric literal in `lisp!`"))
            } else {
                Symbol($crate::cell_gc::GcLeaf::new($crate::value::InternedString::get(s)))
            }
        }
    };
}

pub fn apply<'h>(
    hs: &mut GcHeapSession<'h>,
    fval: Value<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<Trampoline<'h>, String> {
    match fval {
        Builtin(f) => (f.0)(hs, args),
        Lambda(pair) => {
            let code = match pair.car() {
                Code(code) => code,
                _ => panic!("internal error: bad lambda"),
            };
            let parent = Some(match pair.cdr() {
                Environment(pe) => pe,
                _ => panic!("internal error: bad lambda"),
            });
            let names = hs.alloc(Vec::<GcLeaf<InternedString>>::new());
            let values = hs.alloc(Vec::<Value<'h>>::new());
            let env = hs.alloc(Environment {
                parent,
                names,
                values,
            });

            let params = code.params();
            let n = params.len();
            if args.len() < n {
                return Err("apply: not enough arguments".to_string());
            }
            for i in 0..n {
                env.push(params.get(i).unwrap().clone().unwrap(), args[i].clone());
            }
            if let Some(rest_name) = code.rest() {
                let mut rest_list = Nil;
                for v in args.drain(n..).rev() {
                    rest_list = Cons(hs.alloc(Pair {
                        car: v,
                        cdr: rest_list,
                    }));
                }
                env.push(rest_name.unwrap(), rest_list);
            } else if n < args.len() {
                return Err("apply: too many arguments".to_string());
            }

            eval_to_tail_call(hs, code.body(), env)
        }
        _ => Err("apply: not a function".to_string()),
    }
}

/// Evaluate `expr` until we reach a tail call, at which point it is packaged up
/// as a `Trampoline::TailCall` and returned so we can unwind the stack before
/// continuing evaluation.
fn eval_to_tail_call<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Expr<'h>,
    env: EnvironmentRef<'h>,
) -> Result<Trampoline<'h>, String> {
    match expr {
        Expr::Con(k) =>
            Ok(Trampoline::Value(k)),
        Expr::Var(s) =>
            Ok(Trampoline::Value(env.get(s.unwrap())?)),
        Expr::Fun(code) =>
            Ok(Trampoline::Value(Lambda(hs.alloc(Pair {
                car: Value::Code(code),
                cdr: Value::Environment(env.clone()),
            })))),
        Expr::App(subexprs) => {
            let func = eval_compiled(hs, subexprs.get(0), env.clone())?;
            let args: Vec<Value<'h>> =
                (1..subexprs.len())
                .map(|i| eval_compiled(hs, subexprs.get(i), env.clone()))
                .collect::<Result<Vec<Value<'h>>, String>>()?;
            Ok(Trampoline::TailCall { func, args })
        }
        Expr::Seq(exprs) => {
            let len = exprs.len();
            if len == 0 {
                Ok(Trampoline::Value(Nil))
            } else {
                for i in 0..(len - 1) {
                    eval_compiled(hs, exprs.get(i), env.clone())?;
                }
                eval_to_tail_call(hs, exprs.get(len - 1), env)
            }
        }
        Expr::If(if_parts) => {
            let cond_value = eval_compiled(hs, if_parts.cond(), env.clone())?;
            let selected_expr =
                if cond_value.to_bool() {
                    if_parts.t_expr()
                } else {
                    if_parts.f_expr()
                };
            eval_to_tail_call(hs, selected_expr, env)
        }
        Expr::Def(def) => {
            let val = eval_compiled(hs, def.value(), env.clone())?;
            env.push(def.name().unwrap(), val);
            Ok(Trampoline::Value(Nil))
        }
        Expr::Set(def) => {
            let val = eval_compiled(hs, def.value(), env.clone())?;
            env.set(def.name().unwrap(), val)?;
            Ok(Trampoline::Value(Nil))
        }
    }
}

pub fn eval_compiled<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Expr<'h>,
    env: EnvironmentRef<'h>,
) -> Result<Value<'h>, String> {
    let tail = eval_to_tail_call(hs, expr, env)?;
    tail.eval(hs)
}

pub fn eval<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Value<'h>,
    env: EnvironmentRef<'h>,
) -> Result<Value<'h>, String> {
    compile::compile(hs, expr).and_then(|expr| eval_compiled(hs, expr, env))
}

#[cfg(test)]
include!("tests.rs");
<commit_msg>lisp: Style nit: use `?` instead of `and_then`.<commit_after>//! If you use enough force, you can actually use this GC to implement a toy VM.

use builtins;
use cell_gc::{GcHeapSession, GcLeaf};
use cell_gc::collections::VecRef;
use compile::{self, Expr};
use parser;
use value::{BuiltinFnPtr, InternedString, Pair, Value};
use value::Value::*;

/// A potentially partially evaluated value.
pub enum Trampoline<'h> {
    /// A completely evaluated value.
    Value(Value<'h>),
    /// The continuation of a partial evaluation in tail position. The stack
    /// should be unwound before resumption of its evaluation.
    TailCall {
        func: Value<'h>,
        args: Vec<Value<'h>>,
    },
}

impl<'h> Trampoline<'h> {
    /// Complete the evaluation of this value. Avoids recursion to implement
    /// proper tail calls and keep from blowing the stack.
    pub fn eval(mut self, hs: &mut GcHeapSession<'h>) -> Result<Value<'h>, String> {
        while let Trampoline::TailCall { func, args } = self {
            self = apply(hs, func, args)?;
        }
        match self {
            Trampoline::Value(v) => Ok(v),
            Trampoline::TailCall { .. } => unreachable!(),
        }
    }
}

#[derive(Clone, Debug, PartialEq, IntoHeap)]
pub struct Environment<'h> {
    parent: Option<EnvironmentRef<'h>>,
    names: VecRef<'h, GcLeaf<InternedString>>,
    values: VecRef<'h, Value<'h>>,
}

impl<'h> Environment<'h> {
    pub fn default_env(hs: &mut GcHeapSession<'h>) -> EnvironmentRef<'h> {
        let env = Environment {
            parent: None,
            names: hs.alloc(vec![]),
            values: hs.alloc(vec![]),
        };

        macro_rules! builtin {
            ( $lisp_ident:expr , $func:expr ) => {
                env.names.push(GcLeaf::new(InternedString::get($lisp_ident)));
                env.values.push(Builtin(GcLeaf::new(BuiltinFnPtr($func))));
            }
        }

        builtin!("+", builtins::add);
        builtin!("-", builtins::sub);
        builtin!("*", builtins::mul);
        builtin!("=", builtins::numeric_eq);
        builtin!("<", builtins::numeric_lt);
        builtin!(">", builtins::numeric_gt);
        builtin!("<=", builtins::numeric_le);
        builtin!(">=", builtins::numeric_ge);
        builtin!("apply", builtins::apply);
        builtin!("assert", builtins::assert);
        builtin!("car", builtins::car);
        builtin!("cdr", builtins::cdr);
        builtin!("cons", builtins::cons);
        builtin!("eqv?", builtins::eqv_question);
        builtin!("eq?", builtins::eq_question);
        builtin!("print", builtins::print);
        builtin!("boolean?", builtins::boolean_question);
        builtin!("null?", builtins::null_question);
        builtin!("pair?", builtins::pair_question);
        builtin!("procedure?", builtins::procedure_question);
        builtin!("vector?", builtins::vector_question);
        builtin!("vector", builtins::vector);
        builtin!("vector-length", builtins::vector_length);
        builtin!("vector-ref", builtins::vector_ref);

        const PRELUDE: &'static str = include_str!("prelude.sch");
        let prelude = match parser::parse(hs, PRELUDE) {
            Ok(forms) => forms,
            Err(err) => panic!("unexpected error parsing prelude: {}", err),
        };

        let env = hs.alloc(env);
        for expr in prelude {
            if let Err(err) = eval(hs, expr, env.clone()) {
                panic!("unexpected error evaluating prelude: {}", err);
            }
        }
        env
    }
}

impl<'h> EnvironmentRef<'h> {
    pub fn push(&self, key: InternedString, value: Value<'h>) {
        self.names().push(GcLeaf::new(key));
        self.values().push(value);
    }

    pub fn lookup(&self, name: InternedString) -> Result<(EnvironmentRef<'h>, usize), String> {
        let mut next = Some(self.clone());
        while let Some(env) = next {
            let names = env.names();
            for i in (0..names.len()).rev() {
                if name == *names.get(i) {
                    return Ok((env, i));
                }
            }
            next = env.parent();
        }
        Err(format!("undefined symbol: {:?}", name.as_str()))
    }

    pub fn get(&self, name: InternedString) -> Result<Value<'h>, String> {
        let (env, i) = self.lookup(name)?;
        Ok(env.values().get(i))
    }

    pub fn set(&self, name: InternedString, value: Value<'h>) -> Result<(), String> {
        let (env, i) = self.lookup(name)?;
        env.values().set(i, value);
        Ok(())
    }
}

#[macro_export]
macro_rules! lisp {
    { ( ) , $_hs:expr } => {
        Nil
    };
    { ( $h:tt $($t:tt)* ) , $hs:expr } => {
        {
            let h = lisp!($h, $hs);
            let t = lisp!(($($t)*), $hs);
            Cons($hs.alloc(Pair { car: h, cdr: t }))
        }
    };
    { $s:tt , $_hs:expr } => {
        {
            let s = stringify!($s);  // lame, but nothing else matches after an `ident` match fails
            if s.starts_with(|c: char| c.is_digit(10)) {
                Int(s.parse().expect("invalid numeric literal in `lisp!`"))
            } else {
                Symbol($crate::cell_gc::GcLeaf::new($crate::value::InternedString::get(s)))
            }
        }
    };
}

pub fn apply<'h>(
    hs: &mut GcHeapSession<'h>,
    fval: Value<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<Trampoline<'h>, String> {
    match fval {
        Builtin(f) => (f.0)(hs, args),
        Lambda(pair) => {
            let code = match pair.car() {
                Code(code) => code,
                _ => panic!("internal error: bad lambda"),
            };
            let parent = Some(match pair.cdr() {
                Environment(pe) => pe,
                _ => panic!("internal error: bad lambda"),
            });
            let names = hs.alloc(Vec::<GcLeaf<InternedString>>::new());
            let values = hs.alloc(Vec::<Value<'h>>::new());
            let env = hs.alloc(Environment {
                parent,
                names,
                values,
            });

            let params = code.params();
            let n = params.len();
            if args.len() < n {
                return Err("apply: not enough arguments".to_string());
            }
            for i in 0..n {
                env.push(params.get(i).unwrap().clone().unwrap(), args[i].clone());
            }
            if let Some(rest_name) = code.rest() {
                let mut rest_list = Nil;
                for v in args.drain(n..).rev() {
                    rest_list = Cons(hs.alloc(Pair {
                        car: v,
                        cdr: rest_list,
                    }));
                }
                env.push(rest_name.unwrap(), rest_list);
            } else if n < args.len() {
                return Err("apply: too many arguments".to_string());
            }

            eval_to_tail_call(hs, code.body(), env)
        }
        _ => Err("apply: not a function".to_string()),
    }
}

/// Evaluate `expr` until we reach a tail call, at which point it is packaged up
/// as a `Trampoline::TailCall` and returned so we can unwind the stack before
/// continuing evaluation.
fn eval_to_tail_call<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Expr<'h>,
    env: EnvironmentRef<'h>,
) -> Result<Trampoline<'h>, String> {
    match expr {
        Expr::Con(k) =>
            Ok(Trampoline::Value(k)),
        Expr::Var(s) =>
            Ok(Trampoline::Value(env.get(s.unwrap())?)),
        Expr::Fun(code) =>
            Ok(Trampoline::Value(Lambda(hs.alloc(Pair {
                car: Value::Code(code),
                cdr: Value::Environment(env.clone()),
            })))),
        Expr::App(subexprs) => {
            let func = eval_compiled(hs, subexprs.get(0), env.clone())?;
            let args: Vec<Value<'h>> =
                (1..subexprs.len())
                .map(|i| eval_compiled(hs, subexprs.get(i), env.clone()))
                .collect::<Result<Vec<Value<'h>>, String>>()?;
            Ok(Trampoline::TailCall { func, args })
        }
        Expr::Seq(exprs) => {
            let len = exprs.len();
            if len == 0 {
                Ok(Trampoline::Value(Nil))
            } else {
                for i in 0..(len - 1) {
                    eval_compiled(hs, exprs.get(i), env.clone())?;
                }
                eval_to_tail_call(hs, exprs.get(len - 1), env)
            }
        }
        Expr::If(if_parts) => {
            let cond_value = eval_compiled(hs, if_parts.cond(), env.clone())?;
            let selected_expr =
                if cond_value.to_bool() {
                    if_parts.t_expr()
                } else {
                    if_parts.f_expr()
                };
            eval_to_tail_call(hs, selected_expr, env)
        }
        Expr::Def(def) => {
            let val = eval_compiled(hs, def.value(), env.clone())?;
            env.push(def.name().unwrap(), val);
            Ok(Trampoline::Value(Nil))
        }
        Expr::Set(def) => {
            let val = eval_compiled(hs, def.value(), env.clone())?;
            env.set(def.name().unwrap(), val)?;
            Ok(Trampoline::Value(Nil))
        }
    }
}

pub fn eval_compiled<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Expr<'h>,
    env: EnvironmentRef<'h>,
) -> Result<Value<'h>, String> {
    let tail = eval_to_tail_call(hs, expr, env)?;
    tail.eval(hs)
}

pub fn eval<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Value<'h>,
    env: EnvironmentRef<'h>,
) -> Result<Value<'h>, String> {
    let expr = compile::compile(hs, expr)?;
    eval_compiled(hs, expr, env)
}

#[cfg(test)]
include!("tests.rs");
<|endoftext|>