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