for loops and character type

This commit is contained in:
TriMill 2022-09-07 18:10:43 -04:00
parent 9f1c29f712
commit 3d915a5b94
8 changed files with 125 additions and 32 deletions

View File

@ -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

View File

@ -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() })
} }

View File

@ -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 {

View File

@ -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,

View File

@ -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 {

View File

@ -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,

View File

@ -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());

View File

@ -1,7 +1,4 @@
if false {
} elif true { for c: "asdfghjkl" {
let b = 'q' + c;
} else {
} }