for loops and character type
This commit is contained in:
parent
9f1c29f712
commit
3d915a5b94
8 changed files with 125 additions and 32 deletions
|
@ -14,6 +14,10 @@ if 49 <~ data {
|
|||
println("49 is not the square of a prime");
|
||||
}
|
||||
|
||||
for p : primes {
|
||||
println(p);
|
||||
}
|
||||
|
||||
# LET IDENT(square) EQ IDENT(x) ARROW IDENT(x) CARET INT(2) SEMICOLON
|
||||
#
|
||||
# LET IDENT(data) EQ IDENT(range) LPAREN INT(100) RPAREN PIPECOLON IDENT(filter) LPAREN IDENT(is_prime) RPAREN PIPECOLON IDENT(map) LPAREN IDENT(square) RPAREN SEMICOLON
|
||||
|
|
35
src/eval.rs
35
src/eval.rs
|
@ -55,22 +55,30 @@ impl Environment {
|
|||
}
|
||||
}
|
||||
|
||||
fn unwrap_ident_token<'a>(tok: &'a Token) -> &'a Rc<str> {
|
||||
if let Token { ty: TokenType::Ident(s),.. } = tok {
|
||||
s
|
||||
} else {
|
||||
unreachable!("precondition failed")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> {
|
||||
match stmt {
|
||||
Stmt::Expr{ expr }
|
||||
=> drop(eval_expr(expr, env)),
|
||||
Stmt::Let { lhs: Token{ty: TokenType::Ident(s),..}, rhs: None }
|
||||
=> env.borrow_mut().declare(s.clone(), Value::Nil),
|
||||
Stmt::Let { lhs: Token{ty: TokenType::Ident(s),..}, rhs: Some(rhs) } => {
|
||||
Stmt::Let { lhs, rhs: None }
|
||||
=> env.borrow_mut().declare(unwrap_ident_token(lhs).clone(), Value::Nil),
|
||||
Stmt::Let { lhs, rhs: Some(rhs) } => {
|
||||
let r = eval_expr(rhs, env.clone())?;
|
||||
env.borrow_mut().declare(s.clone(), r)
|
||||
env.borrow_mut().declare(unwrap_ident_token(lhs).clone(), r)
|
||||
},
|
||||
Stmt::Block { stmts } => {
|
||||
let block_env = Environment::extend(env).wrap();
|
||||
for stmt in stmts {
|
||||
eval_stmt(stmt, block_env.clone())?
|
||||
}
|
||||
}
|
||||
},
|
||||
Stmt::If { if_clauses, else_clause } => {
|
||||
for ic in if_clauses {
|
||||
let cond = eval_expr(&ic.0, env.clone())?;
|
||||
|
@ -81,7 +89,21 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> {
|
|||
if let Some(ec) = else_clause {
|
||||
return eval_stmt(&ec, env)
|
||||
}
|
||||
},
|
||||
Stmt::For { var, expr, stmt } => {
|
||||
let name = unwrap_ident_token(var);
|
||||
let iter = eval_expr(expr, env.clone())?;
|
||||
env.borrow_mut().declare(name.clone(), Value::Nil);
|
||||
if let Ok(i) = iter.iter() {
|
||||
for v in i {
|
||||
let env = env.clone();
|
||||
env.borrow_mut().set(name.clone(), v).expect("unreachable");
|
||||
eval_stmt(&stmt, env)?;
|
||||
}
|
||||
} else {
|
||||
return Err(RuntimeError { message: "Cannot iterate this type".into(), pos: var.pos.clone() })
|
||||
};
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
Ok(())
|
||||
|
@ -111,6 +133,7 @@ pub fn eval_literal(token: &Token) -> Value {
|
|||
TokenType::Float(f) => Value::Float(*f),
|
||||
TokenType::ImFloat(f) => Value::Complex(Complex64::new(0.0, *f)),
|
||||
TokenType::String(s) => Value::String(s.clone()),
|
||||
TokenType::Char(c) => Value::Char(*c),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +170,7 @@ pub fn eval_assignment(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef
|
|||
TokenType::MinusEqual => &prev_value - &r,
|
||||
TokenType::StarEqual => &prev_value * &r,
|
||||
TokenType::SlashEqual => &prev_value / &r,
|
||||
TokenType::PercentEqual => &prev_value % &r,
|
||||
_ => todo!() // TODO more operations
|
||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })?;
|
||||
|
||||
|
@ -167,6 +191,7 @@ pub fn eval_binary(lhs: &Box<Expr>, rhs: &Box<Expr>, op: &Token, env: EnvRef) ->
|
|||
TokenType::Minus => &l - &r,
|
||||
TokenType::Star => &l * &r,
|
||||
TokenType::Slash => &l / &r,
|
||||
TokenType::Percent => &l % &r,
|
||||
_ => todo!() // TODO other operations
|
||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })
|
||||
}
|
|
@ -6,7 +6,8 @@ pub enum Stmt {
|
|||
Expr { expr: Expr },
|
||||
Let { lhs: Token, rhs: Option<Expr> },
|
||||
Block { stmts: Vec<Stmt> },
|
||||
If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option<Box<Stmt>> }
|
||||
If { if_clauses: Vec<(Expr, Stmt)>, else_clause: Option<Box<Stmt>> },
|
||||
For { var: Token, expr: Expr, stmt: Box<Stmt> }
|
||||
}
|
||||
|
||||
impl fmt::Debug for Stmt {
|
||||
|
|
26
src/lexer.rs
26
src/lexer.rs
|
@ -66,6 +66,10 @@ impl Lexer {
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_literal(&self) -> String {
|
||||
self.code[self.start..self.current].iter().collect::<String>()
|
||||
}
|
||||
|
||||
fn advance(&mut self, newline: bool) {
|
||||
if newline {
|
||||
self.line += 1;
|
||||
|
@ -139,6 +143,7 @@ impl Lexer {
|
|||
},
|
||||
',' => self.add_token(TokenType::Comma, ","),
|
||||
';' => self.add_token(TokenType::Semicolon, ";"),
|
||||
':' => self.add_token(TokenType::Colon, ":"),
|
||||
'(' => self.add_token(TokenType::LParen, "("),
|
||||
')' => self.add_token(TokenType::RParen, ")"),
|
||||
'[' => self.add_token(TokenType::LBrack, "["),
|
||||
|
@ -152,6 +157,7 @@ impl Lexer {
|
|||
self.advance(true);
|
||||
},
|
||||
'"' => self.string()?,
|
||||
'\'' => self.char()?,
|
||||
' ' | '\t' | '\r' | '\n' => (),
|
||||
'0'..='9' => self.number()?,
|
||||
'a'..='z' | 'A'..='Z' | '_' => self.ident()?,
|
||||
|
@ -161,6 +167,20 @@ impl Lexer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn char(&mut self) -> Result<(), ParserError> {
|
||||
if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) }
|
||||
let mut c = self.next();
|
||||
if c == '\'' {
|
||||
return Err(self.mk_error("Empty character literal"))
|
||||
} else if c == '\\' {
|
||||
if self.at_end() { return Err(self.mk_error("Unexpected EOF in character literal")) }
|
||||
// TODO Escapes
|
||||
}
|
||||
self.expect(&['\'']).ok_or(self.mk_error("Expected ' to terminate character literal"))?;
|
||||
self.add_token(TokenType::Char(c), self.collect_literal());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn string(&mut self) -> Result<(), ParserError> {
|
||||
let mut s = String::new();
|
||||
while !self.at_end() && self.peek() != '"' {
|
||||
|
@ -188,7 +208,7 @@ impl Lexer {
|
|||
return Err(self.mk_error("Unexpected EOF while parsing string"))
|
||||
}
|
||||
self.advance(false);
|
||||
self.add_token(TokenType::String(Rc::from(s)), self.code[self.start..self.current].iter().collect::<String>());
|
||||
self.add_token(TokenType::String(Rc::from(s)), self.collect_literal());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -206,7 +226,7 @@ impl Lexer {
|
|||
}
|
||||
let is_imag = !self.at_end() && self.peek() == 'i';
|
||||
if is_imag { self.advance(false); }
|
||||
let literal = self.code[self.start..self.current].iter().collect::<String>();
|
||||
let literal = self.collect_literal();
|
||||
if is_imag {
|
||||
match literal[..literal.len()-1].parse::<f64>() {
|
||||
Ok(num) => self.add_token(TokenType::ImFloat(num), literal),
|
||||
|
@ -230,7 +250,7 @@ impl Lexer {
|
|||
while !self.at_end() && (self.peek().is_ascii_alphanumeric() || self.peek() == '_') {
|
||||
self.advance(false);
|
||||
}
|
||||
let literal = self.code[self.start..self.current].iter().collect::<String>();
|
||||
let literal = self.collect_literal();
|
||||
let token_ty = match literal.as_ref() {
|
||||
"true" => TokenType::True,
|
||||
"false" => TokenType::False,
|
||||
|
|
|
@ -54,24 +54,28 @@ impl Parser {
|
|||
let next = self.peek();
|
||||
|
||||
match next.ty {
|
||||
// let statement
|
||||
TokenType::Let => {
|
||||
// let statement
|
||||
self.next();
|
||||
return self.letstmt()
|
||||
}
|
||||
self.letstmt()
|
||||
},
|
||||
TokenType::LBrace => {
|
||||
// block
|
||||
self.next();
|
||||
return self.block()
|
||||
}
|
||||
|
||||
// if statement
|
||||
self.block()
|
||||
},
|
||||
TokenType::If => {
|
||||
// if statement
|
||||
self.next();
|
||||
return self.ifstmt()
|
||||
}
|
||||
|
||||
// fallback to an expression terminated with a semicolon
|
||||
self.ifstmt()
|
||||
},
|
||||
TokenType::For => {
|
||||
// for statement
|
||||
self.next();
|
||||
self.forstmt()
|
||||
},
|
||||
_ => {
|
||||
// fallback to an expression terminated with a semicolon
|
||||
let expr = self.assignment()?;
|
||||
if self.at_end() {
|
||||
if self.repl {
|
||||
|
@ -83,10 +87,10 @@ impl Parser {
|
|||
|
||||
let next = self.next();
|
||||
|
||||
return match next.ty {
|
||||
match next.ty {
|
||||
TokenType::Semicolon => Ok(Stmt::Expr{expr}),
|
||||
_ => Err(self.mk_error("Missing semicolon after statement"))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +158,25 @@ impl Parser {
|
|||
})
|
||||
}
|
||||
|
||||
fn forstmt(&mut self) -> Result<Stmt, ParserError> {
|
||||
self.err_on_eof()?;
|
||||
let var = self.next();
|
||||
if let TokenType::Ident(_) = &var.ty {
|
||||
self.err_on_eof()?;
|
||||
let x = self.next();
|
||||
if x.ty != TokenType::Colon {
|
||||
return Err(self.mk_error("Expected colon"))
|
||||
}
|
||||
self.err_on_eof()?;
|
||||
let expr = self.assignment()?;
|
||||
self.err_on_eof()?;
|
||||
let stmt = self.statement()?;
|
||||
Ok(Stmt::For{ var, expr, stmt: Box::new(stmt) })
|
||||
} else {
|
||||
Err(self.mk_error("Expected identifier after for"))
|
||||
}
|
||||
}
|
||||
|
||||
fn block(&mut self) -> Result<Stmt, ParserError> {
|
||||
let mut stmts = vec![];
|
||||
while !self.at_end() && self.peek().ty != TokenType::RBrace {
|
||||
|
@ -263,7 +286,7 @@ impl Parser {
|
|||
if matches!(next.ty,
|
||||
TokenType::True | TokenType::False | TokenType::Nil
|
||||
| TokenType::Int(_) | TokenType::Float(_) | TokenType::ImFloat(_)
|
||||
| TokenType::String(_)
|
||||
| TokenType::String(_) | TokenType::Char(_)
|
||||
) {
|
||||
Ok(Expr::Literal { value: next })
|
||||
} else if let TokenType::Ident(..) = next.ty {
|
||||
|
|
|
@ -18,7 +18,7 @@ impl fmt::Debug for Token {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TokenType {
|
||||
Int(i64), Float(f64), ImFloat(f64), String(Rc<str>),
|
||||
Int(i64), Float(f64), ImFloat(f64), String(Rc<str>), Char(char),
|
||||
Ident(Rc<str>),
|
||||
|
||||
Plus, Minus, Star, Slash, Percent, DoubleSlash, Caret,
|
||||
|
@ -29,7 +29,7 @@ pub enum TokenType {
|
|||
|
||||
Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper,
|
||||
|
||||
Comma, Semicolon,
|
||||
Comma, Semicolon, Colon,
|
||||
|
||||
LParen, RParen, LBrack, RBrack, LBrace, RBrace,
|
||||
|
||||
|
|
25
src/value.rs
25
src/value.rs
|
@ -1,4 +1,4 @@
|
|||
use std::{rc::Rc, collections::HashMap, ops::*};
|
||||
use std::{rc::Rc, collections::HashMap, ops::*, ascii::AsciiExt};
|
||||
|
||||
use num_traits::Zero;
|
||||
|
||||
|
@ -54,6 +54,7 @@ pub enum Value {
|
|||
Nil,
|
||||
Int(i64), Float(f64), Complex(Complex), Rational(Rational),
|
||||
Bool(bool),
|
||||
Char(char),
|
||||
String(Rc<str>),
|
||||
List(Rc<Vec<Value>>), Map(Rc<HashMap<Value,Value>>),
|
||||
}
|
||||
|
@ -72,6 +73,16 @@ impl Value {
|
|||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Result<Box<dyn Iterator<Item=Value> + '_>, ()> {
|
||||
match self {
|
||||
Value::String(s)
|
||||
=> Ok(Box::new(s.chars()
|
||||
.map(|c| Value::Char(c)))),
|
||||
Value::List(l) => Ok(Box::new(l.iter().cloned())),
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,11 +92,23 @@ value_from!(Complex, Complex);
|
|||
value_from!(Rational, Rational);
|
||||
value_from!(Bool, bool);
|
||||
value_from!(String, String Rc<str>);
|
||||
value_from!(Char, char);
|
||||
value_from!(List, Vec<Value>);
|
||||
value_from!(Map, HashMap<Value,Value>);
|
||||
|
||||
impl_numeric_op!(Add, add, {
|
||||
(String(a), String(b)) => Ok(((**a).to_owned() + b).into()),
|
||||
(String(a), Char(c)) => {
|
||||
let mut s = (**a).to_owned();
|
||||
s.push(*c);
|
||||
Ok(s.into())
|
||||
},
|
||||
(Char(c), String(a)) => Ok((c.to_string() + a).into()),
|
||||
(Char(c1), Char(c2)) => {
|
||||
let mut s = c1.to_string();
|
||||
s.push(*c2);
|
||||
Ok(s.into())
|
||||
}
|
||||
(List(a), List(b)) => {
|
||||
let mut a = (**a).clone();
|
||||
a.append(&mut (**b).clone());
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
if false {
|
||||
|
||||
} elif true {
|
||||
|
||||
} else {
|
||||
|
||||
for c: "asdfghjkl" {
|
||||
let b = 'q' + c;
|
||||
}
|
Loading…
Reference in a new issue