Add support for implicit semicolons in the repl
parent
893dab62c7
commit
3c348e92ca
2
build.rs
2
build.rs
|
@ -47,7 +47,7 @@ fn do_ast_codegen<W: Write>(mut output: W) {
|
||||||
),
|
),
|
||||||
("Grouping", "expr: Box<Expr>"),
|
("Grouping", "expr: Box<Expr>"),
|
||||||
("Unary", "expr: Box<Expr>, operator: Token"),
|
("Unary", "expr: Box<Expr>, operator: Token"),
|
||||||
("Literal", "value: LiteralValue"),
|
("Literal", "value: LiteralValue, token: Token"),
|
||||||
("Variable", "name: Token"),
|
("Variable", "name: Token"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,11 @@ impl<'a> InterpreterRunner<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprVisitor<Result<EvaluatedValue, ScriptError>> for InterpreterRunner<'_> {
|
impl ExprVisitor<Result<EvaluatedValue, ScriptError>> for InterpreterRunner<'_> {
|
||||||
fn visit_literal(&mut self, value: &LiteralValue) -> Result<EvaluatedValue, ScriptError> {
|
fn visit_literal(
|
||||||
|
&mut self,
|
||||||
|
value: &LiteralValue,
|
||||||
|
_token: &Token,
|
||||||
|
) -> Result<EvaluatedValue, ScriptError> {
|
||||||
Ok(value.into())
|
Ok(value.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use std::{
|
use std::collections::{hash_map::Entry, BTreeMap, HashMap};
|
||||||
borrow::BorrowMut,
|
|
||||||
collections::{hash_map::Entry, BTreeMap, HashMap},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::value::EvaluatedValue;
|
use super::value::EvaluatedValue;
|
||||||
|
|
||||||
|
@ -27,24 +24,11 @@ impl Default for Environment {
|
||||||
impl Environment {
|
impl Environment {
|
||||||
const ROOT_KEY: u32 = 0;
|
const ROOT_KEY: u32 = 0;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn root_scope(&self) -> &Scope {
|
|
||||||
self.scopes
|
|
||||||
.get(&Self::ROOT_KEY)
|
|
||||||
.expect("no root environment defined")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn root_scope_mut(&mut self) -> &mut Scope {
|
|
||||||
self.scopes
|
|
||||||
.get_mut(&Self::ROOT_KEY)
|
|
||||||
.expect("no root environment defined")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enter_scope(&mut self) {
|
pub fn enter_scope(&mut self) {
|
||||||
let parent_key = self.current_scope_key();
|
let parent_key = self.current_scope_key();
|
||||||
let child_key = self.next_scope_key();
|
let child_key = self.next_scope_key();
|
||||||
|
|
50
src/lib.rs
50
src/lib.rs
|
@ -3,7 +3,9 @@
|
||||||
use crate::ast::{Expr, ExprVisitor};
|
use crate::ast::{Expr, ExprVisitor};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use ast::Stmt;
|
||||||
pub use eval::Interpreter;
|
pub use eval::Interpreter;
|
||||||
|
use lex::Token;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
mod eval;
|
mod eval;
|
||||||
|
@ -37,26 +39,54 @@ impl Display for ScriptErrors {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(script: &str) -> Result<(), ScriptErrors> {
|
pub fn run_script(script: &str) -> Result<(), ScriptErrors> {
|
||||||
let mut interpreter = Interpreter::new();
|
run_on(&mut Interpreter::new(), script, parse_script_tokens)
|
||||||
run_on(&mut interpreter, script)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_on(interpreter: &mut Interpreter, script: &str) -> Result<(), ScriptErrors> {
|
pub fn run_repl_input(interpreter: &mut Interpreter, script: &str) -> Result<(), ScriptErrors> {
|
||||||
|
run_on(interpreter, script, parse_repl_tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_on<F: FnOnce(Vec<Token>) -> Result<Vec<Stmt>, ScriptErrors>>(
|
||||||
|
interpreter: &mut Interpreter,
|
||||||
|
script: &str,
|
||||||
|
parse_tokens: F,
|
||||||
|
) -> Result<(), ScriptErrors> {
|
||||||
|
let tokens = lex_source(script)?;
|
||||||
|
let parsed = parse_tokens(tokens)?;
|
||||||
|
let eval_res = interpreter.evaluate(&parsed);
|
||||||
|
|
||||||
|
eval_res.map_err(|err| ScriptErrors(vec![err]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex_source(script: &str) -> Result<Vec<Token>, ScriptErrors> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let tokens = lex::scan_source(script, |err| errors.push(err));
|
let tokens = lex::scan_source(script, |err| errors.push(err));
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into());
|
return Err(errors.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_script_tokens(tokens: Vec<Token>) -> Result<Vec<Stmt>, ScriptErrors> {
|
||||||
|
let mut errors = vec![];
|
||||||
let parsed = parse::parse_program(tokens.into_iter(), |err| errors.push(err));
|
let parsed = parse::parse_program(tokens.into_iter(), |err| errors.push(err));
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return Err(errors.into());
|
return Err(errors.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let eval_res = interpreter.evaluate(&parsed);
|
Ok(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
eval_res.map_err(|err| ScriptErrors(vec![err]))
|
fn parse_repl_tokens(tokens: Vec<Token>) -> Result<Vec<Stmt>, ScriptErrors> {
|
||||||
|
let mut errors = vec![];
|
||||||
|
let parsed = parse::parse_repl_line(tokens.into_iter(), |err| errors.push(err));
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ASTPrinter;
|
struct ASTPrinter;
|
||||||
|
@ -92,7 +122,7 @@ impl ExprVisitor<String> for ASTPrinter {
|
||||||
self.parenthesize(operator.lexeme(), &[expr])
|
self.parenthesize(operator.lexeme(), &[expr])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_literal(&mut self, value: &ast::LiteralValue) -> String {
|
fn visit_literal(&mut self, value: &ast::LiteralValue, _token: &lex::Token) -> String {
|
||||||
match value {
|
match value {
|
||||||
ast::LiteralValue::Nil => "nil".to_string(),
|
ast::LiteralValue::Nil => "nil".to_string(),
|
||||||
ast::LiteralValue::False => "false".to_string(),
|
ast::LiteralValue::False => "false".to_string(),
|
||||||
|
@ -106,7 +136,7 @@ impl ExprVisitor<String> for ASTPrinter {
|
||||||
name.lexeme().to_owned()
|
name.lexeme().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_assign(&mut self, name: &lex::Token, value: &Expr) -> String {
|
fn visit_assign(&mut self, _name: &lex::Token, _value: &Expr) -> String {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,10 +155,12 @@ mod tests {
|
||||||
let result = ASTPrinter.visit_expr(&Expr::Binary {
|
let result = ASTPrinter.visit_expr(&Expr::Binary {
|
||||||
left: Box::new(Expr::Literal {
|
left: Box::new(Expr::Literal {
|
||||||
value: ast::LiteralValue::Number(123_f64),
|
value: ast::LiteralValue::Number(123_f64),
|
||||||
|
token: Token::new(TokenKind::Number(456_f64), "456".to_string(), 10),
|
||||||
}),
|
}),
|
||||||
operator: Token::new(TokenKind::Plus, "+".to_string(), 1),
|
operator: Token::new(TokenKind::Plus, "+".to_string(), 1),
|
||||||
right: Box::new(Expr::Literal {
|
right: Box::new(Expr::Literal {
|
||||||
value: ast::LiteralValue::Number(456_f64),
|
value: ast::LiteralValue::Number(456_f64),
|
||||||
|
token: Token::new(TokenKind::Number(456_f64), "456".to_string(), 10),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -141,6 +173,7 @@ mod tests {
|
||||||
left: Box::new(Expr::Unary {
|
left: Box::new(Expr::Unary {
|
||||||
expr: Box::new(Expr::Literal {
|
expr: Box::new(Expr::Literal {
|
||||||
value: ast::LiteralValue::Number(123_f64),
|
value: ast::LiteralValue::Number(123_f64),
|
||||||
|
token: Token::new(TokenKind::Number(123_f64), "123".to_string(), 10),
|
||||||
}),
|
}),
|
||||||
operator: Token::new(TokenKind::Minus, "-".to_string(), 1),
|
operator: Token::new(TokenKind::Minus, "-".to_string(), 1),
|
||||||
}),
|
}),
|
||||||
|
@ -148,6 +181,7 @@ mod tests {
|
||||||
right: Box::new(Expr::Grouping {
|
right: Box::new(Expr::Grouping {
|
||||||
expr: Box::new(Expr::Literal {
|
expr: Box::new(Expr::Literal {
|
||||||
value: ast::LiteralValue::Number(456.789_f64),
|
value: ast::LiteralValue::Number(456.789_f64),
|
||||||
|
token: Token::new(TokenKind::Number(456.789_f64), "456.789".to_string(), 10),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -33,7 +33,7 @@ fn run_prompt() -> anyhow::Result<()> {
|
||||||
print_prompt()?;
|
print_prompt()?;
|
||||||
for line_res in stdin.lines() {
|
for line_res in stdin.lines() {
|
||||||
let line = line_res.with_context(|| "failed to read input line")?;
|
let line = line_res.with_context(|| "failed to read input line")?;
|
||||||
let run_res = jlox_rust::run_on(&mut interpreter, &line);
|
let run_res = jlox_rust::run_repl_input(&mut interpreter, &line);
|
||||||
if let Err(err) = run_res {
|
if let Err(err) = run_res {
|
||||||
eprintln!("{err}");
|
eprintln!("{err}");
|
||||||
}
|
}
|
||||||
|
@ -46,5 +46,5 @@ fn run_prompt() -> anyhow::Result<()> {
|
||||||
|
|
||||||
fn run_file(path: &str) -> anyhow::Result<()> {
|
fn run_file(path: &str) -> anyhow::Result<()> {
|
||||||
let script = fs::read_to_string(path)?;
|
let script = fs::read_to_string(path)?;
|
||||||
jlox_rust::run(&script).map_err(anyhow::Error::new)
|
jlox_rust::run_script(&script).map_err(anyhow::Error::new)
|
||||||
}
|
}
|
||||||
|
|
127
src/parse.rs
127
src/parse.rs
|
@ -1,7 +1,5 @@
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Expr, LiteralValue, Stmt},
|
ast::{Expr, LiteralValue, Stmt},
|
||||||
lex::{Token, TokenKind},
|
lex::{Token, TokenKind},
|
||||||
|
@ -15,10 +13,58 @@ struct ParseError {
|
||||||
line: Option<usize>,
|
line: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ParsedStatement {
|
||||||
|
Stmt { stmt: Stmt },
|
||||||
|
ImplicitStmt { stmt: Stmt, line_number: usize },
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_program<I: Iterator<Item = Token>, F: FnMut(ScriptError)>(
|
pub fn parse_program<I: Iterator<Item = Token>, F: FnMut(ScriptError)>(
|
||||||
iter: I,
|
iter: I,
|
||||||
mut on_error: F,
|
mut on_error: F,
|
||||||
) -> Vec<Stmt> {
|
) -> Vec<Stmt> {
|
||||||
|
let statements = parse(iter, &mut on_error);
|
||||||
|
ensure_parsed_statements_explicit(statements, on_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_repl_line<I: Iterator<Item = Token> + Clone, F: FnMut(ScriptError)>(
|
||||||
|
iter: I,
|
||||||
|
mut on_error: F,
|
||||||
|
) -> Vec<Stmt> {
|
||||||
|
let mut statements = parse(iter, &mut on_error);
|
||||||
|
if statements.len() == 1 {
|
||||||
|
if let ParsedStatement::ImplicitStmt { stmt, .. } = statements.remove(0) {
|
||||||
|
return vec![stmt];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_parsed_statements_explicit(statements, on_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_parsed_statements_explicit<F: FnMut(ScriptError)>(
|
||||||
|
statements: Vec<ParsedStatement>,
|
||||||
|
mut on_error: F,
|
||||||
|
) -> Vec<Stmt> {
|
||||||
|
statements
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|parsed| match ensure_explicit_statement(parsed) {
|
||||||
|
Ok(stmt) => Some(stmt),
|
||||||
|
Err(err) => {
|
||||||
|
on_error(ScriptError {
|
||||||
|
message: err.message.clone(),
|
||||||
|
// TODO: This sucks and we should make ScriptError handle optional line numbers somehow
|
||||||
|
line: err.line.unwrap_or_default(),
|
||||||
|
location: String::new(),
|
||||||
|
});
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse<I: Iterator<Item = Token>, F: FnMut(ScriptError)>(
|
||||||
|
iter: I,
|
||||||
|
mut on_error: F,
|
||||||
|
) -> Vec<ParsedStatement> {
|
||||||
let peekable_iter = &mut iter.peekable();
|
let peekable_iter = &mut iter.peekable();
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
while let Some(peeked_token) = peekable_iter.peek() {
|
while let Some(peeked_token) = peekable_iter.peek() {
|
||||||
|
@ -74,7 +120,7 @@ fn synchronize_to_next_statement<I: Iterator<Item = Token>>(iter: &mut I) {
|
||||||
|
|
||||||
fn parse_declaration<I: Iterator<Item = Token>>(
|
fn parse_declaration<I: Iterator<Item = Token>>(
|
||||||
iter: &mut Peekable<I>,
|
iter: &mut Peekable<I>,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<ParsedStatement, ParseError> {
|
||||||
let parse_res = if let Some(var_token) = match_next_token(iter, &TokenKind::Var) {
|
let parse_res = if let Some(var_token) = match_next_token(iter, &TokenKind::Var) {
|
||||||
parse_var_declaration(iter).map_err(|err| ParseError {
|
parse_var_declaration(iter).map_err(|err| ParseError {
|
||||||
line: Some(var_token.line()),
|
line: Some(var_token.line()),
|
||||||
|
@ -84,7 +130,7 @@ fn parse_declaration<I: Iterator<Item = Token>>(
|
||||||
parse_statement(iter)
|
parse_statement(iter)
|
||||||
};
|
};
|
||||||
|
|
||||||
if parse_res.is_err() {
|
if parse_res.is_err() || matches!(parse_res, Ok(ParsedStatement::ImplicitStmt { .. })) {
|
||||||
synchronize_to_next_statement(iter);
|
synchronize_to_next_statement(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +139,7 @@ fn parse_declaration<I: Iterator<Item = Token>>(
|
||||||
|
|
||||||
fn parse_var_declaration<I: Iterator<Item = Token>>(
|
fn parse_var_declaration<I: Iterator<Item = Token>>(
|
||||||
iter: &mut Peekable<I>,
|
iter: &mut Peekable<I>,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<ParsedStatement, ParseError> {
|
||||||
let name_token =
|
let name_token =
|
||||||
match_token_kind!(iter, TokenKind::Identifier(_name)).map_err(|maybe_token| {
|
match_token_kind!(iter, TokenKind::Identifier(_name)).map_err(|maybe_token| {
|
||||||
ParseError {
|
ParseError {
|
||||||
|
@ -108,22 +154,25 @@ fn parse_var_declaration<I: Iterator<Item = Token>>(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
match_token_kind!(iter, TokenKind::SemiColon).map_err(|maybe_token| ParseError {
|
let line_number = name_token.line();
|
||||||
message: "Expected ';' after variable declaration".to_string(),
|
let stmt = Stmt::Var {
|
||||||
line: maybe_token.map(Token::line),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(Stmt::Var {
|
|
||||||
name: name_token,
|
name: name_token,
|
||||||
initializer,
|
initializer,
|
||||||
})
|
};
|
||||||
|
if match_token_kind!(iter, TokenKind::SemiColon).is_ok() {
|
||||||
|
Ok(ParsedStatement::Stmt { stmt })
|
||||||
|
} else {
|
||||||
|
Ok(ParsedStatement::ImplicitStmt { stmt, line_number })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Stmt, ParseError> {
|
fn parse_statement<I: Iterator<Item = Token>>(
|
||||||
|
iter: &mut Peekable<I>,
|
||||||
|
) -> Result<ParsedStatement, ParseError> {
|
||||||
if match_token_kind!(iter, TokenKind::Print).is_ok() {
|
if match_token_kind!(iter, TokenKind::Print).is_ok() {
|
||||||
parse_statement_containing_expression(iter, |expression| Stmt::Print { expression })
|
parse_statement_containing_expression(iter, |expression| Stmt::Print { expression })
|
||||||
} else if match_token_kind!(iter, TokenKind::LeftBrace).is_ok() {
|
} else if match_token_kind!(iter, TokenKind::LeftBrace).is_ok() {
|
||||||
parse_block_statement(iter)
|
parse_block_statement(iter).map(|stmt| ParsedStatement::Stmt { stmt })
|
||||||
} else {
|
} else {
|
||||||
parse_statement_containing_expression(iter, |expression| Stmt::Expression { expression })
|
parse_statement_containing_expression(iter, |expression| Stmt::Expression { expression })
|
||||||
}
|
}
|
||||||
|
@ -132,14 +181,16 @@ fn parse_statement<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<
|
||||||
fn parse_statement_containing_expression<I: Iterator<Item = Token>, F: Fn(Expr) -> Stmt>(
|
fn parse_statement_containing_expression<I: Iterator<Item = Token>, F: Fn(Expr) -> Stmt>(
|
||||||
iter: &mut Peekable<I>,
|
iter: &mut Peekable<I>,
|
||||||
make_statement: F,
|
make_statement: F,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<ParsedStatement, ParseError> {
|
||||||
let expression = parse_expression(iter)?;
|
let expression = parse_expression(iter)?;
|
||||||
match_token_kind!(iter, TokenKind::SemiColon)
|
let line_number = find_line_number(&expression);
|
||||||
.map(|_semicolon| make_statement(expression))
|
let stmt = make_statement(expression);
|
||||||
.map_err(|maybe_token| ParseError {
|
|
||||||
message: "Expected a ';' after expression".to_string(),
|
if match_token_kind!(iter, TokenKind::SemiColon).is_ok() {
|
||||||
line: maybe_token.map(Token::line),
|
Ok(ParsedStatement::Stmt { stmt })
|
||||||
})
|
} else {
|
||||||
|
Ok(ParsedStatement::ImplicitStmt { stmt, line_number })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block_statement<I: Iterator<Item = Token>>(
|
fn parse_block_statement<I: Iterator<Item = Token>>(
|
||||||
|
@ -148,12 +199,24 @@ fn parse_block_statement<I: Iterator<Item = Token>>(
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
while match_token_kind!(iter, TokenKind::RightBrace).is_err() {
|
while match_token_kind!(iter, TokenKind::RightBrace).is_err() {
|
||||||
let statement = parse_declaration(iter)?;
|
let statement = parse_declaration(iter)?;
|
||||||
statements.push(statement);
|
let explicit_statement = ensure_explicit_statement(statement)?;
|
||||||
|
|
||||||
|
statements.push(explicit_statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Stmt::Block { statements })
|
Ok(Stmt::Block { statements })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_explicit_statement(parsed: ParsedStatement) -> Result<Stmt, ParseError> {
|
||||||
|
match parsed {
|
||||||
|
ParsedStatement::Stmt { stmt, .. } => Ok(stmt),
|
||||||
|
ParsedStatement::ImplicitStmt { line_number, .. } => Err(ParseError {
|
||||||
|
message: "Expected a ';' after expression".to_string(),
|
||||||
|
line: line_number.into(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_expression<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Expr, ParseError> {
|
fn parse_expression<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Expr, ParseError> {
|
||||||
parse_assignment(iter)
|
parse_assignment(iter)
|
||||||
}
|
}
|
||||||
|
@ -252,21 +315,27 @@ fn parse_primary<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Ex
|
||||||
match token.kind() {
|
match token.kind() {
|
||||||
TokenKind::False => Ok(Expr::Literal {
|
TokenKind::False => Ok(Expr::Literal {
|
||||||
value: LiteralValue::False,
|
value: LiteralValue::False,
|
||||||
|
token,
|
||||||
}),
|
}),
|
||||||
TokenKind::True => Ok(Expr::Literal {
|
TokenKind::True => Ok(Expr::Literal {
|
||||||
value: LiteralValue::True,
|
value: LiteralValue::True,
|
||||||
|
token,
|
||||||
}),
|
}),
|
||||||
TokenKind::Nil => Ok(Expr::Literal {
|
TokenKind::Nil => Ok(Expr::Literal {
|
||||||
value: LiteralValue::Nil,
|
value: LiteralValue::Nil,
|
||||||
|
token,
|
||||||
}),
|
}),
|
||||||
TokenKind::Number(number) => Ok(Expr::Literal {
|
TokenKind::Number(number) => Ok(Expr::Literal {
|
||||||
value: LiteralValue::Number(*number),
|
value: LiteralValue::Number(*number),
|
||||||
|
token,
|
||||||
}),
|
}),
|
||||||
TokenKind::String(_) => {
|
TokenKind::String(_) => {
|
||||||
|
// TODO: we used to do this with into_kind, but we need to change Token to Rc<str> to fix cloning issues
|
||||||
// special case to avoid cloning
|
// special case to avoid cloning
|
||||||
match token.into_kind() {
|
match token.kind() {
|
||||||
TokenKind::String(string) => Ok(Expr::Literal {
|
TokenKind::String(string) => Ok(Expr::Literal {
|
||||||
value: LiteralValue::String(string),
|
value: LiteralValue::String(string.clone()),
|
||||||
|
token,
|
||||||
}),
|
}),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -314,3 +383,13 @@ fn match_next_token<I: Iterator<Item = Token>>(
|
||||||
None | Some(_) => None,
|
None | Some(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_line_number(expr: &Expr) -> usize {
|
||||||
|
match expr {
|
||||||
|
Expr::Assign { name, .. } | Expr::Variable { name } => name.line(),
|
||||||
|
Expr::Unary { operator, .. } => operator.line(),
|
||||||
|
Expr::Literal { token, .. } => token.line(),
|
||||||
|
Expr::Binary { left, .. } => find_line_number(left),
|
||||||
|
Expr::Grouping { expr } => find_line_number(expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue