rlox

Run Settings
LanguageRust
Language Version
Run Command
use std::env; use std::io; use std::fs::File; //use std::io::BufReader; use std::io::prelude::*; use std::io::{Error, ErrorKind}; use std::str::Chars; use std::mem; use std::iter::Peekable; macro_rules! void { ( $x:expr) => {{ let _ = $x; }} } #[derive(Debug)] enum TokenType { // single-character tokens LeftParen, RightParen, LeftBrace, RightBrace, Comma, Dot, Minus, Plus, Semicolon, Slash, Star, Colon, // one or two character tokens Bang, BangEqual, Equal, EqualEqual, Greater, GreaterEqual, Less, LessEqual, // literals Identifier, Str, Number, // keywords And, Class, Else, False, Fn, For, If, Nil, Or, Print, Return, Super, TSelf, True, Let, While, EOF } #[derive(Debug)] enum Literal { #[allow(non_camel_case_types)] Number(f64), #[allow(non_camel_case_types)] Str(String), #[allow(non_camel_case_types)] Bool(bool), #[allow(non_camel_case_types)] Nil, } #[derive(Debug)] struct Token { t_type: TokenType, lexeme: String, literal: Literal, line: usize } #[allow(non_snake_case)] fn Token(t_type: TokenType, lexeme: String, literal: Literal, line: usize) -> Token { Token { t_type, lexeme, literal, line } } struct Scanner { source: String, iter: Peekable<Chars<'static>>, builder: String, tokens: Vec<Token>, start: usize, current: usize, line: usize } #[allow(non_snake_case)] fn Scanner(src: String) -> Scanner { let chars = unsafe { mem::transmute(src.chars().peekable()) }; Scanner { source: src, iter: chars, builder: String::new(), tokens: vec![], start: 0, current: 0, line: 1 } } impl Scanner { fn scan_tokens(&mut self) -> (&Vec<Token>, bool) { let mut has_errs = false; while !self.is_end() { self.start = self.current; let res = self.scan_token(); has_errs = has_errs || res; } self.tokens.push(Token(TokenType::EOF, String::new(), Literal::Nil, self.line)); (&self.tokens, has_errs) } fn scan_token(&mut self) -> bool { let c = self.advance(); match c { Some('(') => { self.builder.push('('); self.add_token(TokenType::LeftParen, Literal::Nil); false }, Some(')') => { self.builder.push(')'); self.add_token(TokenType::RightParen, Literal::Nil); false }, Some('{') => { self.builder.push('{'); self.add_token(TokenType::LeftBrace, Literal::Nil); false }, Some('}') => { self.builder.push('}'); self.add_token(TokenType::RightBrace, Literal::Nil); false }, Some(',') => { self.builder.push(','); self.add_token(TokenType::Comma, Literal::Nil); false }, Some('.') => { self.builder.push('.'); self.add_token(TokenType::Dot, Literal::Nil); false }, Some('-') => { self.builder.push('-'); self.add_token(TokenType::Minus, Literal::Nil); false }, Some('+') => { self.builder.push('+'); self.add_token(TokenType::Plus, Literal::Nil); false }, Some(';') => { self.builder.push(';'); self.add_token(TokenType::Semicolon, Literal::Nil); false }, Some('*') => { self.builder.push('*'); self.add_token(TokenType::Star, Literal::Nil); false }, Some('!') => { self.builder.push('!'); let matched = self.match_char('='); self.add_token({if matched {TokenType::BangEqual} else {TokenType::Bang}}, Literal::Nil); false }, Some('=') => { self.builder.push('='); let matched = self.match_char('='); self.add_token({if matched {TokenType::EqualEqual} else {TokenType::Equal}}, Literal::Nil); false }, Some('>') => { self.builder.push('>'); let matched = self.match_char('='); self.add_token({if matched {TokenType::LessEqual} else {TokenType::Less}}, Literal::Nil); false }, Some('<') => { self.builder.push('<'); let matched = self.match_char('='); self.add_token({if matched {TokenType::GreaterEqual} else {TokenType::Greater}}, Literal::Nil); false }, Some('/') => { if self.match_char('/') { void!(self.builder.pop()); while !self.is_end() && self.iter.peek() != Some(&'\n') { void!(self.advance()); } } else { self.builder.push('/'); self.add_token(TokenType::Slash, Literal::Nil); } false }, Some(' ') => { false }, Some('\t') => { false }, Some('\r') => { false }, Some('\n') => { self.line += 1; false }, Some('"') => { self.string() }, Some(ch) => { error(self.line, &format!("Unexpected character: {:?}", ch)) }, //Some(ch) => { self.builder.push(ch); }, // ??? None => { self.current = self.source.len(); false }, } } fn advance(&mut self) -> Option<char> { self.current += 1; self.iter.next() } fn is_end(&self) -> bool { self.current >= self.source.len() } fn add_token(&mut self, t: TokenType, l: Literal) { self.tokens.push(Token(t, self.builder.clone(), l, self.line)); self.builder = String::new(); } fn match_char(&mut self, ch: char) -> bool { if self.is_end() { false } else if self.iter.peek() != Some(&ch) { false } else { self.current += 1; let addch = self.iter.next().unwrap(); self.builder.push(addch); true } } fn string(&mut self) -> bool { while self.iter.peek() != Some(&'"') && !self.is_end() { if self.iter.peek() == Some(&'\n') { self.line += 1; } let c = self.advance(); match c { None => { self.current = self.source.len(); }, Some(ch) => { self.builder.push(ch); }, } } if self.is_end() { error(self.line, "Unterminated String") } else { void!(self.advance()); let val = self.builder.clone(); self.add_token(TokenType::Str, Literal::Str(val)); false } } } fn report(line: usize, location: &str, msg: &str) -> bool { eprintln!("[{}] Error{}: {}", line, location, msg); true } fn error(line: usize, msg: &str) -> bool { report(line, "", msg) } fn run(source: String) -> io::Result<bool> { let mut had_error = false; //for s in source.split(" ").flat_map(|s| s.split("(")).flat_map(|s| s.split(")")) /* for s in source.split(" ") { print!("{} ", s); }*/ let mut scanner = Scanner(source); let tokens = { let (temp, tb) = scanner.scan_tokens(); had_error = tb; temp }; // DEBUG for token in tokens.iter() { println!("{:?}", token); } Ok(had_error) } fn run_file(pfile: &String) -> io::Result<()> { let mut file = File::open(pfile)?; //let mut buf_reader = BufReader::new(file); let mut contents = String::new(); file.read_to_string(&mut contents)?; match run(contents) { Ok(false) => Ok(()), Ok(true) => Err(Error::new(ErrorKind::Other, "Exited with errors")), //TODO replace rust error with custom error type Err(err) => Err(err) } } macro_rules! reader { ($input:expr) => { $input.lock().lines() } } macro_rules! read_line { ($iter:expr) => { $iter.next().unwrap().unwrap() } } fn run_prompt() -> io::Result<()> { let stdin = io::stdin(); let mut reader = reader!(stdin); loop { print!("> "); let next_line = read_line!(reader); run(next_line)?; } } fn main() -> io::Result<()> { let args: Vec<String> = env::args().collect(); eprintln!("{:?}", args); //DEBUG if args.len() > 2 { println!("Usage: rlox [script]"); Ok(()) } else if args.len() == 2 { run_file(&args[1]) } else { run_prompt() } }
print "Hello world";//aa +-*{}()!.!===/ //asdasd
Editor Settings
Theme
Key bindings
Full width
Lines