├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── cli └── main.rs └── src ├── builtin.rs ├── lang ├── channel.rs ├── context.rs ├── filter.rs ├── mod.rs ├── parser.rs └── value │ ├── array.rs │ ├── mod.rs │ └── object.rs ├── lib.rs └── util.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.rlib 2 | *~ 3 | target 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | notifications: 3 | irc: "chat.freenode.net#jqsh" 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | To contribute to jqsh, please open issues and/or pull requests. 2 | 3 | ~~The approval process is as follows:~~ 4 | 5 | * ~~Pull requests sent by a core member must be approved by another core member.~~ 6 | * ~~Pull requests sent by anyone else must be approved by 2 core members.~~ 7 | 8 | (Since there is currently only one core member, the above is temporarily ineffective. I guess I'll just review all the issues/PRs myself and hope for contributors to eventually show up.) 9 | 10 | The core member is: 11 | 12 | * [@fenhl](https://github.com/fenhl) 13 | 14 | This list is subject to change if and when the project starts to attract additional regular contributors. 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jqsh" 3 | version = "0.0.1" 4 | authors = [ 5 | "madmalik ", 6 | "Fenhl " 7 | ] 8 | 9 | [[bin]] 10 | name = "jqsh" 11 | path = "cli/main.rs" 12 | doc = false 13 | test = false 14 | bench = false 15 | 16 | [lib] 17 | name = "jqsh" 18 | path = "src/lib.rs" 19 | 20 | [dependencies] 21 | chan = "*" 22 | eventual = "*" 23 | itertools = "*" 24 | num = "*" 25 | 26 | [dependencies.readline] 27 | git = "https://github.com/shaleh/rust-readline.git" 28 | 29 | [dependencies.unicode] 30 | git = "https://github.com/fenhl/rust-unicode.git" 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Matthias Tellen ("MadMalik") and Max Dominik Weber ("Fenhl") 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **jqsh** is a shell with a scripting language based on [jq](http://stedolan.github.io/jq/) and [sh](https://en.wikipedia.org/wiki/Bourne_shell). 2 | 3 | With a [prototype](https://github.com/jq-shell/python-jqsh) having been written in Python, this project aims to become the full implementation of the shell in [Rust](http://www.rust-lang.org/). You can run it using `cargo run` (`cargo` comes with Rust). To exit the loop, press control-D. 4 | 5 | This is version 0.0.1 of jqsh ([semver](http://semver.org/)). 6 | 7 | For more info on the project, see [the wiki](https://github.com/jq-shell/jqsh/wiki), or join us at [functionalprogramming.slack.com](http://fpchat.com/) #jqsh. 8 | -------------------------------------------------------------------------------- /cli/main.rs: -------------------------------------------------------------------------------- 1 | extern crate eventual; 2 | extern crate jqsh; 3 | extern crate readline; 4 | extern crate unicode; 5 | 6 | use eventual::Async; 7 | 8 | use unicode::UString; 9 | 10 | use jqsh::builtin; 11 | use jqsh::lang::{Filter, channel, parser}; 12 | 13 | fn main() { 14 | let mut repl_context = builtin::context(); 15 | while let Some(source_utf8) = readline::readline("jqsh> ") { 16 | readline::add_history(&source_utf8); 17 | let source = UString::from(source_utf8); 18 | let filter = parser::parse(source, repl_context.clone()).unwrap_or_else(|err| { 19 | println!("jqsh: syntax error: {:?}", err); 20 | Filter::Empty 21 | }); 22 | let channel::Receiver { context, values } = channel::Receiver::empty(repl_context).filter(&filter); 23 | repl_context = context.await().expect("failed to get repl output context"); 24 | for value in values { 25 | println!("{}", value); 26 | } 27 | } 28 | println!(""); 29 | } 30 | -------------------------------------------------------------------------------- /src/builtin.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use num::{FromPrimitive, BigRational}; 4 | 5 | use lang::context::{Context, PrecedenceGroup}; 6 | 7 | /// The default context for interactive shell sessions. 8 | pub fn context() -> Context { 9 | Context { 10 | filter_allowed: Arc::new(Box::new(|_| true)), 11 | operators: vec![ 12 | (1_000_000, PrecedenceGroup::Circumfix), 13 | (-1_000_000, PrecedenceGroup::AndThen) 14 | ].into_iter().map(|(precedence, group)| { 15 | (BigRational::from_integer(FromPrimitive::from_i32(precedence).unwrap()), group) 16 | }).collect() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lang/channel.rs: -------------------------------------------------------------------------------- 1 | use std::{mem, thread}; 2 | 3 | use chan; 4 | 5 | use eventual::{self, Async}; 6 | 7 | use lang::{Context, Filter, Value}; 8 | 9 | pub struct Sender { 10 | pub context: eventual::Complete, 11 | pub values: chan::Sender 12 | //TODO namespaces 13 | } 14 | 15 | pub struct Receiver { 16 | pub context: eventual::Future, 17 | pub values: chan::Receiver 18 | //TODO namespaces 19 | } 20 | 21 | impl Receiver { 22 | /// A closed receiver with no values. 23 | pub fn empty(context: Context) -> Receiver { 24 | let (_, val_rx) = chan::async(); 25 | Receiver { 26 | context: eventual::Future::of(context), 27 | values: val_rx 28 | } 29 | } 30 | 31 | /// Takes the receiving end of a channel, asynchronously runs it through a filter, and returns the output channel. 32 | pub fn filter(self, f: &Filter) -> Receiver { 33 | let (tx, rx) = channel(); 34 | let f = f.clone(); 35 | thread::spawn(move || f.run(self, tx)); 36 | rx 37 | } 38 | 39 | /// Same as `filter` but waits until the filter function returns. 40 | pub fn filter_sync(self, f: &Filter) -> Receiver { 41 | let (tx, rx) = channel(); 42 | f.run(self, tx); 43 | rx 44 | } 45 | 46 | /// Asynchronously forwards all received values to `dst` and closes `self`'s value channel. 47 | /// 48 | /// Returns `dst`'s context future sender. 49 | pub fn forward_values(&mut self, dst: Sender) -> eventual::Complete { 50 | let (_, val_rx) = chan::async(); 51 | let vals_to_forward = mem::replace(&mut self.values, val_rx); 52 | let Sender { context, values } = dst; 53 | thread::spawn(move || { 54 | for val in vals_to_forward { 55 | values.send(val); 56 | } 57 | }); 58 | context 59 | } 60 | 61 | /// Split `self` into two new receivers, forwarding everything to both. 62 | pub fn split(self) -> (Receiver, Receiver) { 63 | let (Sender { context: ctxt_tx1, values: val_tx1 }, rx1) = channel(); 64 | let (Sender { context: ctxt_tx2, values: val_tx2 }, rx2) = channel(); 65 | let Receiver { context, values } = self; 66 | thread::spawn(move || { 67 | let context = context.await().expect("failed to split contexts"); 68 | ctxt_tx1.complete(context.clone()); 69 | ctxt_tx2.complete(context); 70 | }); 71 | thread::spawn(move || { 72 | for val in values { 73 | val_tx1.send(val.clone()); 74 | val_tx2.send(val); 75 | } 76 | }); 77 | (rx1, rx2) 78 | } 79 | } 80 | 81 | impl IntoIterator for Receiver { 82 | type Item = Value; 83 | type IntoIter = chan::Iter; 84 | 85 | fn into_iter(self) -> chan::Iter { 86 | self.values.into_iter() 87 | } 88 | } 89 | 90 | pub fn channel() -> (Sender, Receiver) { 91 | let (ctxt_tx, ctxt_fut) = eventual::Future::pair(); 92 | let (val_tx, val_rx) = chan::async(); 93 | let tx = Sender { 94 | context: ctxt_tx, 95 | values: val_tx 96 | }; 97 | let rx = Receiver { 98 | context: ctxt_fut, 99 | values: val_rx 100 | }; 101 | (tx, rx) 102 | } 103 | -------------------------------------------------------------------------------- /src/lang/context.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::fmt; 3 | use std::sync::Arc; 4 | 5 | use num::BigRational; 6 | 7 | use lang::Filter; 8 | 9 | #[derive(Clone, Debug)] 10 | pub enum PrecedenceGroup { 11 | AndThen, 12 | Circumfix 13 | } 14 | 15 | #[derive(Clone)] 16 | pub struct Context { 17 | /// A function called each time the parser constructs a new filter anywhere in the syntax tree. If it returns false, the filter is replaced with one that generates an exception. 18 | pub filter_allowed: Arc bool + Send + Sync>>, 19 | /// The context's operators, in decreasing precedence. 20 | pub operators: BTreeMap 21 | } 22 | 23 | impl fmt::Debug for Context { 24 | fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { 25 | write!(w, "Context {{ filter_allowed: [Fn(&Filter) -> bool], operators: {:?} }}", self.operators) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lang/filter.rs: -------------------------------------------------------------------------------- 1 | use unicode::UString; 2 | 3 | use eventual::Async; 4 | 5 | use lang::parser::{self, Code}; 6 | use lang::value::{Value, Object}; 7 | use lang::channel::{Sender, Receiver, channel}; 8 | use util::FilterFn; 9 | 10 | #[derive(Clone, Debug)] 11 | pub enum Filter { 12 | AndThen { 13 | lhs: Box, 14 | remaining_code: Code 15 | }, 16 | Custom { 17 | attributes: Vec, 18 | run: Box 19 | }, 20 | Empty 21 | } 22 | 23 | impl Filter { 24 | pub fn run(&self, input: Receiver, output: Sender) { 25 | use self::Filter::*; 26 | 27 | match *self { 28 | AndThen { ref lhs, ref remaining_code } => { 29 | // synchronously run the left-hand filter 30 | let (lhs_input, mut input) = input.split(); 31 | let Receiver { context: lhs_ctxt, values: _ } = lhs_input.filter_sync(&lhs); // the values output by lhs are discarded 32 | // parse the right-hand filter using the lhs output context 33 | let lhs_ctxt = lhs_ctxt.await().expect("failed to get context of `;;` left operand"); 34 | let rhs = match parser::parse(remaining_code.clone(), lhs_ctxt.clone()) { 35 | Ok(f) => f, 36 | Err(_) => { 37 | let Sender { context, values } = output; 38 | context.complete(lhs_ctxt); 39 | values.send(Value::Exception(UString::from("syntax"), Object::default())); //TODO more useful metadata based on the error contents 40 | return; 41 | } 42 | }; 43 | // synchronously run the right-hand filter 44 | let (rhs_in_tx, rhs_in_rx) = channel(); 45 | let rhs_in_ctxt = input.forward_values(rhs_in_tx); // rhs receives its values from the `;;` filter's input... 46 | rhs_in_ctxt.complete(lhs_ctxt); // ...and its context from the output of lhs. 47 | rhs.run(rhs_in_rx, output); // finally, rhs is run synchronously, with output directly into the `;;` filter's output. 48 | } 49 | Custom { ref attributes, ref run } => { 50 | run(attributes, input, output) 51 | } 52 | Empty => { 53 | let Receiver { context: in_ctxt, values: _ } = input; 54 | let Sender { context, values: _ } = output; 55 | context.complete(in_ctxt.await().expect("failed to get input context")); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/lang/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channel; 2 | pub mod context; 3 | pub mod filter; 4 | pub mod parser; 5 | pub mod value; 6 | 7 | pub use self::context::Context; 8 | pub use self::filter::Filter; 9 | pub use self::value::Value; 10 | -------------------------------------------------------------------------------- /src/lang/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, mem}; 2 | use std::sync::{Arc, Mutex}; 3 | 4 | use itertools::{Itertools, MultiPeek}; 5 | 6 | use unicode::{self, UString}; 7 | 8 | use lang::{Context, Filter}; 9 | use lang::context::PrecedenceGroup; 10 | use util::Labeled; 11 | 12 | #[derive(Debug)] 13 | pub enum ParseError { 14 | InvalidToken(char), 15 | MismatchedParens(Token, Tf), 16 | NotAllowed(Filter), 17 | NotFullyParsed(Vec), 18 | UnbalancedParen(Token) 19 | } 20 | 21 | #[derive(Debug)] 22 | pub enum Token { 23 | /// An unrecognized character 24 | Invalid(char), 25 | /// An opening parenthesis `(` 26 | OpenParen, 27 | /// A closing parenthesis `)` 28 | CloseParen, 29 | /// The sequential execution operator `;;`, and all following code 30 | AndThen(Code), 31 | /// A sequence of one or more whitespace characters 32 | Whitespace 33 | } 34 | 35 | //#[derive(Debug)] // https://github.com/bluss/rust-itertools/issues/32 36 | enum CodeVariant { 37 | Empty, 38 | UString { s: UString, peek_index: usize }, 39 | UStringIter(MultiPeek), 40 | Mutation 41 | } 42 | 43 | impl fmt::Debug for CodeVariant { // https://github.com/bluss/rust-itertools/issues/32 44 | fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { 45 | match *self { 46 | CodeVariant::Empty => try!(write!(w, "CodeVariant::Empty")), 47 | CodeVariant::UString { ref s, ref peek_index } => try!(write!(w, "CodeVariant::UString {{ s: {:?}, peek_index: {:?} }}", s, peek_index)), 48 | CodeVariant::UStringIter(_) => try!(write!(w, "CodeVariant::UStringIter(/* ... */)")), 49 | CodeVariant::Mutation => try!(write!(w, "CodeVariant::Mutation")) 50 | } 51 | Ok(()) 52 | } 53 | } 54 | 55 | #[derive(Debug)] 56 | pub struct Code(Mutex); 57 | 58 | impl Code { 59 | fn peek(&mut self) -> Option { 60 | let mut lock = self.0.lock().unwrap(); 61 | match *&mut *lock { 62 | CodeVariant::Empty => None, 63 | CodeVariant::UString { ref s, ref mut peek_index } => { 64 | if *peek_index < s.len() { 65 | *peek_index += 1; 66 | Some(s[*peek_index - 1]) 67 | } else { 68 | None 69 | } 70 | } 71 | CodeVariant::UStringIter(ref mut it) => { 72 | it.peek().map(|&c| c) 73 | } 74 | CodeVariant::Mutation => panic!("code mutex has been emptied") 75 | } 76 | } 77 | } 78 | 79 | impl Default for Code { 80 | fn default() -> Code { 81 | Code(Mutex::new(CodeVariant::Empty)) 82 | } 83 | } 84 | 85 | impl> From for Code { 86 | fn from(code_string: T) -> Code { 87 | Code(Mutex::new(CodeVariant::UString { s: code_string.into(), peek_index: 0 })) 88 | } 89 | } 90 | 91 | impl Clone for Code { 92 | fn clone(&self) -> Code { 93 | let mut lock = self.0.lock().unwrap(); 94 | match *&mut *lock { 95 | CodeVariant::Empty => Code(Mutex::new(CodeVariant::Empty)), 96 | CodeVariant::UString { ref s, .. } => Code(Mutex::new(CodeVariant::UString { s: s.clone(), peek_index: 0 })), 97 | ref mut code_variant @ CodeVariant::UStringIter(_) => { 98 | if let CodeVariant::UStringIter(it) = mem::replace(code_variant, CodeVariant::Mutation) { 99 | let s = it.collect::(); 100 | *code_variant = CodeVariant::UString { s: s.clone(), peek_index: 0 }; 101 | Code(Mutex::new(CodeVariant::UString { s: s, peek_index: 0 })) 102 | } else { 103 | unreachable!() 104 | } 105 | } 106 | CodeVariant::Mutation => panic!("code mutex has been emptied") 107 | } 108 | } 109 | } 110 | 111 | impl Iterator for Code { 112 | type Item = char; 113 | 114 | fn next(&mut self) -> Option { 115 | let mut lock = self.0.lock().unwrap(); 116 | match *&mut *lock { 117 | CodeVariant::Empty => None, 118 | ref mut code_variant @ CodeVariant::UString { .. } => { 119 | if let CodeVariant::UString { s, .. } = mem::replace(code_variant, CodeVariant::Mutation) { 120 | let mut iter = s.into_iter().multipeek(); 121 | let result = iter.next(); 122 | *code_variant = CodeVariant::UStringIter(iter); 123 | result 124 | } else { 125 | unreachable!() 126 | } 127 | } 128 | CodeVariant::UStringIter(ref mut iter) => iter.next(), 129 | CodeVariant::Mutation => panic!("code mutex has been emptied") 130 | } 131 | } 132 | } 133 | 134 | struct Tokens { 135 | code: Code, 136 | // context: Context 137 | } 138 | 139 | impl Tokens { 140 | fn new>(code: T, _: Context) -> Tokens { 141 | Tokens { 142 | code: code.into(), 143 | // context: context 144 | } 145 | } 146 | } 147 | 148 | impl Iterator for Tokens { 149 | type Item = Token; 150 | 151 | fn next(&mut self) -> Option { 152 | use self::Token::*; 153 | 154 | match self.code.next() { 155 | Some('\t') | 156 | Some('\n') | 157 | Some('\r') | 158 | Some(' ') => Some(Whitespace), 159 | Some('#') => { 160 | while self.code.peek().map(|c| c != '\n').unwrap_or(false) { 161 | self.code.next(); // discard comment contents 162 | } 163 | Some(Whitespace) // comments are treated as whitespace 164 | } 165 | Some('(') => Some(OpenParen), 166 | Some(')') => Some(CloseParen), 167 | Some(';') => { 168 | if self.code.peek() == Some(';') { 169 | self.code.next(); // discard the second semicolon 170 | Some(AndThen(mem::replace(&mut self.code, Code::default()))) 171 | } else { 172 | Some(Invalid(';')) 173 | } 174 | } 175 | Some(c) => Some(Invalid(c)), 176 | None => None 177 | } 178 | } 179 | } 180 | 181 | /// A token or filter, used by the in-place parsing algorithm. 182 | #[derive(Debug)] 183 | pub enum Tf { 184 | Token(Token), 185 | Filter(Filter) 186 | } 187 | 188 | /// Convert a sequence of tokens into an executable filter. 189 | pub fn parse>(code: T, context: Context) -> Result { 190 | parse_inner(Tokens::new(code, context.clone()).map(Tf::Token), context) 191 | } 192 | 193 | fn parse_inner>(tf_iter: I, context: Context) -> Result { 194 | let mut tf = tf_iter.into_iter().collect::>(); // the list of tokens and filters on which the in-place parsing algorithm operates 195 | // error if any invalid token is found 196 | if let Some(pos) = tf.iter().position(|i| if let Tf::Token(Token::Invalid(_)) = *i { true } else { false }) { 197 | if let Tf::Token(Token::Invalid(c)) = tf[pos] { 198 | return Err(ParseError::InvalidToken(c)); 199 | } else { 200 | unreachable!(); 201 | } 202 | } 203 | // define the macro used for testing if filters are allowed 204 | macro_rules! try_filter { 205 | ($f:expr) => { 206 | match $f { 207 | f => { 208 | if (context.filter_allowed)(&f) { 209 | f 210 | } else { 211 | return Err(ParseError::NotAllowed(f)); 212 | } 213 | } 214 | } 215 | } 216 | } 217 | // remove leading and trailing whitespace as it is semantically irrelevant 218 | while let Some(&Tf::Token(Token::Whitespace)) = tf.first() { tf.remove(0); } 219 | while let Some(&Tf::Token(Token::Whitespace)) = tf.last() { tf.pop(); } 220 | // return an empty filter if the token list is empty 221 | if tf.len() == 0 { return Ok(try_filter!(Filter::Empty)); } 222 | // parse operators in decreasing precedence 223 | for (_, precedence_group) in context.operators.clone().into_iter().rev() { // iterate from highest to lowest precedence 224 | match precedence_group { 225 | PrecedenceGroup::AndThen => { 226 | let mut found = None; // flag any AndThen tokens and remember their contents 227 | for idx in (0..tf.len()).rev() { // iterate right-to-left for in-place manipulation 228 | if let Some(remaining_code) = mem::replace(&mut found, None) { 229 | if let Tf::Token(Token::Whitespace) = tf[idx] { 230 | // ignore whitespace between `;;` and its left operand 231 | tf.remove(idx); 232 | continue; 233 | } 234 | tf[idx] = Tf::Filter(try_filter!(Filter::AndThen { 235 | lhs: Box::new(if let Tf::Filter(ref lhs) = tf[idx] { lhs.clone() } else { try_filter!(Filter::Empty) }), 236 | remaining_code: remaining_code 237 | })); 238 | } else { 239 | match tf.remove(idx) { 240 | Tf::Token(Token::AndThen(remaining_code)) => { 241 | found = Some(remaining_code); // found an AndThen (`;;`), will be merged into a syntax tree with the element to its left 242 | } 243 | tf_item => { 244 | tf.insert(idx, tf_item); 245 | } 246 | } 247 | } 248 | } 249 | if let Some(remaining_code) = found { 250 | // the code begins with an `;;` 251 | tf.insert(0, Tf::Filter(try_filter!(Filter::AndThen { 252 | lhs: Box::new(try_filter!(Filter::Empty)), 253 | remaining_code: remaining_code 254 | }))); 255 | } 256 | } 257 | PrecedenceGroup::Circumfix => { 258 | let mut paren_balance = 0; // how many closing parens have not been matched by opening parens 259 | let mut paren_start = None; // the index of the outermost closing paren 260 | for idx in (0..tf.len()).rev() { // iterate right-to-left for in-place manipulation 261 | match tf[idx] { 262 | Tf::Token(Token::CloseParen) => { 263 | if paren_balance == 0 { 264 | paren_start = Some(idx); 265 | } 266 | paren_balance += 1; 267 | } 268 | Tf::Token(Token::OpenParen) => { 269 | paren_balance -= 1; 270 | if paren_balance < 0 { 271 | return Err(ParseError::UnbalancedParen(Token::OpenParen)); 272 | } else if paren_balance == 0 { 273 | if let Some(paren_start) = paren_start { 274 | if let Tf::Token(Token::CloseParen) = tf[paren_start] { 275 | tf.remove(paren_start); 276 | //let inner = tf.drain(idx + 1..paren_start).collect::>(); //TODO use this when stabilized 277 | let mut inner = vec![]; 278 | for _ in idx + 1..paren_start { 279 | inner.push(tf.remove(idx + 1)); 280 | } 281 | tf[idx] = Tf::Filter(try_filter!(Filter::Custom { 282 | attributes: vec![try!(parse_inner(inner, context.clone()))], 283 | run: Box::new(Labeled::new("", Arc::new(|attrs, input, output| { 284 | assert_eq!(attrs.len(), 1); 285 | attrs[0].run(input, output) 286 | }))) 287 | })); 288 | } else { 289 | return Err(ParseError::MismatchedParens(Token::OpenParen, tf.remove(paren_start))); 290 | } 291 | } else { 292 | unreachable!(); 293 | } 294 | paren_start = None; 295 | } 296 | } 297 | _ => { continue; } 298 | } 299 | } 300 | if paren_balance > 0 { 301 | if let Some(paren_start) = paren_start { 302 | if let Tf::Token(Token::CloseParen) = tf[paren_start] { 303 | return Err(ParseError::UnbalancedParen(Token::CloseParen)); 304 | } else { 305 | unreachable!(); 306 | } 307 | } else { 308 | unreachable!(); 309 | } 310 | } 311 | } 312 | } 313 | } 314 | if tf.len() == 1 { 315 | match tf.pop() { 316 | Some(Tf::Filter(result)) => Ok(result), 317 | Some(Tf::Token(token)) => Err(ParseError::NotFullyParsed(vec![Tf::Token(token)])), 318 | None => unreachable!() 319 | } 320 | } else { 321 | Err(ParseError::NotFullyParsed(tf)) 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /src/lang/value/array.rs: -------------------------------------------------------------------------------- 1 | use std::hash; 2 | use std::iter::FromIterator; 3 | use std::sync::mpsc; 4 | 5 | use lang::channel::Receiver; 6 | use lang::value::Value; 7 | 8 | //TODO implement lazy arrays by also including a receiver 9 | #[derive(Clone, Debug)] 10 | pub struct Array { 11 | buffer: Vec 12 | } 13 | 14 | impl<'a, T> Array { 15 | pub fn new() -> Array { 16 | Array::default() 17 | } 18 | 19 | pub fn get(&self, idx: usize) -> Option<&T> { 20 | self.buffer.get(idx) 21 | } 22 | 23 | pub fn iter(&'a self) -> Iter<'a, T> { 24 | Iter { 25 | array: self, 26 | index: 0 27 | } 28 | } 29 | } 30 | 31 | impl Default for Array { 32 | fn default() -> Array { 33 | Array { buffer: Vec::new() } 34 | } 35 | } 36 | 37 | impl From> for Array { 38 | fn from(v: Vec) -> Array { 39 | Array { buffer: v } 40 | } 41 | } 42 | 43 | impl From for Array { 44 | fn from(rx: Receiver) -> Array { 45 | Array { buffer: rx.values.into_iter().collect() } //TODO implement lazy arrays 46 | } 47 | } 48 | 49 | impl FromIterator for Array where U: From { 50 | fn from_iter>(iter: I) -> Array { 51 | Array::from(Vec::from_iter(iter.into_iter().map(U::from))) 52 | } 53 | } 54 | 55 | pub struct IntoIter { 56 | buffer: Vec, 57 | channel: mpsc::Receiver 58 | } 59 | 60 | impl Iterator for IntoIter { 61 | type Item = T; 62 | 63 | fn next(&mut self) -> Option { 64 | if self.buffer.len() > 0 { 65 | Some(self.buffer.remove(0)) 66 | } else { 67 | self.channel.recv().ok() 68 | } 69 | } 70 | } 71 | 72 | pub struct Iter<'a, T: 'a> { 73 | array: &'a Array, 74 | index: usize 75 | } 76 | 77 | impl<'a, T> Iterator for Iter<'a, T> { 78 | type Item = &'a T; 79 | 80 | fn next(&mut self) -> Option<&'a T> { 81 | let result = self.array.get(self.index); 82 | self.index += 1; 83 | result 84 | } 85 | } 86 | 87 | impl IntoIterator for Array { 88 | type Item = T; 89 | type IntoIter = IntoIter; 90 | 91 | fn into_iter(self) -> IntoIter { 92 | IntoIter { 93 | buffer: self.buffer, 94 | channel: mpsc::channel().1 //TODO use the array's channel 95 | } 96 | } 97 | } 98 | 99 | impl<'a, T> IntoIterator for &'a Array { 100 | type Item = &'a T; 101 | type IntoIter = Iter<'a, T>; 102 | 103 | fn into_iter(self) -> Iter<'a, T> { 104 | Iter { 105 | array: self, 106 | index: 0 107 | } 108 | } 109 | } 110 | 111 | impl PartialEq> for Array where T: PartialEq { 112 | fn eq(&self, other: &Array) -> bool { 113 | self.iter().eq(other.iter()) 114 | } 115 | } 116 | 117 | impl Eq for Array { 118 | // marker trait 119 | } 120 | 121 | impl hash::Hash for Array { 122 | fn hash(&self, state: &mut H) { 123 | // only the first element is hashed to avoid blocking on the entire array 124 | if let Some(v) = self.get(0) { 125 | v.hash(state); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/lang/value/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod array; 2 | pub mod object; 3 | 4 | pub use self::array::Array; 5 | pub use self::object::Object; 6 | 7 | use std::{fmt, hash, string}; 8 | use std::iter::FromIterator; 9 | 10 | use num::BigRational; 11 | 12 | use unicode::UString; 13 | 14 | #[derive(Clone, Debug)] 15 | pub enum Value { 16 | Exception(UString, Object), 17 | Null, 18 | Boolean(bool), 19 | Number(BigRational), 20 | String(UString), 21 | Array(Array), 22 | Object(Object), 23 | Function //TODO Function(Function) 24 | } 25 | 26 | #[derive(Clone, Debug)] 27 | pub enum HashableValue { 28 | Exception(UString, Object), 29 | Null, 30 | Boolean(bool), 31 | Number(BigRational), 32 | String(UString), 33 | Array(Array), 34 | Object(Object) 35 | } 36 | 37 | impl fmt::Display for Value { 38 | fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { 39 | use self::Value::*; 40 | 41 | match *self { 42 | Exception(ref name, ref meta) => { 43 | try!(write!(w, "raise {:?}", string::String::from(name))); 44 | if meta.len() > 0 { 45 | try!(write!(w, " {{")); 46 | for (i, (k, v)) in meta.iter().enumerate() { 47 | if i > 0 { 48 | try!(write!(w, ", ")); 49 | } 50 | try!(k.fmt(w)); 51 | try!(write!(w, ": ")); 52 | try!(v.fmt(w)); 53 | } 54 | try!(write!(w, "}}")); 55 | } 56 | } 57 | Null => { 58 | try!(write!(w, "null")); 59 | } 60 | Boolean(b) => { 61 | try!(write!(w, "{}", if b { "true" } else { "false" })); 62 | } 63 | Number(ref n) => { 64 | try!(write!(w, "{}", n)); 65 | } 66 | String(ref s) => { 67 | try!(write!(w, "{:?}", string::String::from(s))); 68 | } 69 | Array(ref a) => { 70 | try!(write!(w, "[")); 71 | for (i, item) in a.iter().enumerate() { 72 | if i > 0 { 73 | try!(write!(w, ", ")); 74 | } 75 | try!(item.fmt(w)); 76 | } 77 | try!(write!(w, "]")); 78 | } 79 | Object(ref o) => { 80 | try!(write!(w, "{{")); 81 | for (i, (k, v)) in o.iter().enumerate() { 82 | if i > 0 { 83 | try!(write!(w, ", ")); 84 | } 85 | try!(k.fmt(w)); 86 | try!(write!(w, ": ")); 87 | try!(v.fmt(w)); 88 | } 89 | try!(write!(w, "}}")); 90 | } 91 | Function => { 92 | try!(write!(w, "def (...)")); 93 | } 94 | } 95 | Ok(()) 96 | } 97 | } 98 | 99 | impl fmt::Display for HashableValue { 100 | fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { 101 | Value::from(self).fmt(w) 102 | } 103 | } 104 | 105 | impl From for Value { 106 | fn from(v: HashableValue) -> Value { 107 | match v { 108 | HashableValue::Exception(name, meta) => { 109 | Value::Exception(name, Object::from_iter(meta)) 110 | } 111 | HashableValue::Null => { 112 | Value::Null 113 | } 114 | HashableValue::Boolean(b) => { 115 | Value::Boolean(b) 116 | } 117 | HashableValue::Number(n) => { 118 | Value::Number(n) 119 | } 120 | HashableValue::String(s) => { 121 | Value::String(s) 122 | } 123 | HashableValue::Array(a) => { 124 | Value::Array(Array::from_iter(a)) 125 | } 126 | HashableValue::Object(o) => { 127 | Value::Object(Object::from_iter(o)) 128 | } 129 | } 130 | } 131 | } 132 | 133 | impl<'a> From<&'a HashableValue> for Value { 134 | fn from(v: &HashableValue) -> Value { 135 | match *v { 136 | HashableValue::Exception(ref name, ref meta) => { 137 | Value::Exception(name.clone(), Object::from_iter(meta.clone())) 138 | } 139 | HashableValue::Null => { 140 | Value::Null 141 | } 142 | HashableValue::Boolean(b) => { 143 | Value::Boolean(b) 144 | } 145 | HashableValue::Number(ref n) => { 146 | Value::Number(n.clone()) 147 | } 148 | HashableValue::String(ref s) => { 149 | Value::String(s.clone()) 150 | } 151 | HashableValue::Array(ref a) => { 152 | Value::Array(Array::from_iter(a.clone())) 153 | } 154 | HashableValue::Object(ref o) => { 155 | Value::Object(Object::from_iter(o.clone())) 156 | } 157 | } 158 | } 159 | } 160 | 161 | impl hash::Hash for HashableValue { 162 | fn hash(&self, state: &mut H) { 163 | use self::HashableValue::*; 164 | 165 | match *self { 166 | Exception(ref s, _) => { 167 | 0.hash(state); 168 | s.hash(state); 169 | } 170 | Null => { 171 | 1.hash(state); 172 | } 173 | Boolean(ref b) => { 174 | 2.hash(state); 175 | b.hash(state); 176 | } 177 | Number(ref n) => { 178 | 3.hash(state); 179 | n.hash(state); 180 | } 181 | String(ref s) => { 182 | 4.hash(state); 183 | s.hash(state); 184 | } 185 | Array(ref a) => { 186 | 5.hash(state); 187 | a.hash(state); 188 | } 189 | Object(ref o) => { 190 | 6.hash(state); 191 | o.hash(state); 192 | } 193 | } 194 | } 195 | } 196 | 197 | impl PartialEq for HashableValue { 198 | fn eq(&self, other: &Self) -> bool { 199 | use self::HashableValue::*; 200 | 201 | match (self, other) { 202 | (&Exception(ref sl, _), &Exception(ref sr, _)) => sl == sr, 203 | (&Null, &Null) => true, 204 | (&Boolean(bl), &Boolean(br)) => bl == br, 205 | (&Number(ref nl), &Number(ref nr)) => nl == nr, 206 | (&String(ref sl), &String(ref sr)) => sl == sr, 207 | (&Array(ref al), &Array(ref ar)) => al == ar, 208 | (&Object(ref ol), &Object(ref or)) => ol == or, 209 | (_, _) => false 210 | } 211 | } 212 | } 213 | 214 | impl Eq for HashableValue {} 215 | 216 | #[test] 217 | fn test_values() { 218 | use std::collections; 219 | 220 | let mut array_map: collections::HashMap, &str> = collections::HashMap::new(); 221 | array_map.insert(Array::from(vec![HashableValue::Boolean(false), HashableValue::Null]), "test 1"); 222 | array_map.insert(Array::new(), "test 2"); 223 | } 224 | -------------------------------------------------------------------------------- /src/lang/value/object.rs: -------------------------------------------------------------------------------- 1 | use std::{hash, mem, vec}; 2 | use std::iter::FromIterator; 3 | 4 | //TODO implement lazy objects by also including a receiver 5 | #[derive(Clone, Debug)] 6 | pub struct Object { 7 | buffer: Vec<(K, V)> 8 | } 9 | 10 | impl Object { 11 | pub fn get_idx(&self, idx: usize) -> Option<(&K, &V)> { 12 | if self.buffer.len() > idx { 13 | let (ref k, ref v) = self.buffer[idx]; 14 | Some((k, v)) 15 | } else { 16 | None 17 | } 18 | } 19 | 20 | pub fn insert(&mut self, k: K, v: V) -> Option { 21 | for &mut (ref key, ref mut val) in &mut self.buffer { 22 | if *key == k { 23 | return Some(mem::replace(val, v)); 24 | } 25 | } 26 | self.buffer.push((k, v)); 27 | None 28 | } 29 | 30 | pub fn iter(&self) -> Iter { 31 | Iter { 32 | object: self, 33 | index: 0 34 | } 35 | } 36 | 37 | pub fn len(&self) -> usize { 38 | self.buffer.len() 39 | } 40 | } 41 | 42 | impl Default for Object { 43 | fn default() -> Object { 44 | Object { buffer: Vec::default() } 45 | } 46 | } 47 | 48 | impl FromIterator<(K, V)> for Object where K: Into, V: Into { 49 | fn from_iter>(iter: I) -> Object { 50 | let mut result = Object::default(); 51 | for (k, v) in iter { 52 | result.insert(k.into(), v.into()); 53 | } 54 | result 55 | } 56 | } 57 | 58 | impl hash::Hash for Object { 59 | fn hash(&self, _: &mut H) { 60 | //TODO 61 | } 62 | } 63 | 64 | impl PartialEq> for Object { 65 | fn eq(&self, other: &Object) -> bool { 66 | self.buffer.len() == other.buffer.len() && 67 | self.iter().all(|(k1, v1)| other.iter().find(|&(k2, _)| k1 == k2).map_or(false, |(_, v2)| v2 == v1)) 68 | } 69 | } 70 | 71 | impl Eq for Object {} 72 | 73 | impl IntoIterator for Object { 74 | type Item = (K, V); 75 | type IntoIter = IntoIter; 76 | 77 | fn into_iter(self) -> IntoIter { 78 | IntoIter { 79 | buffer: self.buffer.into_iter() 80 | } 81 | } 82 | } 83 | 84 | pub struct IntoIter { 85 | buffer: vec::IntoIter<(K, V)> 86 | } 87 | 88 | impl Iterator for IntoIter { 89 | type Item = (K, V); 90 | 91 | fn next(&mut self) -> Option<(K, V)> { 92 | self.buffer.next() 93 | } 94 | } 95 | 96 | impl<'a, K: Eq, V> IntoIterator for &'a Object { 97 | type Item = (&'a K, &'a V); 98 | type IntoIter = Iter<'a, K, V>; 99 | 100 | fn into_iter(self) -> Iter<'a, K, V> { 101 | Iter { 102 | object: self, 103 | index: 0 104 | } 105 | } 106 | } 107 | 108 | pub struct Iter<'a, K: 'a + Eq, V: 'a> { 109 | object: &'a Object, 110 | index: usize 111 | } 112 | 113 | impl<'a, K: Eq, V> Iterator for Iter<'a, K, V> { 114 | type Item = (&'a K, &'a V); 115 | 116 | fn next(&mut self) -> Option<(&'a K, &'a V)> { 117 | let result = self.object.get_idx(self.index); 118 | self.index += 1; 119 | result 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(test, deny(missing_docs, warnings))] 2 | #![forbid(unused_variables)] 3 | 4 | extern crate chan; 5 | extern crate eventual; 6 | extern crate itertools; 7 | extern crate num; 8 | extern crate unicode; 9 | 10 | pub mod builtin; 11 | pub mod lang; 12 | mod util; 13 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use lang::channel::{Sender, Receiver}; 2 | use lang::filter::Filter; 3 | 4 | use std::fmt; 5 | use std::ops::{Deref, DerefMut}; 6 | use std::sync::Arc; 7 | 8 | #[derive(Clone)] 9 | pub struct Labeled { 10 | label: String, 11 | value: T 12 | } 13 | 14 | impl Labeled { 15 | pub fn new>(label: S, value: T) -> Labeled { 16 | Labeled { 17 | label: label.into(), 18 | value: value 19 | } 20 | } 21 | } 22 | 23 | impl From for Labeled { 24 | fn from(value: T) -> Labeled { 25 | Labeled::new("", value) 26 | } 27 | } 28 | 29 | impl Deref for Labeled { 30 | type Target = T; 31 | 32 | fn deref(&self) -> &T { 33 | &self.value 34 | } 35 | } 36 | 37 | impl DerefMut for Labeled { 38 | fn deref_mut(&mut self) -> &mut T { 39 | &mut self.value 40 | } 41 | } 42 | 43 | impl fmt::Debug for Labeled { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | fmt::Display::fmt(&self.label, f) 46 | } 47 | } 48 | 49 | pub type FilterFn = Labeled>; 50 | --------------------------------------------------------------------------------