updated a bunch of stuff

This commit is contained in:
Moritz Gmeiner 2024-09-01 19:16:30 +02:00
commit 67bb5fe8fd
24 changed files with 683 additions and 702 deletions

View file

@ -6,6 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
phf = { version = "0.11.1", features = ["macros"] }
thiserror = "1.0.38"
itertools = "0.10.5"
phf = { version = "0.11", features = ["macros"] }
thiserror = "1"
itertools = "0.13"

View file

@ -129,6 +129,11 @@ impl Lexer {
// line comment
// advance until either source is empty or newline if found
while !self.source_is_empty() && self.advance() != '\n' {}
let comment: Box<str> =
self.source[self.start + 2..self.current].iter().collect();
self.push_token(TokenType::Comment(comment));
} else if self.consume('*') {
// block comment
@ -164,6 +169,12 @@ impl Lexer {
self.advance();
}
let comment: Box<str> = self.source[self.start + 2..self.current - 2]
.iter()
.collect();
self.push_token(TokenType::Comment(comment));
} else {
self.push_token(Slash)
}
@ -237,10 +248,13 @@ impl Lexer {
}
}
let string_literal = self.source[self.start + 1..self.current - 1].iter().collect();
let string_literal: Box<str> = self.source[self.start + 1..self.current - 1]
.iter()
.collect();
// Some(TokenType::String(Box::new(string_literal)))
self.tokens.push(Token::new_string(string_literal, self.code_pos));
self.tokens
.push(Token::new_string(string_literal, self.code_pos));
}
fn try_parse_number(&mut self) {
@ -290,8 +304,12 @@ impl Lexer {
}
fn try_parse_identifier(&mut self) {
let is_alpha_num_underscore =
|c: Option<char>| c.map_or(false, |c| matches!(c, '0'..='9' | 'A'..='Z' | '_' | 'a'..='z'));
let is_alpha_num_underscore = |c: Option<char>| {
c.map_or(
false,
|c| matches!(c, '0'..='9' | 'A'..='Z' | '_' | 'a'..='z'),
)
};
while is_alpha_num_underscore(self.peek()) {
self.advance();
@ -304,11 +322,12 @@ impl Lexer {
.cloned()
.unwrap_or(TokenType::Identifier(Box::new(lexeme))); */
if let Some(&token_type) = KEYWORDS.get(&lexeme) {
if let Some(token_type) = KEYWORDS.get(&lexeme) {
// Token::new(token_type, self.code_pos)
self.push_token(token_type);
self.push_token(token_type.clone());
} else {
self.tokens.push(Token::new_identifier(lexeme, self.code_pos));
self.tokens
.push(Token::new_identifier(lexeme, self.code_pos));
}
// Some(token_type)

View file

@ -4,13 +4,13 @@ use super::CodePos;
#[derive(Error, Debug)]
pub enum LexerError {
#[error("Unexpected character '{c}' at {code_pos}.")]
#[error("LexerError: Unexpected character '{c}' at {code_pos}.")]
UnexpectedCharacter { c: char, code_pos: CodePos },
#[error("Unterminated string literal starting at {code_pos}.")]
#[error("LexerError: Unterminated string literal starting at {code_pos}.")]
UnterminatedStringLiteral { code_pos: CodePos },
#[error("Unterminated block comment starting at {code_pos}.")]
#[error("LexerError: Unterminated block comment starting at {code_pos}.")]
UnterminatedBlockComment { code_pos: CodePos },
#[error("Invalid number literal {lexeme} at {code_pos}: {msg}")]
#[error("LexerError: Invalid number literal {lexeme} at {code_pos}: {msg}")]
InvalidNumberLiteral {
lexeme: String,
msg: String,

View file

@ -1,10 +1,9 @@
use std::fmt::{Debug, Display};
use std::mem::ManuallyDrop;
use super::CodePos;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
#[rustfmt::skip]
pub enum TokenType {
// Single-character tokens
@ -18,42 +17,20 @@ pub enum TokenType {
Less, LessEqual,
// Identifier and literals
Identifier, String, Number,
Identifier(Box<str>), String(Box<str>), Number(f64),
// Keywords
And, Break, Class, Else, False, Fun, For, If, Nil, Or,
Print, Return, Super, This, True, Var, While,
#[allow(dead_code, clippy::upper_case_acronyms)]
Comment(Box<str>),
#[allow(clippy::upper_case_acronyms)]
EOF
}
union TokenData {
none: (),
#[allow(clippy::box_collection)]
s: ManuallyDrop<Box<String>>,
num: f64,
}
impl TokenData {
fn none() -> Self {
TokenData { none: () }
}
fn string(s: String) -> Self {
let s = ManuallyDrop::new(Box::new(s));
TokenData { s }
}
fn num(num: f64) -> Self {
TokenData { num }
}
}
pub struct Token {
pub token_type: TokenType,
// pub lexeme: String,
data: TokenData,
pub code_pos: CodePos,
}
@ -61,95 +38,64 @@ impl Token {
pub fn new(token_type: TokenType, code_pos: CodePos) -> Self {
Token {
token_type,
// lexeme,
data: TokenData::none(),
code_pos,
}
}
pub fn new_string(s: String, code_pos: CodePos) -> Self {
pub fn new_string(s: impl Into<Box<str>>, code_pos: CodePos) -> Self {
Token {
token_type: TokenType::String,
data: TokenData::string(s),
token_type: TokenType::String(s.into()),
code_pos,
}
}
pub fn new_identifier(name: String, code_pos: CodePos) -> Self {
pub fn new_identifier(name: impl Into<Box<str>>, code_pos: CodePos) -> Self {
Token {
token_type: TokenType::Identifier,
data: TokenData::string(name),
token_type: TokenType::Identifier(name.into()),
code_pos,
}
}
pub fn new_number(num: f64, code_pos: CodePos) -> Self {
Token {
token_type: TokenType::Number,
data: TokenData::num(num),
token_type: TokenType::Number(num),
code_pos,
}
}
pub fn string_data(self) -> String {
assert!(self.token_type == TokenType::String || self.token_type == TokenType::Identifier);
pub fn take(&mut self) -> Self {
let code_pos = self.code_pos;
// std::mem::take(&mut self.data.s)
unsafe {
let mut me = self;
use TokenType::*;
let s = std::mem::take(&mut me.data.s);
let token_type = match &mut self.token_type {
String(s) => String(std::mem::take(s)),
Identifier(name) => Identifier(std::mem::take(name)),
other => other.clone(),
};
*ManuallyDrop::into_inner(s)
Token {
token_type,
code_pos,
}
}
pub fn num_data(self) -> f64 {
assert_eq!(self.token_type, TokenType::Number);
unsafe { self.data.num }
}
}
impl Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "<{:?}>", self.token_type)
match self.token_type {
TokenType::Number => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.num) },
TokenType::String => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
TokenType::Identifier => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
_ => write!(f, "<{:?}>", self.token_type),
}
write!(f, "<{:?} {:?}> ", self.token_type, self.code_pos)
}
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "<{:?}>", self.token_type)
match self.token_type {
TokenType::Number => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.num) },
TokenType::String => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
TokenType::Identifier => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
_ => write!(f, "<{:?}>", self.token_type),
use TokenType::*;
match &self.token_type {
String(s) => write!(f, "<String {}>", s),
Identifier(name) => write!(f, "<Identifier {}>", name),
Number(x) => write!(f, "<Number {}>", x),
tt => write!(f, "<{:?}>", tt),
}
}
}
/* impl Clone for Token {
fn clone(&self) -> Self {
let code_pos = self.code_pos;
match self.token_type {
TokenType::Number => Token::new_number(self.num_data(), code_pos),
TokenType::String => unsafe { Token::new_string(self.data.s.as_ref().clone(), code_pos) },
TokenType::Identifier => unsafe { Token::new_identifier(self.data.s.as_ref().clone(), code_pos) },
token_type => Token::new(token_type, code_pos),
}
}
} */
impl Drop for Token {
fn drop(&mut self) {
if self.token_type == TokenType::String {}
}
}

View file

@ -1,5 +1,3 @@
use std::vec::IntoIter;
use crate::lexer::{Token, TokenType};
use crate::parser::expr::BinaryOp;
@ -18,31 +16,39 @@ pub fn parse_tokens(tokens: Vec<Token>) -> Result<Vec<Stmt>, Vec<ParserError>> {
// takes care of token iteration
struct TokenIter {
token_iter: IntoIter<Token>,
tokens: Vec<Token>,
peek_token: Option<Token>,
pos: usize,
}
impl TokenIter {
pub fn new(tokens: Vec<Token>) -> Self {
TokenIter {
token_iter: tokens.into_iter(),
peek_token: None,
TokenIter { tokens, pos: 0 }
}
fn skip_comments(&mut self) {
while !self.is_empty() && matches![self.tokens[self.pos].token_type, TokenType::Comment(_)]
{
self.pos += 1; // skip comment token
}
}
fn peek(&mut self) -> Option<&Token> {
// if peek_token is empty: fill with next token from token_iter
if self.peek_token.is_none() && self.token_iter.len() != 0 {
self.peek_token = self.token_iter.next();
if self.is_empty() {
return None;
}
self.peek_token.as_ref()
self.skip_comments();
let token = &self.tokens[self.pos];
assert!(!matches!(token.token_type, TokenType::Comment(_)));
Some(token)
}
fn is_empty(&self) -> bool {
// peek_token is None and there are no more tokens to take from token_iter
self.peek_token.is_none() && self.token_iter.len() == 0
self.pos == self.tokens.len()
}
}
@ -50,8 +56,16 @@ impl Iterator for TokenIter {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
// return the peeked token if any, or else next token from token_iter
self.peek_token.take().or_else(|| self.token_iter.next())
self.skip_comments();
self.pos += 1;
assert!(!matches!(
self.tokens[self.pos - 1].token_type,
TokenType::Comment(_)
));
Some(self.tokens[self.pos - 1].take())
}
}
@ -101,7 +115,11 @@ impl Parser {
}
}
assert_eq!(me.next_token().token_type, TokenType::EOF);
assert_eq!(
me.next_token().token_type,
TokenType::EOF,
"last token wasn't EOF"
);
if !me.parse_errors.is_empty() {
Err(me.parse_errors)
@ -122,10 +140,10 @@ impl Parser {
self.is_in_function = false;
self.is_in_init = false;
let peek_token = self.peek_token();
let tt = &self.peek_token().token_type;
// if we match a synchronisation point: return
match peek_token.token_type {
match tt {
TokenType::Class
| TokenType::Fun
| TokenType::Var
@ -177,22 +195,12 @@ impl Parser {
let code_pos = self.peek_token().code_pos;
assert_eq!(self.next_token().token_type, TokenType::Return);
let expr = match self.peek_token().token_type {
TokenType::Semicolon => {
if !self.is_in_init {
Expr::nil()
} else {
Expr::Variable {
name: "this".to_owned(),
}
}
}
_ => {
if !self.is_in_init {
self.expression()?
} else {
return Err(ParserError::ReturnInInit { code_pos });
}
let expr = match (self.is_in_init, &self.peek_token().token_type) {
(false, TokenType::Semicolon) => Expr::nil(),
(true, TokenType::Semicolon) => Expr::variable("this"),
(false, _) => self.expression()?,
(true, _) => {
return Err(ParserError::ReturnInInit { code_pos });
}
};
@ -208,14 +216,18 @@ impl Parser {
fn if_statement(&mut self) -> ParserResult<Stmt> {
assert_eq!(self.next_token().token_type, TokenType::If);
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterIf {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftParen, |token| {
ParserError::MissingParenAfterIf {
code_pos: token.code_pos,
}
})?;
let condition = self.expression()?;
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
let then_branch = self.statement()?;
@ -235,14 +247,18 @@ impl Parser {
fn while_statement(&mut self) -> ParserResult<Stmt> {
assert_eq!(self.next_token().token_type, TokenType::While);
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterWhile {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftParen, |token| {
ParserError::MissingParenAfterWhile {
code_pos: token.code_pos,
}
})?;
let condition = self.expression()?;
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
@ -257,8 +273,10 @@ impl Parser {
fn for_statement(&mut self) -> ParserResult<Stmt> {
assert_eq!(self.next_token().token_type, TokenType::For);
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterFor {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftParen, |token| {
ParserError::MissingParenAfterFor {
code_pos: token.code_pos,
}
})?;
let initializer = match self.peek_token().token_type {
@ -282,8 +300,10 @@ impl Parser {
_ => Some(self.expression()?),
};
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
@ -326,7 +346,7 @@ impl Parser {
let token = self.next_token();
let name = match token.token_type {
TokenType::Identifier => token.string_data(),
TokenType::Identifier(s) => s,
_ => return Err(ParserError::ExpectedVarName { token }),
};
@ -354,13 +374,17 @@ impl Parser {
let superclass_name = self.identifier("Expected superclass")?;
Some(Expr::Variable { name: superclass_name })
Some(Expr::Variable {
name: superclass_name,
})
} else {
None
};
self.consume_token(TokenType::LeftBrace, |token| ParserError::MissingClassBody {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftBrace, |token| {
ParserError::MissingClassBody {
code_pos: token.code_pos,
}
})?;
let is_in_loop = std::mem::replace(&mut self.is_in_loop, false);
@ -381,7 +405,7 @@ impl Parser {
})?;
let is_in_init = self.is_in_init;
if method_name == "init" {
if &*method_name == "init" {
self.is_in_init = true;
}
@ -398,7 +422,7 @@ impl Parser {
self.is_in_class = is_in_class;
self.is_in_function = is_in_function;
let class = Expr::class(&name, methods, superclass);
let class = Expr::class(name.clone(), methods, superclass);
Ok(Stmt::var_decl(name, class))
}
@ -451,7 +475,7 @@ impl Parser {
Ok(Expr::function(name, param_names, body))
}
fn collect_params(&mut self) -> ParserResult<Vec<String>> {
fn collect_params(&mut self) -> ParserResult<Vec<Box<str>>> {
assert_eq!(self.next_token().token_type, TokenType::LeftParen);
if self.peek_token().token_type == TokenType::RightParen {
@ -470,8 +494,10 @@ impl Parser {
param_names.push(self.identifier("Expected parameter name")?);
}
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
Ok(param_names)
@ -532,7 +558,11 @@ impl Parser {
Expr::Variable { name } => Ok(Expr::assignment(Expr::Variable { name }, value)),
Expr::Get { target, name } => {
let value = Box::new(value);
Ok(Expr::Set { target, name, value })
Ok(Expr::Set {
target,
name,
value,
})
}
_ => Err(ParserError::InvalidAssignment { expr, code_pos }),
}
@ -720,8 +750,10 @@ impl Parser {
args.push(self.expression()?);
}
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
Ok(args)
@ -736,52 +768,53 @@ impl Parser {
match token.token_type {
TokenType::Fun => Ok(self.fun_params_and_body("<lambda>")?),
TokenType::Number => Ok(Expr::number(token.num_data())),
TokenType::String => Ok(Expr::string(token.string_data())),
TokenType::Number(x) => Ok(Expr::number(x)),
TokenType::String(s) => Ok(Expr::string(s)),
TokenType::False => Ok(Expr::bool(false)),
TokenType::True => Ok(Expr::bool(true)),
TokenType::Nil => Ok(Expr::nil()),
TokenType::This => Ok(Expr::This),
TokenType::Super => {
self.consume_token(TokenType::Dot, |token| ParserError::MissingMethodAfterSuper {
code_pos: token.code_pos,
self.consume_token(TokenType::Dot, |token| {
ParserError::MissingMethodAfterSuper {
code_pos: token.code_pos,
}
})?;
let method = self.identifier("Expected method name after super")?;
let super_var = Box::new(Expr::Variable {
name: "super".to_owned(),
});
let this_var = Box::new(Expr::This);
Ok(Expr::Super {
super_var,
this_var,
method,
})
let super_var = Expr::variable("super");
let this_var = Expr::This;
Ok(Expr::super_(super_var, this_var, method))
}
TokenType::LeftParen => {
let expr = self.expression()?;
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
Ok(Expr::grouping(expr))
}
TokenType::Identifier => Ok(Expr::Variable {
name: token.string_data(),
}),
TokenType::Identifier(name) => Ok(Expr::Variable { name }),
_ => Err(ParserError::ExpectedPrimary { token }),
}
}
fn semicolon(&mut self) -> ParserResult<()> {
self.consume_token(TokenType::Semicolon, |token| ParserError::MissingSemicolon {
code_pos: token.code_pos,
self.consume_token(TokenType::Semicolon, |token| {
ParserError::MissingSemicolon {
code_pos: token.code_pos,
}
})
}
fn identifier(&mut self, msg: &str) -> ParserResult<String> {
fn identifier(&mut self, msg: &str) -> ParserResult<Box<str>> {
match self.peek_token().token_type {
TokenType::Identifier => Ok(self.next_token().string_data()),
TokenType::Identifier(_) => match self.next_token().token_type {
TokenType::Identifier(s) => Ok(s),
_ => unreachable!(),
},
_ => Err(ParserError::MissingIdentifier {
msg: msg.to_owned(),
code_pos: self.peek_token().code_pos,
@ -813,7 +846,7 @@ impl Parser {
fn consume_token<F>(&mut self, token_type: TokenType, err_fn: F) -> ParserResult<()>
where
F: Fn(Token) -> ParserError,
F: Fn(&Token) -> ParserError,
{
/* let token = self.next_token();
@ -829,8 +862,8 @@ impl Parser {
Ok(())
}
// call err_fn with dummy token so we don't have to eat the EOF token
TokenType::EOF => Err(err_fn(Token::new(TokenType::EOF, self.peek_token().code_pos))),
_ => Err(err_fn(self.next_token())),
TokenType::EOF => Err(err_fn(self.peek_token())),
_ => Err(err_fn(&self.next_token())),
}
}
}

View file

@ -6,44 +6,44 @@ use super::Expr;
#[derive(Error, Debug)]
pub enum ParserError {
#[error("Token stream ended unexpectedly.")]
#[error("ParserError: Token stream ended unexpectedly.")]
TokenStreamEnded,
#[error("Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
#[error("ParserError: Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
ExpectedPrimary { token: Token },
#[error("Missing semicolon at {code_pos}")]
#[error("ParserError: Missing semicolon at {code_pos}")]
MissingSemicolon { code_pos: CodePos },
#[error("Expected variable name at {0}, got {token} instead", token.code_pos)]
#[error("ParserError: Expected variable name at {0}, got {token} instead", token.code_pos)]
ExpectedVarName { token: Token },
#[error("Can't assign to {expr} at {code_pos}")]
#[error("ParserError: Can't assign to {expr} at {code_pos}")]
InvalidAssignment { expr: Expr, code_pos: CodePos },
#[error("Missing closing curly brace at {code_pos}")]
#[error("ParserError: Missing closing curly brace at {code_pos}")]
MissingRightBrace { code_pos: CodePos },
#[error("Missing closing parenthesis at {code_pos}")]
#[error("ParserError: Missing closing parenthesis at {code_pos}")]
MissingRightParen { code_pos: CodePos },
#[error("Missing parenthesis after if at {code_pos}")]
#[error("ParserError: Missing parenthesis after if at {code_pos}")]
MissingParenAfterIf { code_pos: CodePos },
#[error("Missing parenthesis after while at {code_pos}")]
#[error("ParserError: Missing parenthesis after while at {code_pos}")]
MissingParenAfterWhile { code_pos: CodePos },
#[error("Missing parenthesis after for at {code_pos}")]
#[error("ParserError: Missing parenthesis after for at {code_pos}")]
MissingParenAfterFor { code_pos: CodePos },
#[error("Call at {code_pos} has too many arguments")]
#[error("ParserError: Call at {code_pos} has too many arguments")]
TooManyArguments { code_pos: CodePos },
#[error("{msg} at {code_pos}")]
#[error("ParserError: {msg} at {code_pos}")]
MissingIdentifier { msg: String, code_pos: CodePos },
#[error("Missing arguments to function declaration at {code_pos}")]
#[error("ParserError: Missing arguments to function declaration at {code_pos}")]
MissingFunctionArgs { code_pos: CodePos },
#[error("Missing body to function declaration at {code_pos}")]
#[error("ParserError: Missing body to function declaration at {code_pos}")]
MissingFunctionBody { code_pos: CodePos },
#[error("Function declaration at {code_pos} has too many parameters")]
#[error("ParserError: Function declaration at {code_pos} has too many parameters")]
TooManyParams { code_pos: CodePos },
#[error("Return statement outside of function definition")]
#[error("ParserError: Return statement outside of function definition")]
ReturnOutsideFunction { code_pos: CodePos },
#[error("Break statement outside of loop")]
#[error("ParserError: Break statement outside of loop")]
InvalidBreak { code_pos: CodePos },
#[error("Missing class body at {code_pos}")]
#[error("ParserError: Missing class body at {code_pos}")]
MissingClassBody { code_pos: CodePos },
#[error("Return statement in init")]
#[error("ParserError: Return statement in init")]
ReturnInInit { code_pos: CodePos },
#[error("Missing method name after super")]
#[error("ParserError: Missing method name after super")]
MissingMethodAfterSuper { code_pos: CodePos },
}

View file

@ -1,5 +1,4 @@
use std::fmt::Display;
use std::rc::Rc;
use itertools::Itertools;
@ -28,14 +27,14 @@ pub enum Expr {
expr: Box<Expr>,
},
Variable {
name: String,
name: Box<str>,
},
LocalVariable {
name: String,
name: Box<str>,
level: usize,
},
GlobalVariable {
name: String,
name: Box<str>,
},
Assignment {
target: Box<Expr>,
@ -47,37 +46,37 @@ pub enum Expr {
},
Get {
target: Box<Expr>,
name: String,
name: Box<str>,
},
Set {
target: Box<Expr>,
name: String,
name: Box<str>,
value: Box<Expr>,
},
Function {
name: String,
param_names: Vec<String>,
closure_vars: Vec<(String, usize)>,
name: Box<str>,
param_names: Vec<Box<str>>,
closure_vars: Vec<(Box<str>, usize)>,
body: Box<Stmt>,
},
Class {
name: Box<str>,
superclass: Option<Box<Expr>>,
name: String,
methods: Box<Vec<Expr>>,
},
This,
Super {
super_var: Box<Expr>,
this_var: Box<Expr>,
method: String,
method: Box<str>,
},
}
impl Expr {
pub fn string(s: impl Into<String>) -> Self {
pub fn string(s: impl Into<Box<str>>) -> Self {
let s = s.into();
Expr::Literal {
literal: Literal::String(Rc::new(s)),
literal: Literal::String(s),
}
}
@ -94,7 +93,9 @@ impl Expr {
}
pub fn nil() -> Self {
Expr::Literal { literal: Literal::Nil }
Expr::Literal {
literal: Literal::Nil,
}
}
pub fn unary(op: UnaryOp, expr: Expr) -> Self {
@ -119,6 +120,21 @@ impl Expr {
Expr::Grouping { expr }
}
pub fn variable(name: impl Into<Box<str>>) -> Self {
let name = name.into();
Expr::Variable { name }
}
pub fn local_variable(name: impl Into<Box<str>>, level: usize) -> Self {
let name = name.into();
Expr::LocalVariable { name, level }
}
pub fn global_variable(name: impl Into<Box<str>>) -> Self {
let name = name.into();
Expr::GlobalVariable { name }
}
pub fn assignment(target: Expr, value: Expr) -> Self {
let target = Box::new(target);
let value = Box::new(value);
@ -130,15 +146,14 @@ impl Expr {
Expr::Call { callee, args }
}
pub fn get(target: Expr, name: impl Into<String>) -> Self {
pub fn get(target: Expr, name: impl Into<Box<str>>) -> Self {
let target = Box::new(target);
let name = name.into();
Expr::Get { target, name }
}
pub fn function(name: String, param_names: Vec<String>, body: Stmt) -> Self {
// let name = Box::new(name);
// let param_names = Box::new(param_names);
pub fn function(name: impl Into<Box<str>>, param_names: Vec<Box<str>>, body: Stmt) -> Self {
let name = name.into();
#[allow(clippy::box_default)]
// let closure_vars = Box::new(Vec::new());
let closure_vars = Vec::new();
@ -151,16 +166,33 @@ impl Expr {
}
}
pub fn class(name: impl Into<String>, methods: Vec<Expr>, superclass: Option<Expr>) -> Self {
pub fn class(name: impl Into<Box<str>>, methods: Vec<Expr>, superclass: Option<Expr>) -> Self {
let superclass = superclass.map(Box::new);
let name = name.into();
let methods = Box::new(methods);
Expr::Class {
superclass,
name,
superclass,
methods,
}
}
pub fn super_(
super_var: impl Into<Box<Expr>>,
this_var: impl Into<Box<Expr>>,
method: impl Into<Box<str>>,
) -> Self {
let super_var = super_var.into();
let this_var = this_var.into();
let method = method.into();
Expr::Super {
super_var,
this_var,
method,
}
}
}
impl Display for Expr {
@ -174,12 +206,20 @@ impl Display for Expr {
}
Expr::Grouping { expr } => write!(f, "(group {expr})"),
Expr::Variable { name } => write!(f, "(var {name})"),
Expr::LocalVariable { name, level } => write!(f, "(var {name} local({level}))"),
Expr::GlobalVariable { name } => write!(f, "(var {name} global)"),
Expr::Assignment { target, value } => write!(f, "{target} = {value}"),
Expr::Call { callee, args } => write!(f, "({callee} {})", args.iter().map(|arg| arg.to_string()).join(" ")),
Expr::LocalVariable { name, level } => write!(f, "(local {name} {level})"),
Expr::GlobalVariable { name } => write!(f, "(global {name})"),
Expr::Assignment { target, value } => write!(f, "(set {target} {value})"),
Expr::Call { callee, args } => write!(
f,
"({callee} {})",
args.iter().map(|arg| arg.to_string()).join(" ")
),
Expr::Get { target, name } => write!(f, "(get {name} {target})"),
Expr::Set { target, name, value } => write!(f, "(set {name} {target} {value})"),
Expr::Set {
target,
name,
value,
} => write!(f, "(set {name} {target} {value})"),
Expr::Function {
name,
param_names,
@ -189,9 +229,13 @@ impl Display for Expr {
if !closure_vars.is_empty() {
let closure_fmt = closure_vars.iter().map(|(name, _level)| name).join(", ");
write!(f, "fun [{closure_fmt}] {name}({}) => {body}", param_names.join(", "))
write!(
f,
"(fun [{closure_fmt}] {name} ({}) {body})",
param_names.join(", ")
)
} else {
write!(f, "fun {name}({}) => {body}", param_names.join(", "))
write!(f, "(fun {name} ({}) {body})", param_names.join(", "))
}
}
Expr::Class {
@ -200,14 +244,14 @@ impl Display for Expr {
methods,
} => {
if let Some(superclass) = superclass {
writeln!(f, "class {name} < {superclass} {{")?;
writeln!(f, "(class {name} < {superclass} ")?;
} else {
writeln!(f, "class {name} {{")?;
writeln!(f, "(class {name} ")?;
}
for method in methods.iter() {
writeln!(f, "{method}")?;
}
write!(f, "}}")
write!(f, ")")
}
Expr::This => write!(f, "this"),
Expr::Super {
@ -223,7 +267,7 @@ impl Display for Expr {
#[derive(Debug, Clone)]
pub enum Literal {
String(Rc<String>),
String(Box<str>),
Number(f64),
Bool(bool),
Nil,

View file

@ -1,5 +1,5 @@
use itertools::Itertools;
pub fn indent(s: String) -> String {
s.split('\n').map(|line| format!("\t{line}")).join("\n")
s.split('\n').map(|line| format!(" {line}")).join("\n")
}

View file

@ -18,7 +18,7 @@ pub enum Stmt {
body: Box<Stmt>,
},
VarDecl {
name: String,
name: Box<str>,
initializer: Box<Expr>,
},
Block {
@ -62,19 +62,23 @@ impl Stmt {
Stmt::While { condition, body }
}
pub fn var_decl(name: impl Into<String>, initializer: impl Into<Box<Expr>>) -> Self {
pub fn var_decl(name: impl Into<Box<str>>, initializer: impl Into<Box<Expr>>) -> Self {
let name = name.into();
let initializer = initializer.into();
Stmt::VarDecl { name, initializer }
}
pub fn expr_stmt(expr: impl Into<Box<Expr>>) -> Self {
let expr = expr.into();
Stmt::ExprStmt { expr }
}
pub fn return_stmt(expr: impl Into<Box<Expr>>) -> Self {
let expr = expr.into();
Stmt::Return { expr }
}
}
@ -82,7 +86,7 @@ impl Stmt {
impl Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Stmt::Print { expr } => write!(f, "(print {expr});"),
Stmt::Print { expr } => write!(f, "(print {expr})"),
Stmt::IfStmt {
condition,
then_branch,
@ -110,18 +114,18 @@ impl Display for Stmt {
}
Ok(())
}
Stmt::VarDecl { name, initializer } => write!(f, "var {name} = {initializer};"),
Stmt::VarDecl { name, initializer } => write!(f, "(decl {name} {initializer})"),
Stmt::Block { statements } => {
writeln!(f, "{{")?;
writeln!(f, "(block")?;
for statement in statements {
writeln!(f, "{}", indent(statement.to_string()))?;
}
write!(f, "}}")
write!(f, ")")
}
Stmt::ExprStmt { expr } => write!(f, "{expr};"),
Stmt::Break => write!(f, "break;"),
Stmt::Break => write!(f, "(break)"),
Stmt::Return { expr } => {
write!(f, "return {expr};")
write!(f, "(return {expr})")
}
}
}