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");
}
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

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

View file

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

View file

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

View file

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

View file

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

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

View file

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