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");
|
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(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
|
# 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
|
||||||
|
|
37
src/eval.rs
37
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> {
|
pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), RuntimeError> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Expr{ expr }
|
Stmt::Expr{ expr }
|
||||||
=> drop(eval_expr(expr, env)),
|
=> drop(eval_expr(expr, env)),
|
||||||
Stmt::Let { lhs: Token{ty: TokenType::Ident(s),..}, rhs: None }
|
Stmt::Let { lhs, rhs: None }
|
||||||
=> env.borrow_mut().declare(s.clone(), Value::Nil),
|
=> env.borrow_mut().declare(unwrap_ident_token(lhs).clone(), Value::Nil),
|
||||||
Stmt::Let { lhs: Token{ty: TokenType::Ident(s),..}, rhs: Some(rhs) } => {
|
Stmt::Let { lhs, rhs: Some(rhs) } => {
|
||||||
let r = eval_expr(rhs, env.clone())?;
|
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 } => {
|
Stmt::Block { stmts } => {
|
||||||
let block_env = Environment::extend(env).wrap();
|
let block_env = Environment::extend(env).wrap();
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
eval_stmt(stmt, block_env.clone())?
|
eval_stmt(stmt, block_env.clone())?
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Stmt::If { if_clauses, else_clause } => {
|
Stmt::If { if_clauses, else_clause } => {
|
||||||
for ic in if_clauses {
|
for ic in if_clauses {
|
||||||
let cond = eval_expr(&ic.0, env.clone())?;
|
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 {
|
if let Some(ec) = else_clause {
|
||||||
return eval_stmt(&ec, env)
|
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!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -111,6 +133,7 @@ pub fn eval_literal(token: &Token) -> Value {
|
||||||
TokenType::Float(f) => Value::Float(*f),
|
TokenType::Float(f) => Value::Float(*f),
|
||||||
TokenType::ImFloat(f) => Value::Complex(Complex64::new(0.0, *f)),
|
TokenType::ImFloat(f) => Value::Complex(Complex64::new(0.0, *f)),
|
||||||
TokenType::String(s) => Value::String(s.clone()),
|
TokenType::String(s) => Value::String(s.clone()),
|
||||||
|
TokenType::Char(c) => Value::Char(*c),
|
||||||
_ => todo!()
|
_ => 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::MinusEqual => &prev_value - &r,
|
||||||
TokenType::StarEqual => &prev_value * &r,
|
TokenType::StarEqual => &prev_value * &r,
|
||||||
TokenType::SlashEqual => &prev_value / &r,
|
TokenType::SlashEqual => &prev_value / &r,
|
||||||
|
TokenType::PercentEqual => &prev_value % &r,
|
||||||
_ => todo!() // TODO more operations
|
_ => todo!() // TODO more operations
|
||||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })?;
|
}.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::Minus => &l - &r,
|
||||||
TokenType::Star => &l * &r,
|
TokenType::Star => &l * &r,
|
||||||
TokenType::Slash => &l / &r,
|
TokenType::Slash => &l / &r,
|
||||||
|
TokenType::Percent => &l % &r,
|
||||||
_ => todo!() // TODO other operations
|
_ => todo!() // TODO other operations
|
||||||
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })
|
}.map_err(|e| RuntimeError { message: e, pos: op.pos.clone() })
|
||||||
}
|
}
|
|
@ -6,7 +6,8 @@ pub enum Stmt {
|
||||||
Expr { expr: Expr },
|
Expr { expr: Expr },
|
||||||
Let { lhs: Token, rhs: Option<Expr> },
|
Let { lhs: Token, rhs: Option<Expr> },
|
||||||
Block { stmts: Vec<Stmt> },
|
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 {
|
impl fmt::Debug for Stmt {
|
||||||
|
|
26
src/lexer.rs
26
src/lexer.rs
|
@ -65,6 +65,10 @@ impl Lexer {
|
||||||
message: msg.into()
|
message: msg.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_literal(&self) -> String {
|
||||||
|
self.code[self.start..self.current].iter().collect::<String>()
|
||||||
|
}
|
||||||
|
|
||||||
fn advance(&mut self, newline: bool) {
|
fn advance(&mut self, newline: bool) {
|
||||||
if newline {
|
if newline {
|
||||||
|
@ -139,6 +143,7 @@ impl Lexer {
|
||||||
},
|
},
|
||||||
',' => self.add_token(TokenType::Comma, ","),
|
',' => self.add_token(TokenType::Comma, ","),
|
||||||
';' => self.add_token(TokenType::Semicolon, ";"),
|
';' => self.add_token(TokenType::Semicolon, ";"),
|
||||||
|
':' => self.add_token(TokenType::Colon, ":"),
|
||||||
'(' => self.add_token(TokenType::LParen, "("),
|
'(' => self.add_token(TokenType::LParen, "("),
|
||||||
')' => self.add_token(TokenType::RParen, ")"),
|
')' => self.add_token(TokenType::RParen, ")"),
|
||||||
'[' => self.add_token(TokenType::LBrack, "["),
|
'[' => self.add_token(TokenType::LBrack, "["),
|
||||||
|
@ -152,6 +157,7 @@ impl Lexer {
|
||||||
self.advance(true);
|
self.advance(true);
|
||||||
},
|
},
|
||||||
'"' => self.string()?,
|
'"' => self.string()?,
|
||||||
|
'\'' => self.char()?,
|
||||||
' ' | '\t' | '\r' | '\n' => (),
|
' ' | '\t' | '\r' | '\n' => (),
|
||||||
'0'..='9' => self.number()?,
|
'0'..='9' => self.number()?,
|
||||||
'a'..='z' | 'A'..='Z' | '_' => self.ident()?,
|
'a'..='z' | 'A'..='Z' | '_' => self.ident()?,
|
||||||
|
@ -161,6 +167,20 @@ impl Lexer {
|
||||||
Ok(())
|
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> {
|
fn string(&mut self) -> Result<(), ParserError> {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
while !self.at_end() && self.peek() != '"' {
|
while !self.at_end() && self.peek() != '"' {
|
||||||
|
@ -188,7 +208,7 @@ impl Lexer {
|
||||||
return Err(self.mk_error("Unexpected EOF while parsing string"))
|
return Err(self.mk_error("Unexpected EOF while parsing string"))
|
||||||
}
|
}
|
||||||
self.advance(false);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +226,7 @@ impl Lexer {
|
||||||
}
|
}
|
||||||
let is_imag = !self.at_end() && self.peek() == 'i';
|
let is_imag = !self.at_end() && self.peek() == 'i';
|
||||||
if is_imag { self.advance(false); }
|
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 {
|
if is_imag {
|
||||||
match literal[..literal.len()-1].parse::<f64>() {
|
match literal[..literal.len()-1].parse::<f64>() {
|
||||||
Ok(num) => self.add_token(TokenType::ImFloat(num), literal),
|
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() == '_') {
|
while !self.at_end() && (self.peek().is_ascii_alphanumeric() || self.peek() == '_') {
|
||||||
self.advance(false);
|
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() {
|
let token_ty = match literal.as_ref() {
|
||||||
"true" => TokenType::True,
|
"true" => TokenType::True,
|
||||||
"false" => TokenType::False,
|
"false" => TokenType::False,
|
||||||
|
|
|
@ -54,24 +54,28 @@ impl Parser {
|
||||||
let next = self.peek();
|
let next = self.peek();
|
||||||
|
|
||||||
match next.ty {
|
match next.ty {
|
||||||
// let statement
|
|
||||||
TokenType::Let => {
|
TokenType::Let => {
|
||||||
|
// let statement
|
||||||
self.next();
|
self.next();
|
||||||
return self.letstmt()
|
self.letstmt()
|
||||||
}
|
},
|
||||||
TokenType::LBrace => {
|
TokenType::LBrace => {
|
||||||
|
// block
|
||||||
self.next();
|
self.next();
|
||||||
return self.block()
|
self.block()
|
||||||
}
|
},
|
||||||
|
|
||||||
// if statement
|
|
||||||
TokenType::If => {
|
TokenType::If => {
|
||||||
|
// if statement
|
||||||
self.next();
|
self.next();
|
||||||
return self.ifstmt()
|
self.ifstmt()
|
||||||
}
|
},
|
||||||
|
TokenType::For => {
|
||||||
// fallback to an expression terminated with a semicolon
|
// for statement
|
||||||
|
self.next();
|
||||||
|
self.forstmt()
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
// fallback to an expression terminated with a semicolon
|
||||||
let expr = self.assignment()?;
|
let expr = self.assignment()?;
|
||||||
if self.at_end() {
|
if self.at_end() {
|
||||||
if self.repl {
|
if self.repl {
|
||||||
|
@ -83,10 +87,10 @@ impl Parser {
|
||||||
|
|
||||||
let next = self.next();
|
let next = self.next();
|
||||||
|
|
||||||
return match next.ty {
|
match next.ty {
|
||||||
TokenType::Semicolon => Ok(Stmt::Expr{expr}),
|
TokenType::Semicolon => Ok(Stmt::Expr{expr}),
|
||||||
_ => Err(self.mk_error("Missing semicolon after statement"))
|
_ => 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> {
|
fn block(&mut self) -> Result<Stmt, ParserError> {
|
||||||
let mut stmts = vec![];
|
let mut stmts = vec![];
|
||||||
while !self.at_end() && self.peek().ty != TokenType::RBrace {
|
while !self.at_end() && self.peek().ty != TokenType::RBrace {
|
||||||
|
@ -263,7 +286,7 @@ impl Parser {
|
||||||
if matches!(next.ty,
|
if matches!(next.ty,
|
||||||
TokenType::True | TokenType::False | TokenType::Nil
|
TokenType::True | TokenType::False | TokenType::Nil
|
||||||
| TokenType::Int(_) | TokenType::Float(_) | TokenType::ImFloat(_)
|
| TokenType::Int(_) | TokenType::Float(_) | TokenType::ImFloat(_)
|
||||||
| TokenType::String(_)
|
| TokenType::String(_) | TokenType::Char(_)
|
||||||
) {
|
) {
|
||||||
Ok(Expr::Literal { value: next })
|
Ok(Expr::Literal { value: next })
|
||||||
} else if let TokenType::Ident(..) = next.ty {
|
} else if let TokenType::Ident(..) = next.ty {
|
||||||
|
|
|
@ -18,7 +18,7 @@ impl fmt::Debug for Token {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum TokenType {
|
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>),
|
Ident(Rc<str>),
|
||||||
|
|
||||||
Plus, Minus, Star, Slash, Percent, DoubleSlash, Caret,
|
Plus, Minus, Star, Slash, Percent, DoubleSlash, Caret,
|
||||||
|
@ -29,7 +29,7 @@ pub enum TokenType {
|
||||||
|
|
||||||
Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper,
|
Arrow, PipeColon, PipePoint, PipeQuestion, PipeAmper,
|
||||||
|
|
||||||
Comma, Semicolon,
|
Comma, Semicolon, Colon,
|
||||||
|
|
||||||
LParen, RParen, LBrack, RBrack, LBrace, RBrace,
|
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;
|
use num_traits::Zero;
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ pub enum Value {
|
||||||
Nil,
|
Nil,
|
||||||
Int(i64), Float(f64), Complex(Complex), Rational(Rational),
|
Int(i64), Float(f64), Complex(Complex), Rational(Rational),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
Char(char),
|
||||||
String(Rc<str>),
|
String(Rc<str>),
|
||||||
List(Rc<Vec<Value>>), Map(Rc<HashMap<Value,Value>>),
|
List(Rc<Vec<Value>>), Map(Rc<HashMap<Value,Value>>),
|
||||||
}
|
}
|
||||||
|
@ -72,6 +73,16 @@ impl Value {
|
||||||
_ => true
|
_ => 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!(Rational, Rational);
|
||||||
value_from!(Bool, bool);
|
value_from!(Bool, bool);
|
||||||
value_from!(String, String Rc<str>);
|
value_from!(String, String Rc<str>);
|
||||||
|
value_from!(Char, char);
|
||||||
value_from!(List, Vec<Value>);
|
value_from!(List, Vec<Value>);
|
||||||
value_from!(Map, HashMap<Value,Value>);
|
value_from!(Map, HashMap<Value,Value>);
|
||||||
|
|
||||||
impl_numeric_op!(Add, add, {
|
impl_numeric_op!(Add, add, {
|
||||||
(String(a), String(b)) => Ok(((**a).to_owned() + b).into()),
|
(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)) => {
|
(List(a), List(b)) => {
|
||||||
let mut a = (**a).clone();
|
let mut a = (**a).clone();
|
||||||
a.append(&mut (**b).clone());
|
a.append(&mut (**b).clone());
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
if false {
|
|
||||||
|
|
||||||
} elif true {
|
for c: "asdfghjkl" {
|
||||||
|
let b = 'q' + c;
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue