mirror of
https://github.com/TheM1Stery/izanami.git
synced 2025-04-20 00:41:11 +00:00
feat: add stdin native function
did it just for basic exercise lol
This commit is contained in:
parent
4bb2ce1af4
commit
42cf268899
@ -16,7 +16,7 @@ type InterpreterResult = Result<LiteralType, InterpreterSignal>;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RuntimeError {
|
pub struct RuntimeError {
|
||||||
pub token: Token,
|
pub token: Option<Token>,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,9 +26,16 @@ pub struct InterpreterEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeError {
|
impl RuntimeError {
|
||||||
pub fn new(token: &Token, message: &str) -> RuntimeError {
|
pub fn new(token: &Token, message: String) -> Self {
|
||||||
RuntimeError {
|
RuntimeError {
|
||||||
token: token.clone(),
|
token: Some(token.clone()),
|
||||||
|
message: message.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_token(message: String) -> Self {
|
||||||
|
RuntimeError {
|
||||||
|
token: None,
|
||||||
message: message.to_string(),
|
message: message.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +93,12 @@ pub fn interpret(
|
|||||||
clock_function,
|
clock_function,
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
|
environment.globals.borrow_mut().define(
|
||||||
|
"read_input",
|
||||||
|
Some(LiteralType::Callable(Callable::NativeFunction(
|
||||||
|
read_input_function(),
|
||||||
|
))),
|
||||||
|
);
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
execute(statement, &environment)?
|
execute(statement, &environment)?
|
||||||
}
|
}
|
||||||
@ -203,14 +216,10 @@ fn evaluate(expr: &Expr, environment: &InterpreterEnvironment) -> InterpreterRes
|
|||||||
Expr::Variable { name } => curr_environment
|
Expr::Variable { name } => curr_environment
|
||||||
.borrow()
|
.borrow()
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or_else(|| RuntimeError {
|
.ok_or_else(|| RuntimeError::new(name, format!("Undefined variable {}.", name.lexeme)))
|
||||||
token: name.clone(),
|
|
||||||
message: format!("Undefined variable {}.", name.lexeme),
|
|
||||||
})
|
|
||||||
.and_then(|x| {
|
.and_then(|x| {
|
||||||
x.ok_or_else(|| RuntimeError {
|
x.ok_or_else(|| {
|
||||||
token: name.clone(),
|
RuntimeError::new(name, format!("Uninitialized variable {}.", name.lexeme))
|
||||||
message: format!("Uninitialized variable {}.", name.lexeme),
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(InterpreterSignal::RuntimeError),
|
.map_err(InterpreterSignal::RuntimeError),
|
||||||
@ -219,11 +228,9 @@ fn evaluate(expr: &Expr, environment: &InterpreterEnvironment) -> InterpreterRes
|
|||||||
curr_environment
|
curr_environment
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.assign(name, value.clone())
|
.assign(name, value.clone())
|
||||||
.map_err(|_| RuntimeError {
|
.map_err(|_| {
|
||||||
token: name.clone(),
|
RuntimeError::new(name, format!("Undefined variable {}.", name.lexeme))
|
||||||
message: format!("Undefined variable {}.", name.lexeme),
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
Expr::Logical { left, op, right } => {
|
Expr::Logical { left, op, right } => {
|
||||||
@ -254,21 +261,21 @@ fn evaluate(expr: &Expr, environment: &InterpreterEnvironment) -> InterpreterRes
|
|||||||
match callee_result {
|
match callee_result {
|
||||||
LiteralType::Callable(function) => {
|
LiteralType::Callable(function) => {
|
||||||
if arguments.len() as u8 != function.arity() {
|
if arguments.len() as u8 != function.arity() {
|
||||||
Err(RuntimeError {
|
Err(RuntimeError::new(
|
||||||
token: paren.clone(),
|
paren,
|
||||||
message: format!(
|
format!(
|
||||||
"Expected {} arguments but got {}.",
|
"Expected {} arguments but got {}.",
|
||||||
function.arity(),
|
function.arity(),
|
||||||
args.len()
|
args.len()
|
||||||
),
|
),
|
||||||
})?
|
))?
|
||||||
}
|
}
|
||||||
Ok(function.call(&arguments, environment)?)
|
Ok(function.call(&arguments, environment)?)
|
||||||
}
|
}
|
||||||
_ => Err(RuntimeError {
|
_ => Err(RuntimeError::new(
|
||||||
token: paren.clone(),
|
paren,
|
||||||
message: "Can only call functions and classes".to_string(),
|
"Can only call functions and classes".to_string(),
|
||||||
})?,
|
))?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,8 +317,8 @@ fn binary(left: &LiteralType, right: &LiteralType, op: &Token) -> InterpreterRes
|
|||||||
(Star, Number(left), Number(right)) => Ok(Number(left * right)),
|
(Star, Number(left), Number(right)) => Ok(Number(left * right)),
|
||||||
/* comma operator discard the left operand, so we just return the evaluation of the right operand */
|
/* comma operator discard the left operand, so we just return the evaluation of the right operand */
|
||||||
(Comma, _,_) => Ok(right.clone()),
|
(Comma, _,_) => Ok(right.clone()),
|
||||||
(Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers"))?,
|
(Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers".to_string()))?,
|
||||||
(Plus, _, _) => Err(RuntimeError::new(op, "Operands must be two numbers or two strings"))?,
|
(Plus, _, _) => Err(RuntimeError::new(op, "Operands must be two numbers or two strings".to_string()))?,
|
||||||
|
|
||||||
_ => unreachable!("Shouldn't happen. Expr::Binary for evaluate. Some case is a binary operation that wasn't matched")
|
_ => unreachable!("Shouldn't happen. Expr::Binary for evaluate. Some case is a binary operation that wasn't matched")
|
||||||
}
|
}
|
||||||
@ -344,3 +351,17 @@ pub fn is_equal(left: &LiteralType, right: &LiteralType) -> bool {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_input_function() -> NativeFunction {
|
||||||
|
use std::io;
|
||||||
|
let read_input = |_: &[LiteralType]| {
|
||||||
|
let mut buf = String::new();
|
||||||
|
io::stdin()
|
||||||
|
.read_line(&mut buf)
|
||||||
|
.map_err(|_| RuntimeError::no_token("Error reading from stdin".to_string()))?;
|
||||||
|
|
||||||
|
Ok(LiteralType::String(buf))
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeFunction::new("read_input".to_string(), 0, read_input)
|
||||||
|
}
|
||||||
|
@ -107,5 +107,7 @@ fn error(ParseError { token, msg }: &ParseError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_error(err: &RuntimeError) {
|
fn runtime_error(err: &RuntimeError) {
|
||||||
eprintln!("{}\n[line {}]", err.message, err.token.line);
|
if let Some(token) = &err.token {
|
||||||
|
eprintln!("{}\n[line {}]", err.message, token.line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user