mirror of
https://github.com/TheM1Stery/izanami.git
synced 2025-04-20 08:51:12 +00:00
Compare commits
No commits in common. "be15364e3be07105310851f7800c3108b18831f5" and "9f903556f9e3c1eae571a0baee23136c2e328b4c" have entirely different histories.
be15364e3b
...
9f903556f9
@ -4,7 +4,9 @@ use crate::token::{LiteralType, Token};
|
||||
pub enum Expr {
|
||||
Ternary {
|
||||
first: Box<Expr>,
|
||||
first_op: Token,
|
||||
second: Box<Expr>,
|
||||
second_op: Token,
|
||||
third: Box<Expr>,
|
||||
},
|
||||
Binary {
|
||||
@ -16,7 +18,7 @@ pub enum Expr {
|
||||
expression: Box<Expr>,
|
||||
},
|
||||
Literal {
|
||||
value: LiteralType,
|
||||
value: Option<LiteralType>,
|
||||
},
|
||||
Unary {
|
||||
op: Token,
|
||||
|
@ -1,98 +0,0 @@
|
||||
use crate::{
|
||||
ast::Expr,
|
||||
token::{LiteralType, Token, TokenType},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RuntimeError {
|
||||
pub token: Token,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
pub fn new(token: &Token, message: &str) -> RuntimeError {
|
||||
RuntimeError {
|
||||
token: token.clone(),
|
||||
message: message.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interpret(expr: &Expr) -> Result<LiteralType, RuntimeError> {
|
||||
match expr {
|
||||
Expr::Ternary {
|
||||
first,
|
||||
second,
|
||||
third,
|
||||
..
|
||||
} => ternary(first, second, third),
|
||||
Expr::Binary { left, op, right } => binary(&interpret(left)?, &interpret(right)?, op),
|
||||
Expr::Grouping { expression } => interpret(expression),
|
||||
Expr::Literal { value } => Ok(value.clone()),
|
||||
Expr::Unary { op, right } => Ok(unary(&interpret(right)?, op)),
|
||||
}
|
||||
}
|
||||
|
||||
fn ternary(first: &Expr, second: &Expr, third: &Expr) -> Result<LiteralType, RuntimeError> {
|
||||
let first = interpret(first)?;
|
||||
if is_truthy(&first) {
|
||||
return interpret(second);
|
||||
}
|
||||
interpret(third)
|
||||
}
|
||||
|
||||
fn binary(
|
||||
left: &LiteralType,
|
||||
right: &LiteralType,
|
||||
op: &Token,
|
||||
) -> Result<LiteralType, RuntimeError> {
|
||||
use LiteralType::{Bool, Number, String};
|
||||
use TokenType::{
|
||||
BangEqual, Comma, EqualEqual, Greater, GreaterEqual, Less, LessEqual, Minus, Plus, Slash,
|
||||
Star,
|
||||
};
|
||||
|
||||
match (op.t_type, &left, &right) {
|
||||
(Greater, Number(left), Number(right)) => Ok(Bool(left > right)),
|
||||
(GreaterEqual, Number(left), Number(right)) => Ok(Bool(left >= right)),
|
||||
(Less, Number(left), Number(right)) => Ok(Bool(left < right)),
|
||||
(LessEqual, Number(left), Number(right)) => Ok(Bool(left <= right)),
|
||||
(BangEqual, _, _) => Ok(Bool(!is_equal(left, right))),
|
||||
(EqualEqual, _, _) => Ok(Bool(is_equal(left, right))),
|
||||
(Minus, Number(left), Number(right)) => Ok(Number(left - right)),
|
||||
(Plus, Number(left), Number(right)) => Ok(Number(left + right)),
|
||||
(Plus, String(left), String(right)) => Ok(String(format!("{left}{right}"))),
|
||||
(Slash, Number(left), Number(right)) => Ok(Number(left / right)),
|
||||
(Star, Number(left), Number(right)) => Ok(Number(left * right)),
|
||||
(Comma, _,_) => Ok(right.clone()),
|
||||
(Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers")),
|
||||
(Plus, _, _) => Err(RuntimeError::new(op, "Operands must be two numbers or two strings")),
|
||||
/* comma operator discard the left operand, so we just return the evaluation of the right operand */
|
||||
|
||||
_ => unreachable!("Shouldn't happen. Expr::Binary for interpret. Some case is a binary operation that wasn't matched")
|
||||
}
|
||||
}
|
||||
|
||||
fn unary(right: &LiteralType, op: &Token) -> LiteralType {
|
||||
match (op.t_type, &right) {
|
||||
(TokenType::Minus, LiteralType::Number(num)) => LiteralType::Number(-num),
|
||||
(TokenType::Bang, _) => LiteralType::Bool(!is_truthy(right)),
|
||||
_ => unreachable!("Shouldn't happen. Expr::Unary for interpret"),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_truthy(literal: &LiteralType) -> bool {
|
||||
match literal {
|
||||
LiteralType::Nil => false,
|
||||
LiteralType::Bool(val) => *val,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_equal(left: &LiteralType, right: &LiteralType) -> bool {
|
||||
match (left, right) {
|
||||
(LiteralType::Nil, LiteralType::Nil) => true,
|
||||
(LiteralType::Nil, _) => false,
|
||||
_ => left == right,
|
||||
}
|
||||
}
|
44
src/lib.rs
44
src/lib.rs
@ -1,55 +1,43 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::Display,
|
||||
fs,
|
||||
io::{self, Write},
|
||||
};
|
||||
|
||||
use interpreter::RuntimeError;
|
||||
use parser::Parser;
|
||||
use parser::{ParseError, Parser};
|
||||
use printer::pretty_print;
|
||||
use scanner::Scanner;
|
||||
use token::{Token, TokenType};
|
||||
|
||||
mod ast;
|
||||
mod interpreter;
|
||||
mod parser;
|
||||
mod printer;
|
||||
mod scanner;
|
||||
mod token;
|
||||
mod utils;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RunError {
|
||||
FileReadError(io::Error),
|
||||
OtherError(Box<dyn Error>), // to be added,
|
||||
RuntimeError(RuntimeError),
|
||||
}
|
||||
|
||||
impl<E: Error + 'static> From<E> for RunError {
|
||||
fn from(value: E) -> Self {
|
||||
Self::OtherError(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_file(path: &str) -> Result<(), RunError> {
|
||||
let file = fs::read_to_string(path).map_err(RunError::FileReadError)?;
|
||||
pub fn run_file(path: &str) -> Result<(), Box<dyn Error>> {
|
||||
let file = fs::read_to_string(path)?;
|
||||
|
||||
run(&file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(src: &str) -> Result<(), RunError> {
|
||||
pub fn run(src: &str) -> Result<(), Box<dyn Error>> {
|
||||
let mut scanner = Scanner::new(src.to_string());
|
||||
let tokens = scanner.scan_tokens()?;
|
||||
|
||||
let mut parser = Parser::new(tokens);
|
||||
|
||||
let expression = parser.parse().inspect_err(|e| error(&e.token, &e.msg))?;
|
||||
let expression = parser.parse();
|
||||
|
||||
let interpreted_value = interpreter::interpret(&expression)
|
||||
.inspect_err(runtime_error)
|
||||
.map_err(RunError::RuntimeError)?;
|
||||
|
||||
println!("{interpreted_value}");
|
||||
match expression {
|
||||
Ok(expr) => println!("{}", pretty_print(&expr)),
|
||||
Err(e) => {
|
||||
error(e.token, &e.msg);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -76,13 +64,9 @@ pub fn report(line: usize, location: &str, message: &str) {
|
||||
eprintln!("[line {line}] Error {location}: {message}");
|
||||
}
|
||||
|
||||
fn error(token: &Token, message: &str) {
|
||||
fn error(token: Token, message: &str) {
|
||||
match token.t_type {
|
||||
TokenType::EOF => report(token.line, " at end", message),
|
||||
_ => report(token.line, &format!(" at '{}'", token.lexeme), message),
|
||||
}
|
||||
}
|
||||
|
||||
fn runtime_error(err: &RuntimeError) {
|
||||
eprintln!("{}\n[line {}]", err.message, err.token.line);
|
||||
}
|
||||
|
14
src/main.rs
14
src/main.rs
@ -1,6 +1,6 @@
|
||||
use std::{env::args_os, ffi::OsString, process::ExitCode};
|
||||
|
||||
use izanami::{run_file, run_prompt, RunError};
|
||||
use izanami::{run_file, run_prompt};
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args: Vec<OsString> = args_os().collect();
|
||||
@ -11,18 +11,10 @@ fn main() -> ExitCode {
|
||||
} else if args.len() == 2 {
|
||||
let result = run_file(args[1].to_str().unwrap());
|
||||
|
||||
if let Err(RunError::FileReadError(e)) = result {
|
||||
println!("Couldn't read the file. Reason: {}", e);
|
||||
if let Err(res) = result {
|
||||
println!("Couldn't read the file. Reason: {}", &*res);
|
||||
return ExitCode::from(1);
|
||||
}
|
||||
if let Err(RunError::OtherError(e)) = result {
|
||||
println!("Error occured. Error: {}", e);
|
||||
return ExitCode::from(75);
|
||||
}
|
||||
|
||||
if let Err(RunError::RuntimeError(r)) = result {
|
||||
return ExitCode::from(70);
|
||||
}
|
||||
} else {
|
||||
let result = run_prompt();
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::{
|
||||
ast::Expr,
|
||||
token::{LiteralType, Token, TokenType},
|
||||
@ -16,14 +14,6 @@ pub struct ParseError {
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
impl Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "ParseError: {} {}", self.token, self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseError {}
|
||||
|
||||
impl Parser<'_> {
|
||||
pub fn new(tokens: &Vec<Token>) -> Parser<'_> {
|
||||
Parser { tokens, current: 0 }
|
||||
@ -50,12 +40,15 @@ impl Parser<'_> {
|
||||
let expr = self.equality()?;
|
||||
|
||||
if self.match_token(&[Question]) {
|
||||
let first_op = self.previous().clone();
|
||||
let second = self.expression()?;
|
||||
let _ = self.consume(Colon, "Expected : after ternary operator ?")?;
|
||||
let second_op = self.consume(Colon, "Expected : after ternary operator ?")?;
|
||||
let third = self.ternary()?;
|
||||
return Ok(Expr::Ternary {
|
||||
first: Box::new(expr),
|
||||
first_op,
|
||||
second: Box::new(second),
|
||||
second_op,
|
||||
third: Box::new(third),
|
||||
});
|
||||
}
|
||||
@ -106,30 +99,25 @@ impl Parser<'_> {
|
||||
use LiteralType::*;
|
||||
use TokenType::*;
|
||||
|
||||
fn create_literal(l_type: LiteralType) -> Expr {
|
||||
fn create_literal(l_type: Option<LiteralType>) -> Expr {
|
||||
Expr::Literal { value: l_type }
|
||||
}
|
||||
|
||||
if self.match_token(&[False]) {
|
||||
return Ok(create_literal(Bool(false)));
|
||||
return Ok(create_literal(Some(Bool(false))));
|
||||
}
|
||||
|
||||
if self.match_token(&[True]) {
|
||||
return Ok(create_literal(Bool(true)));
|
||||
return Ok(create_literal(Some(Bool(true))));
|
||||
}
|
||||
|
||||
if self.match_token(&[TokenType::Number, TokenType::String]) {
|
||||
return Ok(create_literal(
|
||||
self.previous()
|
||||
.literal
|
||||
.clone()
|
||||
.expect("The number and string token should have a literal"),
|
||||
));
|
||||
return Ok(create_literal(self.previous().literal.clone()));
|
||||
}
|
||||
|
||||
// i included the enum name bcs of ambiguity of LiteralType and TokenType
|
||||
if self.match_token(&[TokenType::Nil]) {
|
||||
return Ok(create_literal(LiteralType::Nil));
|
||||
return Ok(create_literal(Some(LiteralType::Nil)));
|
||||
}
|
||||
|
||||
if self.match_token(&[LeftParen]) {
|
||||
|
@ -5,17 +5,23 @@ pub fn pretty_print(expr: &Expr) -> String {
|
||||
Expr::Binary { left, op, right } => parenthesize(&op.lexeme, &[left, right]),
|
||||
Expr::Grouping { expression } => parenthesize("group", &[expression]),
|
||||
Expr::Literal { value } => match value {
|
||||
LiteralType::String(v) => v.to_string(),
|
||||
LiteralType::Number(v) => v.to_string(),
|
||||
LiteralType::Bool(v) => v.to_string(),
|
||||
LiteralType::Nil => "Nil".to_string(),
|
||||
Some(LiteralType::String(v)) => v.to_string(),
|
||||
Some(LiteralType::Number(v)) => v.to_string(),
|
||||
Some(LiteralType::Bool(v)) => v.to_string(),
|
||||
Some(LiteralType::Nil) => "Nil".to_string(),
|
||||
None => "None".to_string(),
|
||||
},
|
||||
Expr::Unary { op, right } => parenthesize(&op.lexeme, &[right]),
|
||||
Expr::Ternary {
|
||||
first,
|
||||
first_op,
|
||||
second,
|
||||
second_op,
|
||||
third,
|
||||
} => parenthesize("?:", &[first, second, third]),
|
||||
} => parenthesize(
|
||||
&format!("{}{}", first_op.lexeme, second_op.lexeme),
|
||||
&[first, second, third],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +50,7 @@ mod test {
|
||||
use TokenType::*;
|
||||
let expression = Binary {
|
||||
left: Box::new(Literal {
|
||||
value: LiteralType::Number(10.2),
|
||||
value: Some(LiteralType::Number(10.2)),
|
||||
}),
|
||||
op: Token {
|
||||
t_type: Plus,
|
||||
@ -53,7 +59,7 @@ mod test {
|
||||
line: 0,
|
||||
},
|
||||
right: Box::new(Literal {
|
||||
value: LiteralType::Number(10.2),
|
||||
value: Some(LiteralType::Number(10.2)),
|
||||
}),
|
||||
};
|
||||
|
||||
@ -71,13 +77,13 @@ mod test {
|
||||
left: Box::new(Unary {
|
||||
op: Token::new(Minus, "-", None, 0),
|
||||
right: Box::new(Expr::Literal {
|
||||
value: LiteralType::number_literal(123.0),
|
||||
value: Some(LiteralType::number_literal(123.0)),
|
||||
}),
|
||||
}),
|
||||
op: Token::new(Star, "*", None, 0),
|
||||
right: Box::new(Grouping {
|
||||
expression: Box::new(Literal {
|
||||
value: LiteralType::number_literal(45.67),
|
||||
value: Some(LiteralType::number_literal(45.67)),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
11
src/token.rs
11
src/token.rs
@ -68,17 +68,6 @@ impl LiteralType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LiteralType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LiteralType::String(v) => write!(f, "{v}"),
|
||||
LiteralType::Number(v) => write!(f, "{v:.2}"),
|
||||
LiteralType::Bool(v) => write!(f, "{v}"),
|
||||
LiteralType::Nil => write!(f, "nil"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Token {
|
||||
pub t_type: TokenType,
|
||||
|
Loading…
Reference in New Issue
Block a user