1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
extern crate pest;
#[macro_use]
extern crate pest_derive;

#[derive(Parser)]
#[grammar = "scheme.pest"]
struct SchemeParser;
use pest::{Parser, RuleType};
use pest::iterators::{Pairs, Pair};
use pest::inputs::{Input,StringInput};

#[cfg(test)]
mod tests;

/// Value represents all the things that are elements of the list expression
#[derive(Debug)]
pub enum Value{
    /// A linked list
    List(Cons),

    /// Symbol, eg. (if this than that), the word "if" is a symbol as well as "this", "than" and
    /// "that"
    Symbol(String),

    ///  A number
    Number(usize),

    /// A boolean (true or false) 
    Bool(bool),

    /// A literal which is not futher parsed
    Literal(String),

    /// The end of a linked list 
    Null
}

impl Value{
    /// Gives the head of the list if value is a list
    pub fn car(self) -> Box<Value>{
        match self{
           Value::List(c) => c.car,
            _ => panic!("Not a pair")
        }
    }

    /// Gives the tail of the list if value is a list
    pub fn cdr(self) -> Box<Value>{
        match self{
            Value::List(c) => c.cdr,
            _ => panic!("Not a pair")
        }
    }

    /// Gives true if the current value is a symbol false if not
    pub fn is_symbol(&self) -> bool{
        match self{
            &Value::Symbol(_) => true,
            _ => false
        }
    }

    /// Gives true if the current value is a number false if not
    pub fn is_number(&self) -> bool{
        match self{
            &Value::Number(_) => true,
            _ => false
        }
    }

    /// If the current value is a boolean, gives the value of that boolean
    /// otherwise false is returned
    pub fn is_true(&self) -> bool{
        match self{
            &Value::Bool(val) => val,
            _ => false
        }
    }

    /// Oposite of is_true()
    pub fn is_false(&self) -> bool{
        !self.is_true()
    }

    /// Gives the symbol stored in the Value container
    pub fn unwrap_symbol(self) -> String{
        match self{
            Value::Symbol(symb) => symb,
            _ => panic!("value is not a symbol")
        }
    }

    /// Gives the number stored in the Value container
    pub fn unwrap_number(self) -> usize{
        match self{
            Value::Number(n) => n,
            _ => panic!("value is not a number")
        }
    }


    /// Gives the literal contained in Value
    pub fn unwrap_literal(self) -> String{
        match self{
            Value::Literal(lit) => lit,
            _ => panic!("value is not a literal")
        }
    }
}

/// A linked list
#[derive(Debug)]
pub struct Cons{
    car: Box<Value>, // head
    cdr: Box<Value> // tail
}   

/// Some extra methods that can be used on a pest::iterators::Pair 
trait PairMixin{
    /// Give the string value of the current expression
    fn string_value(self) -> String;

    /// Give the integer value of the current expression
    /// will panic if the current expression is not something that can be converted to a integer
    fn usize_value(self) -> usize;
}

impl<R: RuleType,I: Input> PairMixin for Pair<R,I>{
    fn string_value(self) -> String{
        self.into_span().as_str().to_string()
    }

    fn usize_value(self) -> usize{
        self.into_span().as_str().to_string().parse().unwrap()
    }
}

trait PairsMixin<R: RuleType, I: Input>{
    fn first_pair(self) -> Option<Pair<R,I>>;
}

impl<R: RuleType, I: Input> PairsMixin<R,I> for Pairs<R,I>{
    fn first_pair(mut self) -> Option<Pair<R,I>>{
        self.next()
    }
}

/// This function will return a abstract syntax tree represented as a deep nested linked list
/// for given s-expression syntax. 
///
/// If some syntax error was in the input the program calling this function will panic.
///
/// Example:
///
/// ```
/// use sparser::parse;
///
/// let tree = parse("(define (fac n) (if (= n 0) 1 (* (fac (- n 1)))))");
/// assert_eq!(tree.car().unwrap_symbol(), "define")
/// ```
pub fn parse(input: &'static str) -> Value{
    let pairs = SchemeParser::parse_str(Rule::scheme, input).expect("Parsed output");
    make_ast(pairs.first_pair().unwrap().into_inner().first_pair().unwrap())
}

fn make_ast(pair: Pair<Rule, StringInput>) -> Value{
    match pair.as_rule(){
            Rule::list => make_list(pair.into_inner()),
            Rule::symbol => Value::Symbol(pair.string_value()),
            Rule::number => Value::Number(pair.usize_value()),
            Rule::true_lit => Value::Bool(true),
            Rule::false_lit => Value::Bool(false), 
            Rule::literal => Value::Literal(pair.string_value()),
            _ => panic!("Not covered expression {:?}. Please implement.", pair.as_rule())
    }
}

fn make_list(mut pairs: Pairs<Rule, StringInput>) -> Value{
    let element = pairs.next();
    match element{
        Some(el) => Value::List(Cons{car: Box::new(make_ast(el)),
                                cdr: Box::new(make_list(pairs))}),
        None => Value::Null
    }
}