sync'd latest master

This commit is contained in:
russ
2017-10-30 08:08:44 -07:00
parent 18c6892df3
commit e5e58fce98
9 changed files with 479 additions and 27 deletions

View File

@@ -4,12 +4,13 @@ use std::iter::Peekable;
use std::str::Chars;
use std::char;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum LexError {
UnexpectedChar,
MalformedEscapeSequence,
MalformedNumber,
MalformedChar,
Nothing
}
impl Error for LexError {
@@ -19,12 +20,9 @@ impl Error for LexError {
LexError::MalformedEscapeSequence => "Unexpected values in escape sequence",
LexError::MalformedNumber => "Unexpected characters in number",
LexError::MalformedChar => "Char constant not a single character",
LexError::Nothing => "This error is for internal use only"
}
}
fn cause(&self) -> Option<&Error> {
None
}
}
impl fmt::Display for LexError {
@@ -88,6 +86,7 @@ pub enum Stmt {
If(Box<Expr>, Box<Stmt>),
IfElse(Box<Expr>, Box<Stmt>, Box<Stmt>),
While(Box<Expr>, Box<Stmt>),
Loop(Box<Stmt>),
Var(String, Option<Box<Expr>>),
Block(Vec<Stmt>),
Expr(Box<Expr>),
@@ -112,7 +111,7 @@ pub enum Expr {
False,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum Token {
IntConst(i64),
FloatConst(f64),
@@ -126,7 +125,9 @@ pub enum Token {
LSquare,
RSquare,
Plus,
UnaryPlus,
Minus,
UnaryMinus,
Multiply,
Divide,
Semicolon,
@@ -140,6 +141,7 @@ pub enum Token {
If,
Else,
While,
Loop,
LessThan,
GreaterThan,
Bang,
@@ -154,10 +156,98 @@ pub enum Token {
Fn,
Break,
Return,
PlusEquals,
MinusEquals,
LexErr(LexError),
}
impl Token {
// if another operator is after these, it's probably an unary operator
// not sure about fn's name
pub fn is_next_unary(&self) -> bool {
use self::Token::*;
match *self {
LCurly | // (+expr) - is unary
// RCurly | {expr} - expr not unary & is closing
LParen | // {-expr} - is unary
// RParen | (expr) - expr not unary & is closing
LSquare | // [-expr] - is unary
// RSquare | [expr] - expr not unary & is closing
Plus |
UnaryPlus |
Minus |
UnaryMinus |
Multiply |
Divide |
Colon |
Comma |
Period |
Equals |
LessThan |
GreaterThan |
Bang |
LessThanEqual |
GreaterThanEqual |
EqualTo |
NotEqualTo |
Pipe |
Or |
Ampersand |
And |
If |
While |
Return => true,
_ => false,
}
}
#[allow(dead_code)]
pub fn is_bin_op(&self) -> bool {
use self::Token::*;
match *self {
RCurly |
RParen |
RSquare |
Plus |
Minus |
Multiply |
Divide |
Comma |
// Period | <- does period count?
Equals |
LessThan |
GreaterThan |
LessThanEqual |
GreaterThanEqual |
EqualTo |
NotEqualTo |
Pipe |
Or |
Ampersand |
And => true,
_ => false,
}
}
#[allow(dead_code)]
pub fn is_un_op(&self) -> bool {
use self::Token::*;
match *self {
UnaryPlus |
UnaryMinus |
Equals |
Bang |
Return => true,
_ => false,
}
}
}
pub struct TokenIterator<'a> {
last: Token,
char_stream: Peekable<Chars<'a>>,
}
@@ -264,12 +354,8 @@ impl<'a> TokenIterator<'a> {
let out: String = result.iter().cloned().collect();
Ok(out)
}
}
impl<'a> Iterator for TokenIterator<'a> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
fn inner_next(&mut self) -> Option<Token> {
while let Some(c) = self.char_stream.next() {
match c {
'0'...'9' => {
@@ -330,6 +416,7 @@ impl<'a> Iterator for TokenIterator<'a> {
"if" => return Some(Token::If),
"else" => return Some(Token::Else),
"while" => return Some(Token::While),
"loop" => return Some(Token::Loop),
"break" => return Some(Token::Break),
"return" => return Some(Token::Return),
"fn" => return Some(Token::Fn),
@@ -366,10 +453,57 @@ impl<'a> Iterator for TokenIterator<'a> {
')' => return Some(Token::RParen),
'[' => return Some(Token::LSquare),
']' => return Some(Token::RSquare),
'+' => return Some(Token::Plus),
'-' => return Some(Token::Minus),
'+' => {
return match self.char_stream.peek() {
Some(&'=') => {
self.char_stream.next();
Some(Token::PlusEquals)
},
_ if self.last.is_next_unary() => Some(Token::UnaryPlus),
_ => Some(Token::Plus),
}
},
'-' => {
return match self.char_stream.peek() {
Some(&'=') => {
self.char_stream.next();
Some(Token::MinusEquals)
},
_ if self.last.is_next_unary() => Some(Token::UnaryMinus),
_ => Some(Token::Minus),
}
},
'*' => return Some(Token::Multiply),
'/' => return Some(Token::Divide),
'/' => {
match self.char_stream.peek() {
Some(&'/') => {
self.char_stream.next();
while let Some(c) = self.char_stream.next() {
if c == '\n' { break; }
}
}
Some(&'*') => {
let mut level = 1;
self.char_stream.next();
while let Some(c) = self.char_stream.next() {
match c {
'/' => if let Some('*') = self.char_stream.next() {
level+=1;
}
'*' => if let Some('/') = self.char_stream.next() {
level-=1;
}
_ => (),
}
if level == 0 {
break;
}
}
}
_ => return Some(Token::Divide),
}
}
';' => return Some(Token::Semicolon),
':' => return Some(Token::Colon),
',' => return Some(Token::Comma),
@@ -437,13 +571,28 @@ impl<'a> Iterator for TokenIterator<'a> {
}
}
impl<'a> Iterator for TokenIterator<'a> {
type Item = Token;
// TODO - perhaps this could be optimized?
fn next(&mut self) -> Option<Self::Item> {
self.last = match self.inner_next() {
Some(c) => c,
None => return None,
};
Some(self.last.clone())
}
}
pub fn lex(input: &str) -> TokenIterator {
TokenIterator { char_stream: input.chars().peekable() }
TokenIterator { last: Token::LexErr(LexError::Nothing), char_stream: input.chars().peekable() }
}
fn get_precedence(token: &Token) -> i32 {
match *token {
Token::Equals => 10,
Token::Equals
| Token::PlusEquals
| Token::MinusEquals => 10,
Token::Or => 11,
Token::And => 12,
Token::LessThan
@@ -587,6 +736,20 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
}
}
fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
let tok = match input.peek() {
Some(tok) => tok.clone(),
None => return Err(ParseError::InputPastEndOfFile),
};
match tok {
Token::UnaryMinus => { input.next(); Ok(Expr::FnCall("-".to_string(), vec![parse_primary(input)?])) }
Token::UnaryPlus => { input.next(); parse_primary(input) }
Token::Bang => { input.next(); Ok(Expr::FnCall("!".to_string(), vec![parse_primary(input)?])) }
_ => parse_primary(input)
}
}
fn parse_binop<'a>(input: &mut Peekable<TokenIterator<'a>>,
prec: i32,
lhs: Expr)
@@ -605,7 +768,7 @@ fn parse_binop<'a>(input: &mut Peekable<TokenIterator<'a>>,
}
if let Some(op_token) = input.next() {
let mut rhs = try!(parse_primary(input));
let mut rhs = try!(parse_unary(input));
let mut next_prec = -1;
@@ -626,6 +789,20 @@ fn parse_binop<'a>(input: &mut Peekable<TokenIterator<'a>>,
Token::Multiply => Expr::FnCall("*".to_string(), vec![lhs_curr, rhs]),
Token::Divide => Expr::FnCall("/".to_string(), vec![lhs_curr, rhs]),
Token::Equals => Expr::Assignment(Box::new(lhs_curr), Box::new(rhs)),
Token::PlusEquals => {
let lhs_copy = lhs_curr.clone();
Expr::Assignment(
Box::new(lhs_curr),
Box::new(Expr::FnCall("+".to_string(), vec![lhs_copy, rhs]))
)
},
Token::MinusEquals => {
let lhs_copy = lhs_curr.clone();
Expr::Assignment(
Box::new(lhs_curr),
Box::new(Expr::FnCall("-".to_string(), vec![lhs_copy, rhs]))
)
},
Token::Period => Expr::Dot(Box::new(lhs_curr), Box::new(rhs)),
Token::EqualTo => Expr::FnCall("==".to_string(), vec![lhs_curr, rhs]),
Token::NotEqualTo => Expr::FnCall("!=".to_string(), vec![lhs_curr, rhs]),
@@ -646,7 +823,7 @@ fn parse_binop<'a>(input: &mut Peekable<TokenIterator<'a>>,
}
fn parse_expr<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
let lhs = try!(parse_primary(input));
let lhs = try!(parse_unary(input));
parse_binop(input, 0, lhs)
}
@@ -676,6 +853,14 @@ fn parse_while<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Pars
Ok(Stmt::While(Box::new(guard), Box::new(body)))
}
fn parse_loop<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
input.next();
let body = try!(parse_block(input));
Ok(Stmt::Loop(Box::new(body)))
}
fn parse_var<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
input.next();
@@ -739,6 +924,7 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
match input.peek() {
Some(&Token::If) => parse_if(input),
Some(&Token::While) => parse_while(input),
Some(&Token::Loop) => parse_loop(input),
Some(&Token::Break) => {
input.next();
Ok(Stmt::Break)