forked from AbleScript/ablescript
Fix all 0..0 span placeholders in interpret.rs
This commit is contained in:
parent
d8f462e0b5
commit
ce20611c07
129
src/interpret.rs
129
src/interpret.rs
|
@ -7,17 +7,18 @@
|
||||||
//! evaluate or execute any number of expressions or statements.
|
//! evaluate or execute any number of expressions or statements.
|
||||||
|
|
||||||
#[deny(missing_docs)]
|
#[deny(missing_docs)]
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
collections::HashMap,
|
||||||
io::{stdout, Write},
|
io::{stdout, Write},
|
||||||
|
ops::Range,
|
||||||
process::exit,
|
process::exit,
|
||||||
|
usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Expr, Stmt, StmtKind},
|
ast::{Expr, Iden, Stmt, StmtKind},
|
||||||
base_55,
|
base_55,
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
variables::{Functio, Value, Variable},
|
variables::{Functio, Value, Variable},
|
||||||
|
@ -43,16 +44,16 @@ struct Scope {
|
||||||
|
|
||||||
/// The reason a successful series of statements halted.
|
/// The reason a successful series of statements halted.
|
||||||
enum HaltStatus {
|
enum HaltStatus {
|
||||||
/// The last statement in the list evaluated to this value.
|
/// We ran out of statements to execute.
|
||||||
Value(Value),
|
Finished,
|
||||||
|
|
||||||
/// A `break` statement occurred and was not caught by a `loop`
|
/// A `break` statement occurred at the given span, and was not
|
||||||
/// statement.
|
/// caught by a `loop` statement up to this point.
|
||||||
Break,
|
Break(Range<usize>),
|
||||||
|
|
||||||
/// A `hopback` statement occurred and was not caught by a `loop`
|
/// A `hopback` statement occurred at the given span, and was not
|
||||||
/// statement.
|
/// caught by a `loop` statement up to this point.
|
||||||
Hopback,
|
Hopback(Range<usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecEnv {
|
impl ExecEnv {
|
||||||
|
@ -67,14 +68,14 @@ impl ExecEnv {
|
||||||
/// Execute a set of Statements in their own stack frame. Return
|
/// Execute a set of Statements in their own stack frame. Return
|
||||||
/// an error if one or more of the Stmts failed to evaluate, or if
|
/// an error if one or more of the Stmts failed to evaluate, or if
|
||||||
/// a `break` or `hopback` statement occurred at the top level.
|
/// a `break` or `hopback` statement occurred at the top level.
|
||||||
pub fn eval_stmts(&mut self, stmts: &[Stmt]) -> Result<Value, Error> {
|
pub fn eval_stmts(&mut self, stmts: &[Stmt]) -> Result<(), Error> {
|
||||||
match self.eval_stmts_hs(stmts)? {
|
match self.eval_stmts_hs(stmts)? {
|
||||||
HaltStatus::Value(v) => Ok(v),
|
HaltStatus::Finished => Ok(()),
|
||||||
HaltStatus::Break | HaltStatus::Hopback => Err(Error {
|
HaltStatus::Break(span) | HaltStatus::Hopback(span) => Err(Error {
|
||||||
// It's an error to issue a `break` outside of a
|
// It's an error to issue a `break` outside of a
|
||||||
// `loop` statement.
|
// `loop` statement.
|
||||||
kind: ErrorKind::TopLevelBreak,
|
kind: ErrorKind::TopLevelBreak,
|
||||||
span: 0..0,
|
span: span,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,10 +89,10 @@ impl ExecEnv {
|
||||||
let init_depth = self.stack.len();
|
let init_depth = self.stack.len();
|
||||||
|
|
||||||
self.stack.push(Default::default());
|
self.stack.push(Default::default());
|
||||||
let mut final_result = Ok(HaltStatus::Value(Value::Nul));
|
let mut final_result = Ok(HaltStatus::Finished);
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
final_result = self.eval_stmt(stmt);
|
final_result = self.eval_stmt(stmt);
|
||||||
if !matches!(final_result, Ok(HaltStatus::Value(_))) {
|
if !matches!(final_result, Ok(HaltStatus::Finished)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,8 +116,8 @@ impl ExecEnv {
|
||||||
match kind {
|
match kind {
|
||||||
// Arithmetic operators.
|
// Arithmetic operators.
|
||||||
Add | Subtract | Multiply | Divide => {
|
Add | Subtract | Multiply | Divide => {
|
||||||
let lhs = i32::try_from(lhs)?;
|
let lhs = lhs.to_i32(&expr.span)?;
|
||||||
let rhs = i32::try_from(rhs)?;
|
let rhs = rhs.to_i32(&expr.span)?;
|
||||||
|
|
||||||
let res = match kind {
|
let res = match kind {
|
||||||
Add => lhs.checked_add(rhs),
|
Add => lhs.checked_add(rhs),
|
||||||
|
@ -134,8 +135,8 @@ impl ExecEnv {
|
||||||
|
|
||||||
// Numeric comparisons.
|
// Numeric comparisons.
|
||||||
Less | Greater => {
|
Less | Greater => {
|
||||||
let lhs = i32::try_from(lhs)?;
|
let lhs = lhs.to_i32(&expr.span)?;
|
||||||
let rhs = i32::try_from(rhs)?;
|
let rhs = rhs.to_i32(&expr.span)?;
|
||||||
|
|
||||||
let res = match kind {
|
let res = match kind {
|
||||||
Less => lhs < rhs,
|
Less => lhs < rhs,
|
||||||
|
@ -157,8 +158,8 @@ impl ExecEnv {
|
||||||
|
|
||||||
// Logical connectives.
|
// Logical connectives.
|
||||||
And | Or => {
|
And | Or => {
|
||||||
let lhs = bool::from(lhs);
|
let lhs = lhs.to_bool();
|
||||||
let rhs = bool::from(rhs);
|
let rhs = rhs.to_bool();
|
||||||
let res = match kind {
|
let res = match kind {
|
||||||
And => lhs && rhs,
|
And => lhs && rhs,
|
||||||
Or => lhs || rhs,
|
Or => lhs || rhs,
|
||||||
|
@ -168,9 +169,15 @@ impl ExecEnv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Not(expr) => Bool(!bool::from(self.eval_expr(&expr)?)),
|
Not(expr) => Bool(!self.eval_expr(&expr)?.to_bool()),
|
||||||
Literal(value) => value.clone(),
|
Literal(value) => value.clone(),
|
||||||
Variable(name) => self.get_var(&name)?,
|
|
||||||
|
// TODO: not too happy with constructing an artificial
|
||||||
|
// Iden here.
|
||||||
|
Variable(name) => self.get_var(&Iden {
|
||||||
|
iden: name.to_owned(),
|
||||||
|
span: expr.span.clone(),
|
||||||
|
})?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,12 +210,12 @@ impl ExecEnv {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
StmtKind::If { cond, body } => {
|
StmtKind::If { cond, body } => {
|
||||||
if self.eval_expr(cond)?.into() {
|
if self.eval_expr(cond)?.to_bool() {
|
||||||
return self.eval_stmts_hs(&body.block);
|
return self.eval_stmts_hs(&body.block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Call { iden, args } => {
|
StmtKind::Call { iden, args } => {
|
||||||
let func = self.get_var(&iden.iden)?;
|
let func = self.get_var(&iden)?;
|
||||||
match func {
|
match func {
|
||||||
Value::Functio(func) => {
|
Value::Functio(func) => {
|
||||||
match func {
|
match func {
|
||||||
|
@ -249,9 +256,9 @@ impl ExecEnv {
|
||||||
StmtKind::Loop { body } => loop {
|
StmtKind::Loop { body } => loop {
|
||||||
let res = self.eval_stmts_hs(&body.block)?;
|
let res = self.eval_stmts_hs(&body.block)?;
|
||||||
match res {
|
match res {
|
||||||
HaltStatus::Value(_) => {}
|
HaltStatus::Finished => {}
|
||||||
HaltStatus::Break => break,
|
HaltStatus::Break(_) => break,
|
||||||
HaltStatus::Hopback => continue,
|
HaltStatus::Hopback(_) => continue,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// This is missing as well. ~~Alex
|
// This is missing as well. ~~Alex
|
||||||
|
@ -259,13 +266,13 @@ impl ExecEnv {
|
||||||
// self.get_var_mut(&iden.0)?.value = self.eval_expr(value)?;
|
// self.get_var_mut(&iden.0)?.value = self.eval_expr(value)?;
|
||||||
// }
|
// }
|
||||||
StmtKind::Break => {
|
StmtKind::Break => {
|
||||||
return Ok(HaltStatus::Break);
|
return Ok(HaltStatus::Break(stmt.span.clone()));
|
||||||
}
|
}
|
||||||
StmtKind::HopBack => {
|
StmtKind::HopBack => {
|
||||||
return Ok(HaltStatus::Hopback);
|
return Ok(HaltStatus::Hopback(stmt.span.clone()));
|
||||||
}
|
}
|
||||||
StmtKind::Melo(iden) => {
|
StmtKind::Melo(iden) => {
|
||||||
self.get_var_mut(&iden.iden)?.melo = true;
|
self.get_var_mut(&iden)?.melo = true;
|
||||||
}
|
}
|
||||||
StmtKind::Rlyeh => {
|
StmtKind::Rlyeh => {
|
||||||
// Maybe print a creepy error message or something
|
// Maybe print a creepy error message or something
|
||||||
|
@ -274,14 +281,14 @@ impl ExecEnv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HaltStatus::Value(Value::Nul))
|
Ok(HaltStatus::Finished)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of a variable. Throw an error if the variable is
|
/// Get the value of a variable. Throw an error if the variable is
|
||||||
/// inaccessible or banned.
|
/// inaccessible or banned.
|
||||||
fn get_var(&self, name: &str) -> Result<Value, Error> {
|
fn get_var(&self, name: &Iden) -> Result<Value, Error> {
|
||||||
// One-letter names are reserved as base55 numbers.
|
// One-letter names are reserved as base55 numbers.
|
||||||
let mut chars = name.chars();
|
let mut chars = name.iden.chars();
|
||||||
if let (Some(first), None) = (chars.next(), chars.next()) {
|
if let (Some(first), None) = (chars.next(), chars.next()) {
|
||||||
return Ok(Value::Int(base_55::char2num(first)));
|
return Ok(Value::Int(base_55::char2num(first)));
|
||||||
}
|
}
|
||||||
|
@ -292,51 +299,49 @@ impl ExecEnv {
|
||||||
.stack
|
.stack
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|scope| scope.variables.get(name))
|
.find_map(|scope| scope.variables.get(&name.iden))
|
||||||
{
|
{
|
||||||
Some(var) => {
|
Some(var) => {
|
||||||
if !var.melo {
|
if !var.melo {
|
||||||
Ok(var.value.clone())
|
Ok(var.value.clone())
|
||||||
} else {
|
} else {
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::MeloVariable(name.to_owned()),
|
kind: ErrorKind::MeloVariable(name.iden.to_owned()),
|
||||||
// TODO: figure out some way to avoid this
|
span: name.span.clone(),
|
||||||
// 0..0 dumbness
|
|
||||||
span: 0..0,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Err(Error {
|
None => Err(Error {
|
||||||
kind: ErrorKind::UnknownVariable(name.to_owned()),
|
kind: ErrorKind::UnknownVariable(name.iden.to_owned()),
|
||||||
span: 0..0,
|
span: name.span.clone(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a variable. Throw an error if the
|
/// Get a mutable reference to a variable. Throw an error if the
|
||||||
/// variable is inaccessible or banned.
|
/// variable is inaccessible or banned.
|
||||||
fn get_var_mut(&mut self, name: &str) -> Result<&mut Variable, Error> {
|
fn get_var_mut(&mut self, name: &Iden) -> Result<&mut Variable, Error> {
|
||||||
// This function has a lot of duplicated code with `get_var`,
|
// This function has a lot of duplicated code with `get_var`,
|
||||||
// which I feel like is a bad sign...
|
// which I feel like is a bad sign...
|
||||||
match self
|
match self
|
||||||
.stack
|
.stack
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|scope| scope.variables.get_mut(name))
|
.find_map(|scope| scope.variables.get_mut(&name.iden))
|
||||||
{
|
{
|
||||||
Some(var) => {
|
Some(var) => {
|
||||||
if !var.melo {
|
if !var.melo {
|
||||||
Ok(var)
|
Ok(var)
|
||||||
} else {
|
} else {
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::MeloVariable(name.to_owned()),
|
kind: ErrorKind::MeloVariable(name.iden.to_owned()),
|
||||||
span: 0..0,
|
span: name.span.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Err(Error {
|
None => Err(Error {
|
||||||
kind: ErrorKind::UnknownVariable(name.to_owned()),
|
kind: ErrorKind::UnknownVariable(name.iden.to_owned()),
|
||||||
span: 0..0,
|
span: name.span.clone(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,15 +372,15 @@ mod tests {
|
||||||
kind: ExprKind::BinOp {
|
kind: ExprKind::BinOp {
|
||||||
lhs: Box::new(Expr {
|
lhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(2)),
|
kind: ExprKind::Literal(Value::Int(2)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(2)),
|
kind: ExprKind::Literal(Value::Int(2)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
kind: crate::ast::BinOpKind::Add,
|
kind: crate::ast::BinOpKind::Add,
|
||||||
},
|
},
|
||||||
span: 0..0
|
span: 1..1
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Value::Int(4)
|
Value::Int(4)
|
||||||
|
@ -392,15 +397,15 @@ mod tests {
|
||||||
kind: ExprKind::BinOp {
|
kind: ExprKind::BinOp {
|
||||||
lhs: Box::new(Expr {
|
lhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(2)),
|
kind: ExprKind::Literal(Value::Int(2)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Bool(true)),
|
kind: ExprKind::Literal(Value::Bool(true)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
kind: crate::ast::BinOpKind::Add,
|
kind: crate::ast::BinOpKind::Add,
|
||||||
},
|
},
|
||||||
span: 0..0
|
span: 1..1
|
||||||
}),
|
}),
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::TypeError(_),
|
kind: ErrorKind::TypeError(_),
|
||||||
|
@ -419,15 +424,15 @@ mod tests {
|
||||||
kind: ExprKind::BinOp {
|
kind: ExprKind::BinOp {
|
||||||
lhs: Box::new(Expr {
|
lhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(i32::MAX)),
|
kind: ExprKind::Literal(Value::Int(i32::MAX)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(1)),
|
kind: ExprKind::Literal(Value::Int(1)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
kind: crate::ast::BinOpKind::Add,
|
kind: crate::ast::BinOpKind::Add,
|
||||||
},
|
},
|
||||||
span: 0..0
|
span: 1..1
|
||||||
}),
|
}),
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::ArithmeticError,
|
kind: ErrorKind::ArithmeticError,
|
||||||
|
@ -441,15 +446,15 @@ mod tests {
|
||||||
kind: ExprKind::BinOp {
|
kind: ExprKind::BinOp {
|
||||||
lhs: Box::new(Expr {
|
lhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(1)),
|
kind: ExprKind::Literal(Value::Int(1)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(0)),
|
kind: ExprKind::Literal(Value::Int(0)),
|
||||||
span: 0..0,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
kind: crate::ast::BinOpKind::Divide,
|
kind: crate::ast::BinOpKind::Divide,
|
||||||
},
|
},
|
||||||
span: 0..0
|
span: 1..1
|
||||||
}),
|
}),
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::ArithmeticError,
|
kind: ErrorKind::ArithmeticError,
|
||||||
|
@ -467,7 +472,7 @@ mod tests {
|
||||||
// We can assume there won't be any syntax errors in the
|
// We can assume there won't be any syntax errors in the
|
||||||
// interpreter tests.
|
// interpreter tests.
|
||||||
let ast = parser.init().unwrap();
|
let ast = parser.init().unwrap();
|
||||||
env.eval_stmts(&ast)
|
env.eval_stmts(&ast).map(|()| Value::Nul)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{convert::TryFrom, fmt::Display, io::Write};
|
use std::{fmt::Display, io::Write, ops::Range};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ pub enum Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
/// Writes an AbleScript value to a Brainfuck input stream. This
|
/// Write an AbleScript value to a Brainfuck input stream. This
|
||||||
/// should generally only be called on `Write`rs that cannot fail,
|
/// should generally only be called on `Write`rs that cannot fail,
|
||||||
/// e.g., `Vec<u8>`, because any IO errors will cause a panic.
|
/// e.g., `Vec<u8>`, because any IO errors will cause a panic.
|
||||||
///
|
///
|
||||||
|
@ -105,6 +105,38 @@ 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
|
||||||
|
/// fails, the generated error message is associated with the
|
||||||
|
/// given span.
|
||||||
|
pub fn to_i32(self, span: &Range<usize>) -> Result<i32, Error> {
|
||||||
|
match self {
|
||||||
|
Value::Int(i) => Ok(i),
|
||||||
|
_ => Err(Error {
|
||||||
|
kind: ErrorKind::TypeError(format!("Expected int, got {}", self)),
|
||||||
|
span: span.clone(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Coerce a Value to a boolean. The conversion cannot fail.
|
||||||
|
pub fn to_bool(self) -> bool {
|
||||||
|
match self {
|
||||||
|
// Booleans and abooleans have a trivial conversion.
|
||||||
|
Value::Bool(b) => b,
|
||||||
|
Value::Abool(b) => b.into(),
|
||||||
|
// The empty string is falsey, other strings are truthy.
|
||||||
|
Value::Str(s) => s.len() != 0,
|
||||||
|
// 0 is falsey, nonzero is truthy.
|
||||||
|
Value::Int(x) => x != 0,
|
||||||
|
// Functios are always truthy.
|
||||||
|
Value::Functio(_) => true,
|
||||||
|
// And nul is truthy as a symbol of the fact that the
|
||||||
|
// deep, fundamental truth of this world is nothing but
|
||||||
|
// the eternal void.
|
||||||
|
Value::Nul => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
|
@ -134,48 +166,6 @@ impl Display for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Value> for i32 {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
Value::Int(i) => Ok(i),
|
|
||||||
_ => Err(Error {
|
|
||||||
kind: ErrorKind::TypeError(format!("Expected int, got {}", value)),
|
|
||||||
// TODO: either add some kind of metadata to `Value`
|
|
||||||
// so we can tell where the value came from and assign
|
|
||||||
// this `position` correctly, or re-write the
|
|
||||||
// `error::Error` struct so we can omit the `position`
|
|
||||||
// when using some error kinds.
|
|
||||||
span: 0..0,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Coercions from a value to a boolean always succeed, so every value
|
|
||||||
// can be used in an `if` statement. C does things that way, so how
|
|
||||||
// could it possibly be a bad idea?
|
|
||||||
impl From<Value> for bool {
|
|
||||||
fn from(value: Value) -> Self {
|
|
||||||
match value {
|
|
||||||
// Booleans and abooleans have a trivial conversion.
|
|
||||||
Value::Bool(b) => b,
|
|
||||||
Value::Abool(b) => b.into(),
|
|
||||||
// The empty string is falsey, other strings are truthy.
|
|
||||||
Value::Str(s) => s.len() != 0,
|
|
||||||
// 0 is falsey, nonzero is truthy.
|
|
||||||
Value::Int(x) => x != 0,
|
|
||||||
// Functios are always truthy.
|
|
||||||
Value::Functio(_) => true,
|
|
||||||
// And nul is truthy as a symbol of the fact that the
|
|
||||||
// deep, fundamental truth of this world is nothing but
|
|
||||||
// the eternal void.
|
|
||||||
Value::Nul => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Variable {
|
pub struct Variable {
|
||||||
pub melo: bool,
|
pub melo: bool,
|
||||||
|
|
Loading…
Reference in a new issue