sync'd latest master
This commit is contained in:
222
src/parser.rs
222
src/parser.rs
@@ -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)
|
||||
|
Reference in New Issue
Block a user