├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── eval.rs ├── lib.rs ├── main.rs ├── parser.rs ├── tokenizer.rs └── types.rs └── tests ├── callback.lol ├── fac.lol ├── fail ├── divide-by-zero.lol └── stack-overflow.lol ├── function-ordering.lol ├── generate-quine.rb ├── implicit-return.lol ├── int-overflow.lol ├── nan.lol ├── pow.lol └── quine.lol /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["jD91mZM2 "] 3 | description = "A LOLCODE interpreter written in Rust" 4 | license-file = "LICENSE" 5 | name = "lci" 6 | readme = "README.md" 7 | repository = "https://github.com/jD91mZM2/rust-lci" 8 | version = "0.1.8" 9 | 10 | [dependencies] 11 | failure = "0.1.1" 12 | unic-char-range = "0.7.0" 13 | unic-ucd-name = "0.7.0" 14 | 15 | [features] 16 | debug = [] 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 jD91mZM2 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 | # rust-lci [![Crates.io](https://img.shields.io/crates/v/lci.svg)](https://crates.io/crates/lci) ![Travis](https://img.shields.io/travis/jD91mZM2/rust-lci.svg) [![WASM Demo](https://img.shields.io/badge/Demo-WASM-%23FF0000.svg)](https://jd91mzm2.github.io/lolcode) 2 | 3 | *A LOLCODE interpreter written in Rust* 4 | 5 | Target version: 1.2, following [the specs](https://github.com/justinmeza/lolcode-spec/blob/master/v1.2/lolcode-spec-v1.2.md). 6 | 7 | **Note: This is NOT a 100% faithful clone. Some minor things are changed if I think it makes more sense that way.** 8 | Example: 9 | ```LOLCODE 10 | SUM OF OBTW comment in the way TLDR 5 AN 4 11 | ``` 12 | In the original LOLCODE, that's a parsing error because the OBTW is read as an identifier. 13 | In my clone, this works perfectly fine (although I recommend not doing it, because it's hard to read). 14 | 15 | There is no type casting in rust-lci either. 16 | This is because everything is already implicit where needed, so I don't see any point. 17 | 18 | ## Why? 19 | 20 | I'm bored. 21 | I feel like porting more of my things to Web Assembly, but I ran out of things to port. 22 | I could've probably just ported the original LOLCODE directly, but meh. 23 | -------------------------------------------------------------------------------- /src/eval.rs: -------------------------------------------------------------------------------- 1 | use parser::{AST, Expr, Operation}; 2 | use std::{ 3 | cell::RefCell, 4 | collections::HashMap, 5 | io, 6 | result::Result as StdResult 7 | }; 8 | use types::Value; 9 | 10 | #[derive(Debug, Fail)] 11 | pub enum Error { 12 | #[fail(display = "attempt to divide by zero")] 13 | DivideByZero, 14 | #[fail(display = "cannot cast value to that type")] 15 | InvalidCast, 16 | #[fail(display = "loop variable cannot be casted to numbr")] 17 | InvalidCastLoop, 18 | #[fail(display = "function {:?} expected {} parameters", _0, _1)] 19 | InvalidUsage(String, usize), 20 | #[fail(display = "io error: {}", _0)] 21 | IoError(io::Error), 22 | #[fail(display = "recursion limit reached: can't go more than {} levels deep", _0)] 23 | RecursionLimit(usize), 24 | #[fail(display = "can't shadow variable from the same scope: {:?}", _0)] 25 | ShadowVar(String), 26 | #[fail(display = "undefined function {:?}", _0)] 27 | UndefinedFunc(String), 28 | #[fail(display = "undefined variable {:?}", _0)] 29 | UndefinedVar(String) 30 | } 31 | 32 | type Result = StdResult; 33 | 34 | pub enum Return { 35 | None, 36 | Gtfo, 37 | Value(Value) 38 | } 39 | 40 | struct Function { 41 | args: Vec, 42 | block: Vec 43 | } 44 | 45 | /// Parameters global to the whole evaluation 46 | pub struct EvalParams { 47 | stdin: R, 48 | stdout: W, 49 | 50 | funcs: HashMap, Box) -> Value>)>, 51 | recursion_limit: usize, 52 | recursion: usize 53 | } 54 | impl EvalParams { 55 | pub fn new(stdin: R, stdout: W) -> Self { 56 | Self { 57 | stdin: stdin, 58 | stdout: stdout, 59 | 60 | funcs: HashMap::new(), 61 | recursion_limit: 64, 62 | recursion: 0 63 | } 64 | } 65 | /// Set the recursion limit 66 | pub fn set_recursion_limit(&mut self, limit: usize) { 67 | self.recursion_limit = limit; 68 | } 69 | /// Bind a LOLCODE function to a rust closure 70 | pub fn bind_func(&mut self, name: S, args: Option, func: F) 71 | where S: Into, 72 | F: FnMut(Vec) -> Value + 'static 73 | { 74 | self.funcs.insert(name.into(), (args, Box::new(func))); 75 | } 76 | /// Create a new top-level scope with this evaluator. 77 | /// Use the return value of this to evaluate AST. 78 | pub fn scope<'a>(self) -> Scope<'a, R, W> { 79 | Scope { 80 | params: Some(RefCell::new(self)), 81 | 82 | it: RefCell::new(Value::Noob), 83 | vars: RefCell::new(HashMap::new()), 84 | funcs: RefCell::new(HashMap::new()), 85 | parent: None 86 | } 87 | } 88 | } 89 | 90 | /// Parameters local to the current scope 91 | pub struct Scope<'a, R: io::BufRead + 'a, W: io::Write + 'a> { 92 | params: Option>>, 93 | 94 | it: RefCell, 95 | vars: RefCell>, 96 | funcs: RefCell>, 97 | parent: Option<&'a Scope<'a, R, W>> 98 | } 99 | impl<'a, R: io::BufRead, W: io::Write> Scope<'a, R, W> { 100 | pub fn params(&self) -> &RefCell> { 101 | let mut me = self; 102 | while let Some(parent) = me.parent { 103 | me = parent; 104 | } 105 | me.params.as_ref().expect("Missing 'params' on top-level scope") 106 | } 107 | pub fn find_var(&self, name: &str, apply: F) -> Option 108 | where F: FnOnce(&mut Value) -> T 109 | { 110 | let mut me = self; 111 | loop { 112 | let mut vars = me.vars.borrow_mut(); 113 | if let Some(var) = vars.get_mut(name) { 114 | break Some(apply(var)); 115 | } else if let Some(parent) = me.parent { 116 | me = parent; 117 | } else { 118 | break None; 119 | } 120 | } 121 | } 122 | pub fn call_func(&self, name: &str, args: Vec) -> Result { 123 | { 124 | let params = self.params(); 125 | let params = &mut params.borrow_mut(); 126 | 127 | // Check for any library defined functions 128 | if let Some(&mut (nargs, ref mut func)) = params.funcs.get_mut(name) { 129 | if let Some(nargs) = nargs { 130 | if args.len() != nargs { 131 | return Err(Error::InvalidUsage(name.to_string(), nargs)); 132 | } 133 | } 134 | return Ok(func(args)); 135 | } 136 | 137 | // Prevent stack overflow 138 | params.recursion += 1; 139 | if params.recursion > params.recursion_limit { 140 | return Err(Error::RecursionLimit(params.recursion_limit)); 141 | } 142 | } 143 | // Traverse down scopes 144 | let mut me = self; 145 | Ok(loop { 146 | let block = match me.funcs.borrow_mut().get_mut(name) { 147 | None => None, 148 | Some(the_func) => { 149 | if args.len() != the_func.args.len() { 150 | return Err(Error::InvalidUsage(name.to_string(), the_func.args.len())); 151 | } 152 | 153 | let mut vars = me.vars.borrow_mut(); 154 | for (i, arg) in args.iter().enumerate() { 155 | vars.insert(the_func.args[i].clone(), arg.clone()); 156 | } 157 | Some(the_func.block.clone()) 158 | } 159 | }; 160 | if let Some(block) = block { 161 | let scope = me.scope(); 162 | let val = match scope.eval_all(block)? { 163 | Return::None => scope.it.borrow().clone(), 164 | Return::Gtfo => Value::Noob, 165 | Return::Value(val) => val 166 | }; 167 | { 168 | let params = &mut me.params(); 169 | params.borrow_mut().recursion -= 1; 170 | } 171 | break val; 172 | } else if let Some(parent) = me.parent { 173 | me = parent; 174 | } else { 175 | return Err(Error::UndefinedFunc(name.to_string())); 176 | } 177 | }) 178 | } 179 | pub fn scope(&'a self) -> Self { 180 | Self { 181 | params: None, 182 | 183 | it: self.it.clone(), 184 | vars: RefCell::new(HashMap::new()), 185 | funcs: RefCell::new(HashMap::new()), 186 | parent: Some(self) 187 | } 188 | } 189 | 190 | fn apply_num(&self, one: Expr, two: Expr, if_numbr: F1, if_numbar: F2) -> Result 191 | where F1: FnOnce(i64, i64) -> Result, 192 | F2: FnOnce(f64, f64) -> f64 193 | { 194 | let one = self.eval_expr(one)?; 195 | let two = self.eval_expr(two)?; 196 | if one.is_numbar() || two.is_numbar() { 197 | Ok(Value::Numbar(if_numbar( 198 | one.cast_numbar().ok_or(Error::InvalidCast)?, 199 | two.cast_numbar().ok_or(Error::InvalidCast)? 200 | ))) 201 | } else { 202 | Ok(Value::Numbr(if_numbr( 203 | one.cast_numbr().ok_or(Error::InvalidCast)?, 204 | two.cast_numbr().ok_or(Error::InvalidCast)? 205 | )?)) 206 | } 207 | } 208 | fn apply_any(&self, one: Expr, two: Expr, apply: F) -> Result 209 | where F: FnOnce(Value, Value) -> bool 210 | { 211 | Ok(Value::Troof(apply(self.eval_expr(one)?, self.eval_expr(two)?))) 212 | } 213 | fn apply_bool(&self, one: Expr, two: Expr, apply: F) -> Result 214 | where F: FnOnce(bool, bool) -> bool 215 | { 216 | self.apply_any(one, two, |x, y| apply(x.cast_troof(), y.cast_troof())) 217 | } 218 | pub fn eval_expr(&self, expr: Expr) -> Result { 219 | macro_rules! apply_num { 220 | ($one:expr, $two:expr, $func:ident, $op:tt) => { 221 | self.apply_num(*$one, *$two, |x, y| Ok(x.$func(y)), |x, y| x $op y) 222 | } 223 | } 224 | match expr { 225 | Expr::It => Ok(self.it.borrow().clone()), 226 | Expr::Value(mut val) => { 227 | if let Some(missing) = val.interpolate(|var| self.find_var(var, |var| var.clone())) { 228 | return Err(Error::UndefinedVar(missing)); 229 | } 230 | Ok(val) 231 | }, 232 | Expr::Var(ident) => { 233 | if let Some(val) = self.find_var(&ident, |var| var.clone()) { 234 | return Ok(val); 235 | } else { 236 | return Err(Error::UndefinedVar(ident)); 237 | } 238 | }, 239 | Expr::IIz(name, args) => { 240 | let mut args_val = Vec::with_capacity(args.len()); 241 | for arg in args { 242 | args_val.push(self.eval_expr(arg)?); 243 | } 244 | self.call_func(&name, args_val) 245 | }, 246 | 247 | Expr::SumOf(one, two) => apply_num!(one, two, wrapping_add, +), 248 | Expr::DiffOf(one, two) => apply_num!(one, two, wrapping_sub, -), 249 | Expr::ProduktOf(one, two) => apply_num!(one, two, wrapping_mul, *), 250 | Expr::QuoshuntOf(one, two) => self.apply_num(*one, *two, |x, y| { 251 | if y == 0 { 252 | return Err(Error::DivideByZero); 253 | } 254 | Ok(x / y) 255 | }, |x, y| x / y), 256 | Expr::ModOf(one, two) => self.apply_num(*one, *two, |x, y| { 257 | if y == 0 { 258 | return Err(Error::DivideByZero); 259 | } 260 | Ok(x % y) 261 | }, |x, y| x % y), 262 | Expr::BiggrOf(one, two) => self.apply_num(*one, *two, |x, y| Ok(x.max(y)), |x, y| x.max(y)), 263 | Expr::SmallrOf(one, two) => self.apply_num(*one, *two, |x, y| Ok(x.min(y)), |x, y| x.min(y)), 264 | 265 | Expr::BothOf(one, two) => self.apply_bool(*one, *two, |x, y| x && y), 266 | Expr::EitherOf(one, two) => self.apply_bool(*one, *two, |x, y| x || y), 267 | Expr::WonOf(one, two) => self.apply_bool(*one, *two, |x, y| x ^ y), 268 | Expr::Not(inner) => Ok(Value::Troof(!self.eval_expr(*inner)?.cast_troof())), 269 | Expr::AllOf(values) => { 270 | for value in values { 271 | if !self.eval_expr(value)?.cast_troof() { 272 | return Ok(Value::Troof(false)) 273 | } 274 | } 275 | Ok(Value::Troof(true)) 276 | }, 277 | Expr::AnyOf(values) => { 278 | for value in values { 279 | if self.eval_expr(value)?.cast_troof() { 280 | return Ok(Value::Troof(true)) 281 | } 282 | } 283 | Ok(Value::Troof(false)) 284 | }, 285 | 286 | Expr::BothSaem(one, two) => self.apply_any(*one, *two, |x, y| x.equals(&y)), 287 | Expr::Diffrint(one, two) => self.apply_any(*one, *two, |x, y| !x.equals(&y)), 288 | 289 | Expr::Smoosh(exprs) => { 290 | let mut result = String::new(); 291 | for expr in exprs { 292 | result.push_str(&self.eval_expr(expr)?.cast_yarn().ok_or(Error::InvalidCast)?); 293 | } 294 | Ok(Value::Yarn(result)) 295 | } 296 | } 297 | } 298 | pub fn eval(&self, ast: AST) -> Result { 299 | match ast { 300 | AST::IHasA(ident, expr) => { 301 | let val = self.eval_expr(expr)?; 302 | { 303 | let mut vars = self.vars.borrow_mut(); 304 | if vars.contains_key(&ident) { 305 | return Err(Error::ShadowVar(ident)); 306 | } 307 | vars.insert(ident, val); 308 | } 309 | }, 310 | AST::R(ident, expr) => { 311 | let val = self.eval_expr(expr)?; 312 | if self.find_var(&ident, |var| *var = val).is_none() { 313 | return Err(Error::UndefinedVar(ident)); 314 | } 315 | }, 316 | AST::It(expr) => { 317 | let expr = self.eval_expr(expr)?; 318 | *self.it.borrow_mut() = expr; 319 | }, 320 | AST::ORly(yarly, mebbe, nowai) => { 321 | if self.it.borrow().cast_troof() { 322 | return self.eval_scope(yarly); 323 | } 324 | for (condition, block) in mebbe { 325 | if self.eval_expr(condition)?.cast_troof() { 326 | return self.eval_scope(block); 327 | } 328 | } 329 | return self.eval_scope(nowai); 330 | }, 331 | AST::Wtf(omg, omgwtf) => { 332 | let mut matched = false; 333 | let it = self.it.borrow(); 334 | for (condition, block) in omg { 335 | if matched || *it == self.eval_expr(condition)? { 336 | matched = true; 337 | match self.eval_scope(block)? { 338 | Return::None => (), 339 | Return::Gtfo => return Ok(Return::None), 340 | val @ Return::Value(_) => return Ok(val) 341 | } 342 | } 343 | } 344 | return self.eval_scope(omgwtf); 345 | }, 346 | AST::ImInYr(operation, var, condition, block) => { 347 | let mut scope = self.scope(); 348 | scope.vars.borrow_mut().insert(var.clone(), Value::Numbr(0)); 349 | while condition.is_none() || scope.eval_expr(condition.clone().unwrap())?.cast_troof() { 350 | match scope.eval_scope(block.clone())? { 351 | Return::None => (), 352 | Return::Gtfo => return Ok(Return::None), 353 | val @ Return::Value(_) => return Ok(val) 354 | } 355 | let val = scope.vars.borrow_mut()[&var].clone(); 356 | let val = match operation { 357 | Operation::Uppin => Value::Numbr(val.cast_numbr().ok_or(Error::InvalidCastLoop)? + 1), 358 | Operation::Nerfin => Value::Numbr(val.cast_numbr().ok_or(Error::InvalidCastLoop)? - 1), 359 | Operation::IIz(ref name) => scope.call_func(&name, vec![val])? 360 | }; 361 | *scope.vars.borrow_mut().get_mut(&var).unwrap() = val; 362 | } 363 | }, 364 | AST::HowIzI(name, args, block) => { 365 | self.funcs.borrow_mut().insert(name, Function { 366 | args: args, 367 | block: block 368 | }); 369 | }, 370 | 371 | AST::Gtfo => return Ok(Return::Gtfo), 372 | AST::FoundYr(expr) => return Ok(Return::Value(self.eval_expr(expr)?)), 373 | 374 | AST::Visible(exprs, newline) => { 375 | let mut result = String::new(); 376 | for expr in exprs { 377 | result.push_str(&self.eval_expr(expr)?.cast_yarn().ok_or(Error::InvalidCast)?); 378 | } 379 | let stdout = &mut self.params().borrow_mut().stdout; 380 | stdout.write_all(result.as_bytes()).map_err(|err| Error::IoError(err))?; 381 | if newline { 382 | stdout.write_all(b"\n").map_err(|err| Error::IoError(err))?; 383 | } else { 384 | stdout.flush().map_err(|err| Error::IoError(err))?; 385 | } 386 | }, 387 | AST::Gimmeh(ident) => { 388 | let stdin = &mut self.params().borrow_mut().stdin; 389 | 390 | let mut text = String::new(); 391 | stdin.read_line(&mut text).map_err(|err| Error::IoError(err))?; 392 | 393 | let text = text.trim().to_string(); 394 | self.vars.borrow_mut().insert(ident, Value::Yarn(text)); 395 | } 396 | } 397 | Ok(Return::None) 398 | } 399 | /// Evaluate all lines of ASTs. You probably want to use this for running code. 400 | pub fn eval_all(&self, asts: IN) -> Result 401 | where I: Iterator + Clone, 402 | IN: IntoIterator 403 | { 404 | let iter = asts.into_iter(); 405 | for ast in iter.clone() { 406 | if let AST::HowIzI(..) = ast { 407 | // Pre-process function calls 408 | self.eval(ast)?; 409 | } 410 | } 411 | for ast in iter { 412 | match self.eval(ast)? { 413 | Return::None => (), 414 | val => return Ok(val) 415 | } 416 | } 417 | Ok(Return::None) 418 | } 419 | /// Evaluate all lines of ASTs in a new child scope. Convenience function. 420 | pub fn eval_scope(&self, asts: IN) -> Result 421 | where I: Iterator + Clone, 422 | IN: IntoIterator 423 | { 424 | self.scope().eval_all(asts) 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate failure; 2 | #[macro_use] extern crate unic_char_range; 3 | extern crate unic_ucd_name; 4 | 5 | pub mod eval; 6 | pub mod parser; 7 | pub mod tokenizer; 8 | pub mod types; 9 | 10 | #[derive(Debug, Fail)] 11 | pub enum Error { 12 | #[fail(display = "tokenize error: {}", _0)] 13 | TokenizeError(tokenizer::Error), 14 | #[fail(display = "parse error: {}", _0)] 15 | ParseError(parser::Error), 16 | #[fail(display = "eval error: {}", _0)] 17 | EvalError(eval::Error), 18 | } 19 | 20 | use eval::EvalParams; 21 | use parser::AST; 22 | use std::io; 23 | 24 | /// Convenience function for tokenizing and parsing code 25 | pub fn parse(code: &str) -> Result, Error> { 26 | let tokens = tokenizer::tokenize(code.chars()).map_err(|err| Error::TokenizeError(err))?; 27 | #[cfg(feature = "debug")] println!("{:#?}", tokens); 28 | let parsed = parser::parse(tokens).map_err(|err| Error::ParseError(err))?; 29 | #[cfg(feature = "debug")] println!("{:#?}", parsed); 30 | Ok(parsed) 31 | } 32 | 33 | /// Convenience function for tokenizing, parsing, and evaluating code 34 | pub fn eval(code: &str, stdin: R, stdout: W, callback: F) -> Result<(), Error> 35 | where R: io::BufRead, 36 | W: io::Write, 37 | F: FnOnce(&mut EvalParams) 38 | { 39 | let parsed = parse(code)?; 40 | let mut eval = eval::EvalParams::new(stdin, stdout); 41 | callback(&mut eval); 42 | eval.scope().eval_all(parsed).map_err(|err| Error::EvalError(err))?; 43 | Ok(()) 44 | } 45 | 46 | /// Convenience function for capturing the output of `eval` 47 | pub fn capture(code: &str, stdin: R, callback: F) -> Result 48 | where R: io::BufRead, 49 | F: FnOnce(&mut EvalParams>) 50 | { 51 | let mut output = Vec::new(); 52 | eval(code, stdin, &mut output, callback)?; 53 | Ok(String::from_utf8(output).expect("Program (somehow) returned non-utf8 data")) 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | use types::Value; 60 | 61 | fn run(code: &str) -> Result { 62 | capture(code, io::empty(), |_| ()) 63 | } 64 | 65 | #[test] 66 | fn run_all() { 67 | assert_eq!(run(include_str!("../tests/fac.lol")).expect("Running test failed"), "120\n"); 68 | assert_eq!(run(include_str!("../tests/implicit-return.lol")).expect("Running test failed"), "hi\n"); 69 | assert_eq!(run(include_str!("../tests/int-overflow.lol")).expect("Running test failed"), "WIN\n"); 70 | assert_eq!(run(include_str!("../tests/pow.lol")).expect("Running test failed"), "32\n"); 71 | assert_eq!( 72 | run(include_str!("../tests/function-ordering.lol")).expect("Running test failed"), 73 | "PING 5\n\ 74 | PONG 4\n\ 75 | PING 3\n\ 76 | PONG 2\n\ 77 | PING 1\n" 78 | ); 79 | assert_eq!( 80 | run(include_str!("../tests/quine.lol")).expect("Running test failed"), 81 | include_str!("../tests/quine.lol") 82 | ); 83 | } 84 | 85 | #[test] 86 | fn rust_callback() { 87 | assert_eq!( 88 | capture(include_str!("../tests/callback.lol"), io::empty(), |eval| { 89 | eval.bind_func("LOWERIN", Some(1), |values| { 90 | Value::Yarn(values[0].clone().cast_yarn().unwrap().to_lowercase()) 91 | }); 92 | }).expect("Running test failed"), 93 | "test\n" 94 | ); 95 | } 96 | 97 | #[test] 98 | fn run_fails() { 99 | match run(include_str!("../tests/fail/divide-by-zero.lol")) { 100 | Err(Error::EvalError(eval::Error::DivideByZero)) => (), 101 | _ => panic!("Running test failed (in a bad way)") 102 | } 103 | match run(include_str!("../tests/fail/stack-overflow.lol")) { 104 | Err(Error::EvalError(eval::Error::RecursionLimit(_))) => (), 105 | _ => panic!("Running test failed (in a bad way)") 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate lci; 2 | 3 | use std::{env, fs, io}; 4 | 5 | fn main() { 6 | let file = match env::args().skip(1).next() { 7 | Some(file) => file, 8 | None => { 9 | eprintln!("usage: lci "); 10 | return; 11 | } 12 | }; 13 | 14 | let input = match fs::read_to_string(file) { 15 | Ok(input) => input, 16 | Err(err) => { 17 | eprintln!("error reading file: {}", err); 18 | return; 19 | } 20 | }; 21 | 22 | let stdin = io::stdin(); 23 | let stdin = stdin.lock(); 24 | let stdout = io::stdout(); 25 | let stdout = stdout.lock(); 26 | 27 | match lci::eval(&input, stdin, stdout, |_| ()) { 28 | Ok(()) => (), 29 | Err(err) => { 30 | eprintln!("{}", err); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | iter::Peekable, 3 | result::Result as StdResult 4 | }; 5 | use tokenizer::Token; 6 | use types::Value; 7 | 8 | #[derive(Debug, Fail)] 9 | pub enum Error { 10 | #[fail(display = "expected {}", _0)] 11 | ExpectedKind(&'static str), 12 | #[fail(display = "expected token {:?}, found {:?}", _0, _1)] 13 | ExpectedToken(Token, Token), 14 | #[fail(display = "loop label mismatch. started {:?}, got {:?}", _0, _1)] 15 | LabelMismatch(String, String), 16 | #[fail(display = "trailing characters after statement")] 17 | Trailing, 18 | #[fail(display = "unexpected end of file")] 19 | UnexpectedEOF 20 | } 21 | 22 | type Result = StdResult; 23 | 24 | #[derive(Clone, Debug, PartialEq)] 25 | pub enum Operation { 26 | Uppin, 27 | Nerfin, 28 | IIz(String) 29 | } 30 | #[derive(Clone, Debug, PartialEq)] 31 | pub enum Expr { 32 | It, 33 | Var(String), 34 | Value(Value), 35 | IIz(String, Vec), 36 | 37 | SumOf(Box, Box), 38 | DiffOf(Box, Box), 39 | ProduktOf(Box, Box), 40 | QuoshuntOf(Box, Box), 41 | ModOf(Box, Box), 42 | BiggrOf(Box, Box), 43 | SmallrOf(Box, Box), 44 | 45 | BothOf(Box, Box), 46 | EitherOf(Box, Box), 47 | WonOf(Box, Box), 48 | Not(Box), 49 | AllOf(Vec), 50 | AnyOf(Vec), 51 | 52 | BothSaem(Box, Box), 53 | Diffrint(Box, Box), 54 | 55 | Smoosh(Vec) 56 | } 57 | #[derive(Clone, Debug, PartialEq)] 58 | pub enum AST { 59 | IHasA(String, Expr), 60 | R(String, Expr), 61 | It(Expr), 62 | ORly(Vec, Vec<(Expr, Vec)>, Vec), 63 | Wtf(Vec<(Expr, Vec)>, Vec), 64 | ImInYr(Operation, String, Option, Vec), 65 | HowIzI(String, Vec, Vec), 66 | 67 | Gtfo, 68 | FoundYr(Expr), 69 | 70 | Visible(Vec, bool), 71 | Gimmeh(String), 72 | } 73 | 74 | pub struct Parser> { 75 | pub iter: Peekable 76 | } 77 | impl> Parser { 78 | fn block(&mut self, until: &[Token]) -> Result> { 79 | let mut block = Vec::new(); 80 | loop { 81 | match self.iter.peek() { 82 | Some(token) => if until.contains(&token) { break; }, 83 | None => () 84 | } 85 | if let Some(ast) = self.statement()? { 86 | block.push(ast); 87 | } 88 | } 89 | Ok(block) 90 | } 91 | fn expect(&mut self, token: Token) -> Result<()> { 92 | match self.iter.next() { 93 | Some(ref token2) if token == *token2 => Ok(()), 94 | Some(token2) => Err(Error::ExpectedToken(token, token2)), 95 | None => Err(Error::UnexpectedEOF) 96 | } 97 | } 98 | fn expect_peek(&mut self, token: Token) -> Result<()> { 99 | match self.iter.peek() { 100 | Some(token2) if token == *token2 => Ok(()), 101 | Some(token2) => Err(Error::ExpectedToken(token, token2.clone())), 102 | None => Err(Error::UnexpectedEOF) 103 | } 104 | } 105 | fn expect_expr(&mut self) -> Result { 106 | self.expression()?.ok_or(Error::ExpectedKind("expression")) 107 | } 108 | fn expect_ident(&mut self) -> Result { 109 | match self.iter.next() { 110 | Some(Token::Ident(ident)) => Ok(ident), 111 | _ => Err(Error::ExpectedKind("identifier")) 112 | } 113 | } 114 | fn trim(&mut self) { 115 | while let Some(&Token::Separator) = self.iter.peek() { 116 | self.iter.next(); 117 | } 118 | } 119 | /// Read one statement from the AST, including trailing line separator 120 | pub fn statement(&mut self) -> Result> { 121 | let stmt = self.inner_statement()?; 122 | match self.iter.next() { 123 | None | Some(Token::Separator) => Ok(stmt), 124 | _ => Err(Error::Trailing) 125 | } 126 | } 127 | fn inner_statement(&mut self) -> Result> { 128 | match self.iter.peek() { 129 | Some(&Token::Hai) => { 130 | self.iter.next(); 131 | match self.iter.next() { 132 | Some(Token::Value(_)) => Ok(None), 133 | _ => return Err(Error::ExpectedKind("numbar")) 134 | } 135 | }, 136 | Some(&Token::KThxBye) => { 137 | self.iter.next(); 138 | Ok(None) 139 | }, 140 | Some(&Token::Gtfo) => { 141 | self.iter.next(); 142 | Ok(Some(AST::Gtfo)) 143 | }, 144 | Some(&Token::FoundYr) => { 145 | self.iter.next(); 146 | Ok(Some(AST::FoundYr(self.expect_expr()?))) 147 | }, 148 | Some(&Token::IHasA) => { 149 | self.iter.next(); 150 | let ident = self.expect_ident()?; 151 | match self.iter.peek() { 152 | Some(&Token::Itz) => { 153 | self.iter.next(); 154 | let expression = self.expect_expr()?; 155 | Ok(Some(AST::IHasA(ident, expression))) 156 | }, 157 | None | Some(&Token::Separator) => { 158 | Ok(Some(AST::IHasA(ident, Expr::Value(Value::Noob)))) 159 | }, 160 | _ => Err(Error::Trailing) 161 | } 162 | }, 163 | Some(&Token::Ident(_)) => { 164 | if let Some(Token::Ident(ident)) = self.iter.next() { 165 | match self.iter.peek() { 166 | Some(&Token::R) => { 167 | self.iter.next(); 168 | let expression = self.expect_expr()?; 169 | Ok(Some(AST::R(ident, expression))) 170 | }, 171 | None | Some(&Token::Separator) => { 172 | Ok(Some(AST::It(Expr::Var(ident)))) 173 | }, 174 | _ => Err(Error::Trailing) 175 | } 176 | } else { unreachable!(); } 177 | }, 178 | Some(&Token::ORly) => { 179 | self.iter.next(); 180 | self.expect(Token::Separator)?; 181 | self.trim(); 182 | self.expect(Token::YaRly)?; 183 | self.expect(Token::Separator)?; 184 | let mut yarly = self.block(&[Token::Mebbe, Token::NoWai, Token::Oic])?; 185 | 186 | let mut mebbe = Vec::new(); 187 | self.trim(); 188 | while let Some(&Token::Mebbe) = self.iter.peek() { 189 | self.iter.next(); 190 | let condition = self.expect_expr()?; 191 | self.expect(Token::Separator)?; 192 | let mut block = self.block(&[Token::Mebbe, Token::NoWai, Token::Oic])?; 193 | self.trim(); 194 | 195 | mebbe.push((condition, block)); 196 | } 197 | 198 | let nowai = if let Some(&Token::NoWai) = self.iter.peek() { 199 | self.iter.next(); 200 | self.expect(Token::Separator)?; 201 | self.block(&[Token::Oic])? 202 | } else { Vec::new() }; 203 | self.expect(Token::Oic)?; 204 | Ok(Some(AST::ORly(yarly, mebbe, nowai))) 205 | }, 206 | Some(&Token::Wtf) => { 207 | self.iter.next(); 208 | self.expect(Token::Separator)?; 209 | self.trim(); 210 | self.expect_peek(Token::Omg)?; 211 | 212 | let mut omg = Vec::new(); 213 | while let Some(&Token::Omg) = self.iter.peek() { 214 | self.iter.next(); 215 | let expr = self.expect_expr()?; 216 | self.expect(Token::Separator)?; 217 | let mut block = self.block(&[Token::Omg, Token::OmgWtf, Token::Oic])?; 218 | self.trim(); 219 | 220 | omg.push((expr, block)); 221 | } 222 | let mut omgwtf = if let Some(&Token::OmgWtf) = self.iter.peek() { 223 | self.iter.next(); 224 | self.expect(Token::Separator)?; 225 | self.block(&[Token::Oic])? 226 | } else { Vec::new() }; 227 | self.expect(Token::Oic)?; 228 | Ok(Some(AST::Wtf(omg, omgwtf))) 229 | }, 230 | Some(&Token::ImInYr) => { 231 | self.iter.next(); 232 | let label = self.expect_ident()?; 233 | let operation = match self.iter.next() { 234 | Some(Token::Uppin) => Operation::Uppin, 235 | Some(Token::Nerfin) => Operation::Nerfin, 236 | Some(Token::IIz) => Operation::IIz(self.expect_ident()?), 237 | _ => return Err(Error::ExpectedKind("operation")) 238 | }; 239 | self.expect(Token::Yr)?; 240 | let var = self.expect_ident()?; 241 | if let Operation::IIz(_) = operation { 242 | self.expect(Token::Mkay)?; 243 | } 244 | let condition = match self.iter.peek() { 245 | Some(&Token::Wile) => { self.iter.next(); Some(self.expect_expr()?) }, 246 | Some(&Token::Til) => { self.iter.next(); Some(Expr::Not(Box::new(self.expect_expr()?))) }, 247 | Some(&Token::Separator) => None, 248 | _ => return Err(Error::ExpectedKind("condition")) 249 | }; 250 | self.expect(Token::Separator)?; 251 | let block = self.block(&[Token::ImOuttaYr])?; 252 | self.expect(Token::ImOuttaYr)?; 253 | let label2 = self.expect_ident()?; 254 | if label != label2 { 255 | return Err(Error::LabelMismatch(label, label2)); 256 | } 257 | Ok(Some(AST::ImInYr(operation, var, condition, block))) 258 | }, 259 | Some(&Token::HowIzI) => { 260 | self.iter.next(); 261 | let name = self.expect_ident()?; 262 | let mut args = Vec::new(); 263 | if let Some(&Token::Yr) = self.iter.peek() { 264 | self.iter.next(); 265 | args.push(self.expect_ident()?); 266 | while let Some(&Token::An) = self.iter.peek() { 267 | self.iter.next(); 268 | self.expect(Token::Yr)?; 269 | args.push(self.expect_ident()?); 270 | } 271 | } 272 | self.expect(Token::Separator)?; 273 | let block = self.block(&[Token::IfUSaySo])?; 274 | self.expect(Token::IfUSaySo)?; 275 | 276 | Ok(Some(AST::HowIzI(name, args, block))) 277 | }, 278 | Some(&Token::Visible) => { 279 | self.iter.next(); 280 | let mut exprs = Vec::new(); 281 | let newline = loop { 282 | exprs.push(self.expect_expr()?); 283 | match self.iter.peek() { 284 | Some(&Token::Exclamation) => { self.iter.next(); break false }, 285 | None | Some(&Token::Separator) => break true, 286 | _ => () 287 | } 288 | }; 289 | Ok(Some(AST::Visible(exprs, newline))) 290 | }, 291 | Some(&Token::Gimmeh) => { 292 | self.iter.next(); 293 | let ident = self.expect_ident()?; 294 | Ok(Some(AST::Gimmeh(ident))) 295 | }, 296 | _ => Ok(self.expression()?.map(|expr| AST::It(expr))) 297 | } 298 | } 299 | fn two_exprs(&mut self) -> Result<(Box, Box)> { 300 | let one = self.expect_expr()?; 301 | if let Some(&Token::An) = self.iter.peek() { 302 | self.iter.next(); 303 | } 304 | let two = self.expect_expr()?; 305 | Ok((Box::new(one), Box::new(two))) 306 | } 307 | fn multiple_exprs(&mut self) -> Result> { 308 | let mut all = Vec::new(); 309 | all.push(self.expect_expr()?); 310 | loop { 311 | match self.iter.peek() { 312 | Some(&Token::Mkay) => { self.iter.next(); break }, 313 | None | Some(&Token::Separator) => break, 314 | Some(&Token::An) => { 315 | self.iter.next(); 316 | all.push(self.expect_expr()?); 317 | }, 318 | _ => all.push(self.expect_expr()?) 319 | } 320 | } 321 | Ok(all) 322 | } 323 | fn expression(&mut self) -> Result> { 324 | macro_rules! x_of { 325 | ($what:path) => { 326 | { 327 | self.iter.next(); 328 | let (one, two) = self.two_exprs()?; 329 | Ok(Some($what(one, two))) 330 | } 331 | } 332 | } 333 | match self.iter.peek() { 334 | Some(&Token::It) => { 335 | self.iter.next(); 336 | Ok(Some(Expr::It)) 337 | } 338 | Some(&Token::Value(_)) => { 339 | if let Some(Token::Value(val)) = self.iter.next() { 340 | Ok(Some(Expr::Value(val))) 341 | } else { unreachable!(); } 342 | }, 343 | Some(&Token::Ident(_)) => { 344 | if let Some(Token::Ident(var)) = self.iter.next() { 345 | Ok(Some(Expr::Var(var))) 346 | } else { unreachable!(); } 347 | }, 348 | Some(&Token::IIz) => { 349 | self.iter.next(); 350 | let name = self.expect_ident()?; 351 | let mut args = Vec::new(); 352 | if let Some(&Token::Yr) = self.iter.peek() { 353 | self.iter.next(); 354 | args.push(self.expect_expr()?); 355 | while let Some(&Token::An) = self.iter.peek() { 356 | self.iter.next(); 357 | self.expect(Token::Yr)?; 358 | args.push(self.expect_expr()?); 359 | } 360 | } 361 | self.expect(Token::Mkay)?; 362 | Ok(Some(Expr::IIz(name, args))) 363 | }, 364 | Some(&Token::SumOf) => x_of!(Expr::SumOf), 365 | Some(&Token::DiffOf) => x_of!(Expr::DiffOf), 366 | Some(&Token::ProduktOf) => x_of!(Expr::ProduktOf), 367 | Some(&Token::QuoshuntOf) => x_of!(Expr::QuoshuntOf), 368 | Some(&Token::ModOf) => x_of!(Expr::ModOf), 369 | Some(&Token::BiggrOf) => x_of!(Expr::BiggrOf), 370 | Some(&Token::SmallrOf) => x_of!(Expr::SmallrOf), 371 | 372 | Some(&Token::BothOf) => x_of!(Expr::BothOf), 373 | Some(&Token::EitherOf) => x_of!(Expr::EitherOf), 374 | Some(&Token::WonOf) => x_of!(Expr::WonOf), 375 | Some(&Token::Not) => { 376 | self.iter.next(); 377 | let expr = self.expect_expr()?; 378 | Ok(Some(Expr::Not(Box::new(expr)))) 379 | }, 380 | Some(&Token::AllOf) => { 381 | self.iter.next(); 382 | Ok(Some(Expr::AllOf(self.multiple_exprs()?))) 383 | }, 384 | Some(&Token::AnyOf) => { 385 | self.iter.next(); 386 | Ok(Some(Expr::AnyOf(self.multiple_exprs()?))) 387 | }, 388 | 389 | Some(&Token::BothSaem) => x_of!(Expr::BothSaem), 390 | Some(&Token::Diffrint) => x_of!(Expr::Diffrint), 391 | 392 | Some(&Token::Smoosh) => { 393 | self.iter.next(); 394 | Ok(Some(Expr::Smoosh(self.multiple_exprs()?))) 395 | }, 396 | _ => Ok(None) 397 | } 398 | } 399 | } 400 | 401 | /// Convenience function for reading all AST from `input` 402 | pub fn parse>(input: I) -> Result> { 403 | let mut parser = Parser { iter: input.into_iter().peekable() }; 404 | let mut parsed = Vec::new(); 405 | while parser.iter.peek().is_some() { 406 | if let Some(ast) = parser.statement()? { 407 | parsed.push(ast); 408 | } 409 | } 410 | Ok(parsed) 411 | } 412 | 413 | #[cfg(test)] 414 | mod tests { 415 | use super::*; 416 | 417 | #[test] 418 | fn assign() { 419 | assert_eq!( 420 | parse(vec![ 421 | Token::IHasA, 422 | Token::Ident("VAR".to_string()), 423 | Token::Itz, 424 | Token::ProduktOf, 425 | Token::SumOf, 426 | Token::Value(Value::Numbr(12)), Token::An, Token::Value(Value::Numbar(5.0)), 427 | Token::An, 428 | Token::Value(Value::Numbr(10)) 429 | ]).unwrap(), 430 | &[AST::IHasA("VAR".to_string(), Expr::ProduktOf( 431 | Box::new(Expr::SumOf( 432 | Box::new(Expr::Value(Value::Numbr(12))), 433 | Box::new(Expr::Value(Value::Numbar(5.0))) 434 | )), 435 | Box::new(Expr::Value(Value::Numbr(10))) 436 | ))] 437 | ); 438 | assert_eq!( 439 | parse(vec![ 440 | Token::Ident("VAR".to_string()), 441 | Token::R, 442 | Token::Value(Value::Numbr(12)) 443 | ]).unwrap(), 444 | &[AST::R("VAR".to_string(), Expr::Value(Value::Numbr(12)))] 445 | ); 446 | } 447 | #[test] 448 | fn troofs() { 449 | assert_eq!( 450 | parse(vec![ 451 | Token::AllOf, 452 | Token::BothSaem, Token::Value(Value::Numbr(1)), Token::An, Token::Value(Value::Numbr(1)), 453 | Token::An, 454 | Token::Not, Token::Diffrint, Token::Value(Value::Numbr(2)), Token::An, Token::Value(Value::Numbr(2)) 455 | ]).unwrap(), 456 | &[AST::It(Expr::AllOf(vec![ 457 | Expr::BothSaem(Box::new(Expr::Value(Value::Numbr(1))), Box::new(Expr::Value(Value::Numbr(1)))), 458 | Expr::Not(Box::new( 459 | Expr::Diffrint(Box::new(Expr::Value(Value::Numbr(2))), Box::new(Expr::Value(Value::Numbr(2)))) 460 | )) 461 | ]))] 462 | ) 463 | } 464 | #[test] 465 | fn nested_orlys() { 466 | assert_eq!( 467 | parse(vec![ 468 | Token::Value(Value::Troof(true)), Token::Separator, 469 | Token::ORly, Token::Separator, 470 | Token::YaRly, Token::Separator, 471 | Token::Value(Value::Numbr(1)), Token::Separator, 472 | Token::Value(Value::Numbr(3)), Token::Separator, 473 | Token::Mebbe, Token::Value(Value::Troof(false)), Token::Separator, 474 | Token::Value(Value::Numbr(3)), Token::Separator, 475 | Token::NoWai, Token::Separator, 476 | Token::Value(Value::Troof(true)), Token::Separator, 477 | Token::ORly, Token::Separator, 478 | Token::YaRly, Token::Separator, 479 | Token::Value(Value::Numbr(7)), Token::Separator, 480 | Token::Oic, Token::Separator, 481 | Token::Oic 482 | ]).unwrap(), 483 | &[AST::It(Expr::Value(Value::Troof(true))), 484 | AST::ORly( 485 | vec![AST::It(Expr::Value(Value::Numbr(1))), 486 | AST::It(Expr::Value(Value::Numbr(3)))], 487 | vec![(Expr::Value(Value::Troof(false)), 488 | vec![AST::It(Expr::Value(Value::Numbr(3)))])], 489 | vec![AST::It(Expr::Value(Value::Troof(true))), 490 | AST::ORly( 491 | vec![AST::It(Expr::Value(Value::Numbr(7)))], 492 | Vec::new(), 493 | Vec::new() 494 | )])] 495 | ); 496 | } 497 | #[test] 498 | fn wtf() { 499 | assert_eq!( 500 | parse(vec![ 501 | Token::SumOf, Token::Value(Value::Numbr(1)), Token::An, Token::Value(Value::Numbr(3)), Token::Separator, 502 | Token::Wtf, Token::Separator, 503 | Token::Omg, Token::Value(Value::Numbr(1)), Token::Separator, 504 | Token::Visible, Token::Value(Value::Yarn("WHAT, NO".to_string())), Token::Separator, 505 | Token::Omg, Token::Value(Value::Numbr(2)), Token::Separator, 506 | Token::Omg, Token::Value(Value::Numbr(3)), Token::Separator, 507 | Token::Visible, Token::Value(Value::Yarn("R U STUPID?".to_string())), Token::Separator, 508 | Token::Gtfo, Token::Separator, 509 | Token::Omg, Token::Value(Value::Numbr(4)), Token::Separator, 510 | Token::Visible, Token::Value(Value::Yarn("CORREC!".to_string())), Token::Separator, 511 | Token::Gtfo, Token::Separator, 512 | Token::OmgWtf, Token::Separator, 513 | Token::Visible, Token::Value(Value::Yarn("IDFK".to_string())), Token::Separator, 514 | Token::Gtfo, Token::Separator, 515 | Token::Oic 516 | ]).unwrap(), 517 | &[AST::It(Expr::SumOf(Box::new(Expr::Value(Value::Numbr(1))), Box::new(Expr::Value(Value::Numbr(3))))), 518 | AST::Wtf( 519 | vec![(Expr::Value(Value::Numbr(1)), 520 | vec![AST::Visible(vec![Expr::Value(Value::Yarn("WHAT, NO".to_string()))], true)]), 521 | (Expr::Value(Value::Numbr(2)), vec![]), 522 | (Expr::Value(Value::Numbr(3)), 523 | vec![AST::Visible(vec![Expr::Value(Value::Yarn("R U STUPID?".to_string()))], true), AST::Gtfo]), 524 | (Expr::Value(Value::Numbr(4)), 525 | vec![AST::Visible(vec![Expr::Value(Value::Yarn("CORREC!".to_string()))], true), AST::Gtfo])], 526 | vec![AST::Visible(vec![Expr::Value(Value::Yarn("IDFK".to_string()))], true), 527 | AST::Gtfo] 528 | )] 529 | ); 530 | } 531 | #[test] 532 | fn im_in_yr() { 533 | assert_eq!( 534 | parse(vec![ 535 | Token::ImInYr, Token::Ident("LOOP".to_string()), Token::Uppin, Token::Yr, Token::Ident("VAR".to_string()), 536 | Token::Til, Token::BothSaem, Token::Ident("VAR".to_string()), Token::An, Token::Value(Value::Numbr(5)), 537 | Token::Separator, 538 | Token::Visible, Token::Ident("VAR".to_string()), Token::Separator, 539 | Token::ImOuttaYr, Token::Ident("LOOP".to_string()) 540 | ]).unwrap(), 541 | &[AST::ImInYr(Operation::Uppin, String::from("VAR"), 542 | Some(Expr::Not(Box::new(Expr::BothSaem( 543 | Box::new(Expr::Var("VAR".to_string())), 544 | Box::new(Expr::Value(Value::Numbr(5))))))), 545 | vec![AST::Visible(vec![Expr::Var("VAR".to_string())], true)])] 546 | ); 547 | } 548 | #[test] 549 | fn inf_loop() { 550 | assert_eq!( 551 | parse(vec![ 552 | Token::ImInYr, Token::Ident("LOOP".to_string()), Token::Uppin, Token::Yr, Token::Ident("VAR".to_string()), 553 | Token::Separator, 554 | Token::Visible, Token::Ident("VAR".to_string()), Token::Separator, 555 | Token::ImOuttaYr, Token::Ident("LOOP".to_string()) 556 | ]).unwrap(), 557 | &[AST::ImInYr(Operation::Uppin, String::from("VAR"), None, 558 | vec![AST::Visible(vec![Expr::Var("VAR".to_string())], true)])] 559 | ); 560 | assert_eq!( 561 | parse(vec![ 562 | Token::ImInYr, Token::Ident("LOOP".to_string()), 563 | Token::IIz, Token::Ident("CHANGIN".to_string()), Token::Yr, Token::Ident("VAR".to_string()), Token::Mkay, 564 | Token::Separator, 565 | Token::Visible, Token::Ident("VAR".to_string()), Token::Separator, 566 | Token::ImOuttaYr, Token::Ident("LOOP".to_string()) 567 | ]).unwrap(), 568 | &[AST::ImInYr(Operation::IIz("CHANGIN".to_string()), String::from("VAR"), None, 569 | vec![AST::Visible(vec![Expr::Var("VAR".to_string())], true)])] 570 | ); 571 | } 572 | #[test] 573 | fn how_iz_i() { 574 | assert_eq!( 575 | parse(vec![ 576 | Token::HowIzI, Token::Ident("PRINTING".to_string()), Token::Yr, Token::Ident("VAR".to_string()), 577 | Token::Separator, 578 | Token::Visible, Token::Ident("VAR".to_string()), Token::Separator, 579 | Token::IfUSaySo 580 | ]).unwrap(), 581 | &[AST::HowIzI("PRINTING".to_string(), vec!["VAR".to_string()], 582 | vec![AST::Visible(vec![Expr::Var("VAR".to_string())], true)])] 583 | ); 584 | } 585 | #[test] 586 | fn i_iz() { 587 | assert_eq!( 588 | parse(vec![ 589 | Token::IIz, Token::Ident("PRINTING".to_string()), Token::Yr, 590 | Token::Value(Value::Yarn("TEST".to_string())), Token::An, Token::Yr, 591 | Token::Value(Value::Yarn("TEST 2".to_string())), Token::Mkay 592 | ]).unwrap(), 593 | &[AST::It(Expr::IIz("PRINTING".to_string(), 594 | vec![Expr::Value(Value::Yarn("TEST".to_string())), 595 | Expr::Value(Value::Yarn("TEST 2".to_string()))]))] 596 | ); 597 | } 598 | } 599 | -------------------------------------------------------------------------------- /src/tokenizer.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | char as stdchar, 3 | iter::Peekable, 4 | result::Result as StdResult 5 | }; 6 | use types::{Interpolate, Value}; 7 | use unic_ucd_name::Name as UnicName; 8 | 9 | #[derive(Debug, Fail)] 10 | pub enum Error { 11 | #[fail(display = "invalid character in identifier: {}", _0)] 12 | InvalidIdent(char), 13 | #[fail(display = "invalid characters in interpolation: {:?}", _0)] 14 | InvalidInterpolation(String), 15 | #[fail(display = "invalid number: {:?}", _0)] 16 | InvalidNumber(String), 17 | #[fail(display = "invalid unicode character: {}", _0)] 18 | InvalidUnicode(String), 19 | #[fail(display = "unclosed comment")] 20 | UnclosedComment, 21 | #[fail(display = "unclosed interpolation in string")] 22 | UnclosedInterpolation, 23 | #[fail(display = "unclosed string")] 24 | UnclosedString, 25 | #[fail(display = "unexpected end of file")] 26 | UnexpectedEOF, 27 | #[fail(display = "unknown escape character: {}", _0)] 28 | UnknownEscape(char), 29 | #[fail(display = "unknown token")] 30 | UnknownToken, 31 | } 32 | 33 | type Result = StdResult; 34 | 35 | #[derive(Clone, Debug, PartialEq)] 36 | pub enum Token { 37 | It, 38 | Ident(String), 39 | Value(Value), 40 | 41 | Hai, 42 | KThxBye, 43 | Separator, 44 | 45 | IHasA, 46 | Itz, 47 | R, 48 | 49 | SumOf, 50 | DiffOf, 51 | ProduktOf, 52 | QuoshuntOf, 53 | ModOf, 54 | BiggrOf, 55 | SmallrOf, 56 | 57 | BothOf, 58 | EitherOf, 59 | WonOf, 60 | Not, 61 | AllOf, 62 | AnyOf, 63 | BothSaem, 64 | Diffrint, 65 | 66 | Smoosh, 67 | An, 68 | Mkay, 69 | 70 | ORly, 71 | YaRly, 72 | Mebbe, 73 | NoWai, 74 | Oic, 75 | 76 | Wtf, 77 | Omg, 78 | OmgWtf, 79 | Gtfo, 80 | 81 | ImInYr, 82 | Uppin, 83 | Nerfin, 84 | Til, 85 | Wile, 86 | ImOuttaYr, 87 | 88 | HowIzI, 89 | Yr, 90 | IfUSaySo, 91 | FoundYr, 92 | IIz, 93 | 94 | Visible, 95 | Exclamation, 96 | Gimmeh 97 | } 98 | 99 | #[derive(Clone)] 100 | pub struct Tokenizer + Clone> { 101 | pub iter: Peekable 102 | } 103 | 104 | fn is_space(c: char) -> bool { 105 | c == ' ' || c == '\t' 106 | } 107 | 108 | impl + Clone> Tokenizer { 109 | fn trim(&mut self) { 110 | loop { 111 | match self.iter.peek().cloned() { 112 | Some(c) if is_space(c) => { self.iter.next(); }, 113 | _ => break 114 | } 115 | } 116 | } 117 | fn peek(&mut self) -> Option { 118 | self.trim(); 119 | self.iter.peek().cloned() 120 | } 121 | fn word(&mut self) -> String { 122 | let mut word = String::new(); 123 | loop { 124 | match self.iter.peek().cloned() { 125 | Some(c) if is_space(c) => { 126 | self.trim(); 127 | return word; 128 | }, 129 | None | Some('\n') | Some(',') => return word, 130 | Some(c) => { 131 | self.iter.next(); 132 | word.push(c); 133 | } 134 | } 135 | } 136 | } 137 | /// Read one token from the input 138 | pub fn next(&mut self) -> Result> { 139 | let c = match self.peek() { 140 | Some(c) => c, 141 | None => return Ok(None) 142 | }; 143 | if c == '"' { 144 | fn read_until>(iter: &mut I, c: char) -> Result { 145 | let mut string = String::new(); 146 | loop { 147 | match iter.next() { 148 | Some('"') => return Err(Error::UnclosedInterpolation), 149 | None => return Err(Error::UnclosedString), 150 | 151 | Some(c2) if c == c2 => break, 152 | Some(c) => string.push(c) 153 | } 154 | } 155 | Ok(string) 156 | } 157 | self.iter.next(); // leading " 158 | let mut interpolated = Vec::new(); 159 | let mut string = String::new(); 160 | while let Some(c) = self.iter.next() { 161 | if c == ':' { 162 | match self.iter.next() { 163 | Some(')') => string.push('\n'), 164 | Some('>') => string.push('\t'), 165 | Some('o') => string.push('\x07'), 166 | Some('"') => string.push('"'), 167 | Some(':') => string.push(':'), 168 | Some('(') => { 169 | let mut hex = read_until(&mut self.iter, ')')?; 170 | let num = match u32::from_str_radix(&hex, 16) { 171 | Ok(num) => num, 172 | Err(_) => return Err(Error::InvalidNumber(hex)) 173 | }; 174 | match stdchar::from_u32(num) { 175 | Some(c) => string.push(c), 176 | None => return Err(Error::InvalidUnicode(hex)) 177 | } 178 | }, 179 | Some('{') => { 180 | let mut var = read_until(&mut self.iter, '}')?; 181 | match var.chars().next() { 182 | None | 183 | Some('0'...'9') => return Err(Error::InvalidInterpolation(var)), 184 | _ => () 185 | } 186 | if !var.chars().all(|c| 187 | (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 188 | (c >= '0' && c <= '9') || c == '_') { 189 | return Err(Error::InvalidInterpolation(var)); 190 | } 191 | if !string.is_empty() { 192 | interpolated.push(Interpolate::Str(string)); 193 | } 194 | interpolated.push(Interpolate::Var(var)); 195 | string = String::new(); 196 | }, 197 | Some('[') => { 198 | let mut name = read_until(&mut self.iter, ']')?.to_uppercase(); 199 | let mut unicode = None; 200 | for c in chars!(..) { 201 | if UnicName::of(c) 202 | .map(|n| n.to_string().to_uppercase() == name) 203 | .unwrap_or(false) { 204 | unicode = Some(c); 205 | break; 206 | } 207 | } 208 | match unicode { 209 | Some(c) => string.push(c), 210 | None => return Err(Error::InvalidUnicode(name)) 211 | } 212 | }, 213 | Some(c) => return Err(Error::UnknownEscape(c)), 214 | None => return Err(Error::UnclosedString) 215 | }; 216 | continue; 217 | } else if c == '"' { 218 | break; 219 | } 220 | string.push(c); 221 | } 222 | if interpolated.is_empty() { 223 | return Ok(Some(Token::Value(Value::Yarn(string)))); 224 | } else { 225 | if !string.is_empty() { 226 | interpolated.push(Interpolate::Str(string)); 227 | } 228 | return Ok(Some(Token::Value(Value::YarnRaw(interpolated)))); 229 | } 230 | } else if c == '\n' || c == ',' { 231 | self.iter.next(); 232 | return Ok(Some(Token::Separator)); 233 | } 234 | 235 | let word = self.word(); 236 | match &*word { 237 | "HAI" => return Ok(Some(Token::Hai)), 238 | "KTHXBYE" => return Ok(Some(Token::KThxBye)), 239 | "BTW" => { 240 | loop { 241 | match self.iter.next() { 242 | Some('\n') | None => break, 243 | _ => () 244 | } 245 | } 246 | return self.next(); 247 | }, 248 | "OBTW" => { 249 | loop { 250 | match self.peek() { 251 | None => return Err(Error::UnclosedComment), 252 | Some('T') => { 253 | if self.word() == "TLDR" { 254 | return self.next(); 255 | } else { 256 | self.iter.next(); 257 | } 258 | }, 259 | _ => { self.iter.next(); }, 260 | } 261 | } 262 | }, 263 | "WIN" => return Ok(Some(Token::Value(Value::Troof(true)))), 264 | "FAIL" => return Ok(Some(Token::Value(Value::Troof(false)))), 265 | "IT" => return Ok(Some(Token::It)), 266 | "I" => { 267 | let mut clone = self.clone(); 268 | match &*clone.word() { 269 | "HAS" => if clone.word() == "A" { 270 | *self = clone; 271 | return Ok(Some(Token::IHasA)); 272 | }, 273 | "IZ" => { 274 | *self = clone; 275 | return Ok(Some(Token::IIz)); 276 | }, 277 | _ => () 278 | } 279 | }, 280 | "ITZ" => return Ok(Some(Token::Itz)), 281 | "R" => return Ok(Some(Token::R)), 282 | "SUM" | "DIFF" | "PRODUKT" | "QUOSHUNT" | "MOD" | "BIGGR" | "SMALLR" | 283 | "BOTH" | "EITHER" | "WON" | "ALL" | "ANY" => { 284 | let mut clone = self.clone(); 285 | match &*clone.word() { 286 | "OF" => { 287 | *self = clone; 288 | return Ok(Some(match &*word { 289 | "SUM" => Token::SumOf, 290 | "DIFF" => Token::DiffOf, 291 | "PRODUKT" => Token::ProduktOf, 292 | "QUOSHUNT" => Token::QuoshuntOf, 293 | "MOD" => Token::ModOf, 294 | "BIGGR" => Token::BiggrOf, 295 | "SMALLR" => Token::SmallrOf, 296 | 297 | "BOTH" => Token::BothOf, 298 | "EITHER" => Token::EitherOf, 299 | "WON" => Token::WonOf, 300 | "ALL" => Token::AllOf, 301 | "ANY" => Token::AnyOf, 302 | 303 | _ => unreachable!() 304 | })); 305 | }, 306 | "SAEM" if word == "BOTH" => { 307 | *self = clone; 308 | return Ok(Some(Token::BothSaem)); 309 | }, 310 | _ => () 311 | } 312 | }, 313 | "NOT" => return Ok(Some(Token::Not)), 314 | "DIFFRINT" => return Ok(Some(Token::Diffrint)), 315 | "SMOOSH" => return Ok(Some(Token::Smoosh)), 316 | "AN" => return Ok(Some(Token::An)), 317 | "MKAY" => return Ok(Some(Token::Mkay)), 318 | "O" => { 319 | let mut clone = self.clone(); 320 | if clone.word() == "RLY?" { 321 | *self = clone; 322 | return Ok(Some(Token::ORly)); 323 | } 324 | }, 325 | "YA" => { 326 | let mut clone = self.clone(); 327 | if clone.word() == "RLY" { 328 | *self = clone; 329 | return Ok(Some(Token::YaRly)); 330 | } 331 | }, 332 | "MEBBE" => return Ok(Some(Token::Mebbe)), 333 | "NO" => { 334 | let mut clone = self.clone(); 335 | if clone.word() == "WAI" { 336 | *self = clone; 337 | return Ok(Some(Token::NoWai)); 338 | } 339 | }, 340 | "OIC" => return Ok(Some(Token::Oic)), 341 | "WTF?" => return Ok(Some(Token::Wtf)), 342 | "OMG" => return Ok(Some(Token::Omg)), 343 | "OMGWTF" => return Ok(Some(Token::OmgWtf)), 344 | "GTFO" => return Ok(Some(Token::Gtfo)), 345 | "IM" => { 346 | let mut clone = self.clone(); 347 | match &*clone.word() { 348 | "IN" => if clone.word() == "YR" { 349 | *self = clone; 350 | return Ok(Some(Token::ImInYr)); 351 | }, 352 | "OUTTA" => if clone.word() == "YR" { 353 | *self = clone; 354 | return Ok(Some(Token::ImOuttaYr)); 355 | }, 356 | _ => () 357 | } 358 | }, 359 | "UPPIN" => return Ok(Some(Token::Uppin)), 360 | "NERFIN" => return Ok(Some(Token::Nerfin)), 361 | "YR" => return Ok(Some(Token::Yr)), 362 | "TIL" => return Ok(Some(Token::Til)), 363 | "WILE" => return Ok(Some(Token::Wile)), 364 | "HOW" => { 365 | let mut clone = self.clone(); 366 | if clone.word() == "IZ" { 367 | if clone.word() == "I" { 368 | *self = clone; 369 | return Ok(Some(Token::HowIzI)); 370 | } 371 | } 372 | }, 373 | "IF" => { 374 | let mut clone = self.clone(); 375 | if clone.word() == "U" { 376 | if clone.word() == "SAY" { 377 | if clone.word() == "SO" { 378 | *self = clone; 379 | return Ok(Some(Token::IfUSaySo)); 380 | } 381 | } 382 | } 383 | }, 384 | "FOUND" => { 385 | let mut clone = self.clone(); 386 | if clone.word() == "YR" { 387 | *self = clone; 388 | return Ok(Some(Token::FoundYr)); 389 | } 390 | }, 391 | "VISIBLE" => return Ok(Some(Token::Visible)), 392 | "!" => return Ok(Some(Token::Exclamation)), 393 | "GIMMEH" => return Ok(Some(Token::Gimmeh)), 394 | _ => () 395 | } 396 | 397 | match c { 398 | 'a'...'z' | 399 | 'A'...'Z' | 400 | '_' => { 401 | for c in word.chars() { 402 | match c { 403 | 'a'...'z' | 404 | 'A'...'Z' | 405 | '0'...'9' | 406 | '_' => (), 407 | c => return Err(Error::InvalidIdent(c)) 408 | } 409 | } 410 | return Ok(Some(Token::Ident(word))); 411 | }, 412 | '-' | '0'...'9' => { 413 | if let Ok(num) = word.parse::() { 414 | return Ok(Some(Token::Value(Value::Numbr(num)))); 415 | } else if let Ok(num) = word.parse::() { 416 | return Ok(Some(Token::Value(Value::Numbar(num)))); 417 | } 418 | return Err(Error::InvalidNumber(word)); 419 | }, 420 | _ => () 421 | } 422 | 423 | Err(Error::UnknownToken) 424 | } 425 | } 426 | 427 | /// Convenience function for reading all tokens from `input` 428 | pub fn tokenize + Clone>(input: I) -> Result> { 429 | let mut tokenizer = Tokenizer { iter: input.peekable() }; 430 | let mut tokens = Vec::new(); 431 | while let Some(token) = tokenizer.next()? { 432 | tokens.push(token); 433 | } 434 | Ok(tokens) 435 | } 436 | /// Convenience function for reading all tokens from `input` from a string 437 | pub fn tokenize_str(input: &str) -> Result> { 438 | tokenize(input.chars()) 439 | } 440 | 441 | #[cfg(test)] 442 | mod tests { 443 | use super::*; 444 | #[test] 445 | fn yarns() { 446 | assert_eq!( 447 | tokenize_str(r#" "Hello World :) How are you :>? I'm:: :"fine:"" "#).unwrap(), 448 | &[Token::Value(Value::Yarn("Hello World \n How are you \t? I'm: \"fine\"".to_string()))] 449 | ); 450 | } 451 | #[test] 452 | fn interpolation() { 453 | assert_eq!( 454 | tokenize_str(r#" ":[SNOWMAN] is :(1F60A). He says:: :{something}" "#).unwrap(), 455 | &[Token::Value(Value::YarnRaw(vec![ 456 | Interpolate::Str("☃ is 😊. He says: ".to_string()), 457 | Interpolate::Var("something".to_string()) 458 | ]))] 459 | ); 460 | } 461 | #[test] 462 | fn primitives() { 463 | assert_eq!( 464 | tokenize_str("1, -5, 2.3, WIN, FAIL").unwrap(), 465 | &[ 466 | Token::Value(Value::Numbr(1)), Token::Separator, 467 | Token::Value(Value::Numbr(-5)), Token::Separator, 468 | Token::Value(Value::Numbar(2.3)), Token::Separator, 469 | Token::Value(Value::Troof(true)), Token::Separator, 470 | Token::Value(Value::Troof(false)) 471 | ] 472 | ); 473 | } 474 | #[test] 475 | fn assign() { 476 | assert_eq!( 477 | tokenize_str("I HAS A VAR ITZ 12 BTW this is a comment").unwrap(), 478 | &[Token::IHasA, Token::Ident("VAR".to_string()), Token::Itz, Token::Value(Value::Numbr(12))] 479 | ); 480 | assert_eq!( 481 | tokenize_str("VAR R 12").unwrap(), 482 | &[Token::Ident("VAR".to_string()), Token::R, Token::Value(Value::Numbr(12))] 483 | ); 484 | } 485 | #[test] 486 | fn sum_of() { 487 | assert_eq!( 488 | tokenize_str("SUM OF OBTW hi TLDR 2 AN 4").unwrap(), 489 | &[Token::SumOf, Token::Value(Value::Numbr(2)), Token::An, Token::Value(Value::Numbr(4))] 490 | ); 491 | } 492 | #[test] 493 | fn orly() { 494 | assert_eq!( 495 | tokenize_str("\ 496 | BOTH SAEM 1 AN 1, O RLY? 497 | YA RLY, RESULT R \"YES\" 498 | MEBBE BOTH SAEM 1 AN 2, RESULT R \"CLOSE\" 499 | NO WAI, RESULT R \"NO\" 500 | OIC\ 501 | ").unwrap(), 502 | &[ 503 | Token::BothSaem, Token::Value(Value::Numbr(1)), Token::An, Token::Value(Value::Numbr(1)), Token::Separator, 504 | Token::ORly, Token::Separator, 505 | Token::YaRly, Token::Separator, 506 | Token::Ident("RESULT".to_string()), Token::R, Token::Value(Value::Yarn("YES".to_string())), 507 | Token::Separator, 508 | Token::Mebbe, 509 | Token::BothSaem, Token::Value(Value::Numbr(1)), Token::An, Token::Value(Value::Numbr(2)), 510 | Token::Separator, 511 | Token::Ident("RESULT".to_string()), Token::R, Token::Value(Value::Yarn("CLOSE".to_string())), 512 | Token::Separator, 513 | Token::NoWai, Token::Separator, 514 | Token::Ident("RESULT".to_string()), Token::R, Token::Value(Value::Yarn("NO".to_string())), 515 | Token::Separator, 516 | Token::Oic 517 | ] 518 | ) 519 | } 520 | #[test] 521 | fn wtf() { 522 | assert_eq!( 523 | tokenize_str("\ 524 | SUM OF 1 AN 3 525 | WTF? 526 | OMG 1 527 | VISIBLE \"WHAT, NO\" 528 | OMG 2 529 | OMG 3 530 | VISIBLE \"R U STUPID?\" 531 | GTFO 532 | OMG 4 533 | VISIBLE \"CORREC!\" 534 | GTFO 535 | OMGWTF 536 | VISIBLE \"IDFK\" 537 | GTFO 538 | OIC\ 539 | ").unwrap(), 540 | vec![ 541 | Token::SumOf, Token::Value(Value::Numbr(1)), Token::An, Token::Value(Value::Numbr(3)), Token::Separator, 542 | Token::Wtf, Token::Separator, 543 | Token::Omg, Token::Value(Value::Numbr(1)), Token::Separator, 544 | Token::Visible, Token::Value(Value::Yarn("WHAT, NO".to_string())), Token::Separator, 545 | Token::Omg, Token::Value(Value::Numbr(2)), Token::Separator, 546 | Token::Omg, Token::Value(Value::Numbr(3)), Token::Separator, 547 | Token::Visible, Token::Value(Value::Yarn("R U STUPID?".to_string())), Token::Separator, 548 | Token::Gtfo, Token::Separator, 549 | Token::Omg, Token::Value(Value::Numbr(4)), Token::Separator, 550 | Token::Visible, Token::Value(Value::Yarn("CORREC!".to_string())), Token::Separator, 551 | Token::Gtfo, Token::Separator, 552 | Token::OmgWtf, Token::Separator, 553 | Token::Visible, Token::Value(Value::Yarn("IDFK".to_string())), Token::Separator, 554 | Token::Gtfo, Token::Separator, 555 | Token::Oic 556 | ] 557 | ); 558 | } 559 | #[test] 560 | fn loops() { 561 | assert_eq!( 562 | tokenize_str("\ 563 | IM IN YR LOOP UPPIN YR VAR TIL BOTH SAEM VAR AN 5 564 | VISIBLE VAR 565 | IM OUTTA YR LOOP\ 566 | ").unwrap(), 567 | &[Token::ImInYr, Token::Ident("LOOP".to_string()), Token::Uppin, Token::Yr, Token::Ident("VAR".to_string()), 568 | Token::Til, Token::BothSaem, Token::Ident("VAR".to_string()), Token::An, Token::Value(Value::Numbr(5)), 569 | Token::Separator, 570 | Token::Visible, Token::Ident("VAR".to_string()), Token::Separator, 571 | Token::ImOuttaYr, Token::Ident("LOOP".to_string())] 572 | ); 573 | } 574 | #[test] 575 | fn functions() { 576 | assert_eq!( 577 | tokenize_str("\ 578 | HOW IZ I SCREAMING YR STUFF 579 | VISIBLE STUFF \"!\" 580 | IF U SAY SO 581 | 582 | I IZ SCREAMING \"STUFF\" MKAY\ 583 | ").unwrap(), 584 | &[Token::HowIzI, Token::Ident("SCREAMING".to_string()), Token::Yr, Token::Ident("STUFF".to_string()), 585 | Token::Separator, 586 | Token::Visible, Token::Ident("STUFF".to_string()), Token::Value(Value::Yarn("!".to_string())), 587 | Token::Separator, 588 | Token::IfUSaySo, Token::Separator, 589 | Token::Separator, 590 | Token::IIz, Token::Ident("SCREAMING".to_string()), Token::Value(Value::Yarn("STUFF".to_string())), Token::Mkay] 591 | ); 592 | } 593 | } 594 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | #[derive(Clone, Debug, PartialEq)] 4 | pub enum Interpolate { 5 | Str(String), 6 | Var(String) 7 | } 8 | 9 | #[derive(Clone, Debug, PartialEq)] 10 | pub enum Value { 11 | Noob, 12 | Yarn(String), 13 | YarnRaw(Vec), 14 | Numbr(i64), 15 | Numbar(f64), 16 | Troof(bool) 17 | } 18 | impl Default for Value { 19 | fn default() -> Self { 20 | Value::Noob 21 | } 22 | } 23 | impl Value { 24 | pub fn cast_yarn(&self) -> Option> { 25 | match *self { 26 | Value::Noob => None, 27 | Value::Yarn(ref inner) => Some(Cow::Borrowed(inner)), 28 | Value::YarnRaw(_) => panic!("yarn not interpolated yet"), 29 | Value::Numbr(n) => Some(Cow::Owned(n.to_string())), 30 | Value::Numbar(n) => Some(Cow::Owned(n.to_string())), 31 | Value::Troof(true) => Some(Cow::Borrowed("WIN")), 32 | Value::Troof(false) => Some(Cow::Borrowed("FAIL")) 33 | } 34 | } 35 | pub fn cast_numbr(&self) -> Option { 36 | match *self { 37 | Value::Noob => None, 38 | Value::Yarn(ref inner) => Some(inner.parse().unwrap_or(0)), 39 | Value::YarnRaw(_) => panic!("yarn not interpolated yet"), 40 | Value::Numbr(n) => Some(n), 41 | Value::Numbar(n) => Some(n as i64), 42 | Value::Troof(b) => Some(b as i64) 43 | } 44 | } 45 | pub fn cast_numbar(&self) -> Option { 46 | match *self { 47 | Value::Noob => None, 48 | Value::Yarn(ref inner) => Some(inner.parse().unwrap_or(0.0)), 49 | Value::YarnRaw(_) => panic!("yarn not interpolated yet"), 50 | Value::Numbr(n) => Some(n as f64), 51 | Value::Numbar(n) => Some(n), 52 | Value::Troof(b) => Some(b as i64 as f64) 53 | } 54 | } 55 | pub fn is_numbr(&self) -> bool { 56 | match *self { 57 | Value::Yarn(ref inner) => inner.parse::().is_ok(), 58 | Value::YarnRaw(_) => panic!("yarn not interpolated yet"), 59 | Value::Numbr(_) => true, 60 | _ => false 61 | } 62 | } 63 | pub fn is_numbar(&self) -> bool { 64 | match *self { 65 | Value::Yarn(ref inner) => !self.is_numbr() && inner.parse::().is_ok(), 66 | Value::YarnRaw(_) => panic!("yarn not interpolated yet"), 67 | Value::Numbar(_) => true, 68 | _ => false 69 | } 70 | } 71 | pub fn cast_troof(&self) -> bool { 72 | match *self { 73 | Value::Noob => false, 74 | Value::Yarn(ref inner) => inner.is_empty(), 75 | Value::YarnRaw(_) => panic!("yarn not interpolated yet"), 76 | Value::Numbr(n) => n == 0, 77 | Value::Numbar(n) => n == 0.0, 78 | Value::Troof(b) => b 79 | } 80 | } 81 | /// Check if the values are equal (used by the BOTH SAEM operator). 82 | /// This does auto-coercion, unlike the PartialEq trait implementation. 83 | pub fn equals(&self, other: &Self) -> bool { 84 | if let Value::YarnRaw(_) = *self { panic!("yarn not interpolated yet"); } 85 | if let Value::YarnRaw(_) = *other { panic!("yarn not interpolated yet"); } 86 | 87 | if let Value::Noob = *self { 88 | if let Value::Noob = *other { 89 | return true; 90 | } 91 | } 92 | if let Value::Troof(b) = *self { 93 | if let Value::Troof(b2) = *other { 94 | return b == b2; 95 | } 96 | } 97 | if self.is_numbar() || other.is_numbar() { 98 | self.cast_numbar() == other.cast_numbar() 99 | } else if self.is_numbr() || other.is_numbr() { 100 | self.cast_numbr() == other.cast_numbr() 101 | } else { 102 | self.cast_yarn() == other.cast_yarn() 103 | } 104 | } 105 | /// Interpolate a YARN value at evaluation time. 106 | /// This does nothing if it's not a YARN or if it already has 107 | /// been interpolated. 108 | /// Returns any missing variable in the interpolation, if any. 109 | pub fn interpolate(&mut self, lookup: F) -> Option 110 | where F: Fn(&str) -> Option 111 | { 112 | let mut string_ = None; 113 | if let Value::YarnRaw(ref parts) = *self { 114 | let mut capacity = 0; 115 | for part in parts { 116 | if let Interpolate::Str(ref part) = *part { 117 | capacity += part.len(); 118 | } 119 | } 120 | let mut string = String::with_capacity(capacity); 121 | for part in parts { 122 | let mut _val = None; 123 | string.push_str(&match *part { 124 | Interpolate::Str(ref part) => Cow::Borrowed(&**part), 125 | Interpolate::Var(ref var) => match lookup(var).and_then(|val| { 126 | _val = Some(val); 127 | _val.as_ref().unwrap().cast_yarn() 128 | }) { 129 | Some(val) => val, 130 | None => return Some(var.clone()) 131 | } 132 | }); 133 | } 134 | string_ = Some(string); 135 | } 136 | if let Some(string) = string_ { 137 | *self = Value::Yarn(string); 138 | } 139 | None 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /tests/callback.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | OBTW 4 | Testing rust callbacks. 5 | From rust code, a function called "LOWERIN" that converts a string to lowercase is defined. 6 | See Eval.bind_func 7 | TLDR 8 | VISIBLE I IZ LOWERIN YR "TEST" MKAY 9 | 10 | KTHXBYE 11 | -------------------------------------------------------------------------------- /tests/fac.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | HOW IZ I FAC YR N 4 | BOTH SAEM N AN 0, O RLY? 5 | YA RLY, FOUND YR 1 6 | NO WAI, FOUND YR PRODUKT OF N AN I IZ FAC YR DIFF OF N AN 1 MKAY 7 | OIC 8 | IF U SAY SO 9 | 10 | VISIBLE I IZ FAC YR 5 MKAY 11 | 12 | KTHXBYE 13 | -------------------------------------------------------------------------------- /tests/fail/divide-by-zero.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | VISIBLE QUOSHUNT OF 1 AN 0 4 | 5 | KTHXBYE 6 | -------------------------------------------------------------------------------- /tests/fail/stack-overflow.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | HOW IZ I TESTING 4 | I IZ TESTING MKAY 5 | IF U SAY SO 6 | 7 | I IZ TESTING MKAY 8 | 9 | KTHXBYE 10 | -------------------------------------------------------------------------------- /tests/function-ordering.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | I IZ PINGING YR 5 MKAY 4 | 5 | HOW IZ I PINGING YR BALL 6 | VISIBLE "PING :{BALL}" 7 | BALL R DIFF OF BALL AN 1 8 | DIFFRINT BALL AN 0, O RLY? 9 | YA RLY, I IZ PONGING YR BALL MKAY 10 | OIC 11 | IF U SAY SO 12 | 13 | HOW IZ I PONGING YR BALL 14 | VISIBLE "PONG :{BALL}" 15 | BALL R DIFF OF BALL AN 1 16 | DIFFRINT BALL AN 0, O RLY? 17 | YA RLY, I IZ PINGING YR BALL MKAY 18 | OIC 19 | IF U SAY SO 20 | 21 | KTHXBYE 22 | -------------------------------------------------------------------------------- /tests/generate-quine.rb: -------------------------------------------------------------------------------- 1 | def quote(input) 2 | result = "" 3 | for i in 0...input.length 4 | c = input[i] 5 | case c 6 | when "\n" then 7 | result += "\" NEWLINE \"" 8 | when '"' then 9 | result += "\" QUOTE \"" 10 | when ':' then 11 | result += "\" ESCAPE \"" 12 | else 13 | result += c 14 | end 15 | end 16 | result 17 | end 18 | def quote_yarn(input) 19 | result = "" 20 | for i in 0...input.length 21 | c = input[i] 22 | case c 23 | when "\n" then 24 | result += ":)" 25 | when '"' then 26 | result += ":\"" 27 | when ':' then 28 | result += "::" 29 | else 30 | result += c 31 | end 32 | end 33 | result 34 | end 35 | 36 | footer = 'IF U SAY SO 37 | 38 | VISIBLE I IZ HEADER YR ":)" AN YR ":"" AN YR "::" MKAY ! 39 | VISIBLE ":"" ! 40 | VISIBLE I IZ HEADER YR ":" NEWLINE :"" AN YR ":" QUOTE :"" AN YR ":" ESCAPE :"" MKAY ! 41 | VISIBLE ":"" 42 | VISIBLE FOOTER 43 | 44 | KTHXBYE' 45 | header = 'HAI 1.2 46 | 47 | I HAS A FOOTER ITZ "' + quote_yarn(footer) + '" 48 | HOW IZ I HEADER YR NEWLINE AN YR QUOTE AN YR ESCAPE 49 | SMOOSH ' 50 | 51 | print header 52 | print '"' 53 | print quote(header) 54 | puts '"' 55 | puts footer 56 | -------------------------------------------------------------------------------- /tests/implicit-return.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | HOW IZ I RETURNIN YR VAR 4 | VAR 5 | IF U SAY SO 6 | 7 | VISIBLE I IZ RETURNIN YR "hi" MKAY 8 | 9 | KTHXBYE 10 | -------------------------------------------------------------------------------- /tests/int-overflow.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | VISIBLE BOTH SAEM -9223372036854775808 AN SUM OF 9223372036854775807 AN 1 4 | 5 | KTHXBYE 6 | -------------------------------------------------------------------------------- /tests/nan.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | I HAS A NAN ITZ QUOSHUNT OF 0.0 AN 0.0 4 | 5 | VISIBLE NOT BOTH SAEM NAN AN NAN 6 | VISIBLE DIFFRINT NAN AN NAN 7 | 8 | KTHXBYE 9 | -------------------------------------------------------------------------------- /tests/pow.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | HOW IZ I POW YR N AN YR EXP 4 | I HAS A RESULT ITZ 1 5 | IM IN YR LOOP UPPIN YR COUNTR TIL BOTH SAEM COUNTR AN EXP 6 | RESULT R PRODUKT OF RESULT AN N 7 | IM OUTTA YR LOOP 8 | FOUND YR RESULT 9 | IF U SAY SO 10 | 11 | VISIBLE I IZ POW YR 2 AN YR 5 MKAY 12 | 13 | KTHXBYE 14 | -------------------------------------------------------------------------------- /tests/quine.lol: -------------------------------------------------------------------------------- 1 | HAI 1.2 2 | 3 | I HAS A FOOTER ITZ "IF U SAY SO:):)VISIBLE I IZ HEADER YR :"::):" AN YR :":::":" AN YR :":::::" MKAY !:)VISIBLE :":::":" !:)VISIBLE I IZ HEADER YR :":::" NEWLINE :::":" AN YR :":::" QUOTE :::":" AN YR :":::" ESCAPE :::":" MKAY !:)VISIBLE :":::":":)VISIBLE FOOTER:):)KTHXBYE" 4 | HOW IZ I HEADER YR NEWLINE AN YR QUOTE AN YR ESCAPE 5 | SMOOSH "HAI 1.2" NEWLINE "" NEWLINE "I HAS A FOOTER ITZ " QUOTE "IF U SAY SO" ESCAPE ")" ESCAPE ")VISIBLE I IZ HEADER YR " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE ")" ESCAPE "" QUOTE " AN YR " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE "" ESCAPE "" QUOTE "" ESCAPE "" QUOTE " AN YR " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE "" ESCAPE "" ESCAPE "" ESCAPE "" QUOTE " MKAY !" ESCAPE ")VISIBLE " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE "" ESCAPE "" QUOTE "" ESCAPE "" QUOTE " !" ESCAPE ")VISIBLE I IZ HEADER YR " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE "" ESCAPE "" QUOTE " NEWLINE " ESCAPE "" ESCAPE "" ESCAPE "" QUOTE "" ESCAPE "" QUOTE " AN YR " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE "" ESCAPE "" QUOTE " QUOTE " ESCAPE "" ESCAPE "" ESCAPE "" QUOTE "" ESCAPE "" QUOTE " AN YR " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE "" ESCAPE "" QUOTE " ESCAPE " ESCAPE "" ESCAPE "" ESCAPE "" QUOTE "" ESCAPE "" QUOTE " MKAY !" ESCAPE ")VISIBLE " ESCAPE "" QUOTE "" ESCAPE "" ESCAPE "" ESCAPE "" QUOTE "" ESCAPE "" QUOTE "" ESCAPE ")VISIBLE FOOTER" ESCAPE ")" ESCAPE ")KTHXBYE" QUOTE "" NEWLINE "HOW IZ I HEADER YR NEWLINE AN YR QUOTE AN YR ESCAPE" NEWLINE " SMOOSH " 6 | IF U SAY SO 7 | 8 | VISIBLE I IZ HEADER YR ":)" AN YR ":"" AN YR "::" MKAY ! 9 | VISIBLE ":"" ! 10 | VISIBLE I IZ HEADER YR ":" NEWLINE :"" AN YR ":" QUOTE :"" AN YR ":" ESCAPE :"" MKAY ! 11 | VISIBLE ":"" 12 | VISIBLE FOOTER 13 | 14 | KTHXBYE 15 | --------------------------------------------------------------------------------