mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
updated a bunch of stuff
This commit is contained in:
parent
660464638f
commit
67bb5fe8fd
24 changed files with 683 additions and 702 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue