Make integer coercion infallible

This commit is contained in:
Alex Bethel 2021-07-16 18:56:45 -05:00
parent f6e6f8cea1
commit 56623de96a
2 changed files with 24 additions and 30 deletions

View file

@ -143,8 +143,8 @@ impl ExecEnv {
match kind { match kind {
// Arithmetic operators. // Arithmetic operators.
Add | Subtract | Multiply | Divide => { Add | Subtract | Multiply | Divide => {
let lhs = lhs.try_into_i32(&expr.span)?; let lhs = lhs.into_i32();
let rhs = rhs.try_into_i32(&expr.span)?; let rhs = rhs.into_i32();
let res = match kind { let res = match kind {
Add => lhs.checked_add(rhs), Add => lhs.checked_add(rhs),
@ -159,8 +159,8 @@ impl ExecEnv {
// Numeric comparisons. // Numeric comparisons.
Less | Greater => { Less | Greater => {
let lhs = lhs.try_into_i32(&expr.span)?; let lhs = lhs.into_i32();
let rhs = rhs.try_into_i32(&expr.span)?; let rhs = rhs.into_i32();
let res = match kind { let res = match kind {
Less => lhs < rhs, Less => lhs < rhs,
@ -241,8 +241,7 @@ impl ExecEnv {
.as_ref() .as_ref()
.map(|tape_len| { .map(|tape_len| {
self.eval_expr(tape_len) self.eval_expr(tape_len)
.and_then(|v| v.try_into_i32(&stmt.span)) .map(|v| v.into_i32() as usize)
.map(|len| len as usize)
}) })
.unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?, .unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?,
}), }),

View file

@ -1,11 +1,8 @@
use std::{cell::RefCell, fmt::Display, io::Write, ops::Range, rc::Rc}; use std::{cell::RefCell, fmt::Display, io::Write, rc::Rc};
use rand::Rng; use rand::Rng;
use crate::{ use crate::{ast::Stmt, consts};
ast::Stmt,
error::{Error, ErrorKind},
};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Abool { pub enum Abool {
@ -119,35 +116,33 @@ impl Value {
.expect("Failed to write to Brainfuck input"); .expect("Failed to write to Brainfuck input");
} }
/// Attempt to coerce a Value to an integer. If the conversion /// Coerce a value to an integer.
/// fails, the generated error message is associated with the pub fn into_i32(self) -> i32 {
/// given span.
pub fn try_into_i32(self, span: &Range<usize>) -> Result<i32, Error> {
match self { match self {
Value::Int(i) => Ok(i), Value::Abool(a) => a as _,
_ => Err(Error { Value::Bool(b) => b as _,
kind: ErrorKind::TypeError(format!("expected int, got {}", self)), Value::Functio(func) => match func {
span: span.clone(), Functio::BfFunctio {
}), instructions,
tape_len,
} => (instructions.len() + tape_len) as _,
Functio::AbleFunctio { params, body } => (params.len() + body.len()) as _,
},
Value::Int(i) => i,
Value::Nul => consts::ANSWER,
Value::Str(text) => text.parse().unwrap_or(consts::ANSWER),
} }
} }
/// Coerce a Value to a boolean. The conversion cannot fail. /// Coerce a Value to a boolean. The conversion cannot fail.
pub fn into_bool(self) -> bool { pub fn into_bool(self) -> bool {
match self { match self {
// Booleans and abooleans have a trivial conversion.
Value::Bool(b) => b,
Value::Abool(b) => b.into(), Value::Abool(b) => b.into(),
// The empty string is falsey, other strings are truthy. Value::Bool(b) => b,
Value::Str(s) => !s.is_empty(),
// 0 is falsey, nonzero is truthy.
Value::Int(x) => x != 0,
// Functios are always truthy.
Value::Functio(_) => true, Value::Functio(_) => true,
// And nul is truthy as a symbol of the fact that the Value::Int(x) => x != 0,
// deep, fundamental truth of this world is nothing but
// the eternal void.
Value::Nul => true, Value::Nul => true,
Value::Str(s) => !s.is_empty(),
} }
} }
} }