forked from AbleScript/ablescript
Implement more statements
Added variable declaration, `if` statements, `loop` statements, variable assignment, and variable banning to go along with printing (which was already implemented). We still need function declarations, brainfuck declarations, function calls, and the control flow operators "break" and "hopback".
This commit is contained in:
parent
b37c189da9
commit
1c2032ab87
|
@ -21,7 +21,6 @@ use crate::{
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
/// The mapping from variable names to values.
|
/// The mapping from variable names to values.
|
||||||
variables: HashMap<String, Variable>,
|
variables: HashMap<String, Variable>,
|
||||||
|
|
||||||
// In the future, this will store functio definitions, a link to a
|
// In the future, this will store functio definitions, a link to a
|
||||||
// parent scope (so we can have nested scopes), and possibly other
|
// parent scope (so we can have nested scopes), and possibly other
|
||||||
// information.
|
// information.
|
||||||
|
@ -82,13 +81,13 @@ impl Scope {
|
||||||
}
|
}
|
||||||
Eq { left, right } => Bool(self.eval_expr(left)? == self.eval_expr(right)?),
|
Eq { left, right } => Bool(self.eval_expr(left)? == self.eval_expr(right)?),
|
||||||
Neq { left, right } => Bool(self.eval_expr(left)? != self.eval_expr(right)?),
|
Neq { left, right } => Bool(self.eval_expr(left)? != self.eval_expr(right)?),
|
||||||
And { left, right } => Bool(
|
And { left, right } => {
|
||||||
bool::try_from(self.eval_expr(left)?)? && bool::try_from(self.eval_expr(right)?)?,
|
Bool(bool::from(self.eval_expr(left)?) && bool::from(self.eval_expr(right)?))
|
||||||
),
|
}
|
||||||
Or { left, right } => Bool(
|
Or { left, right } => {
|
||||||
bool::try_from(self.eval_expr(left)?)? || bool::try_from(self.eval_expr(right)?)?,
|
Bool(bool::from(self.eval_expr(left)?) || bool::from(self.eval_expr(right)?))
|
||||||
),
|
}
|
||||||
Not(expr) => Bool(!bool::try_from(self.eval_expr(expr)?)?),
|
Not(expr) => Bool(!bool::from(self.eval_expr(expr)?)),
|
||||||
Literal(value) => value.clone(),
|
Literal(value) => value.clone(),
|
||||||
Identifier(Iden(name)) => self
|
Identifier(Iden(name)) => self
|
||||||
.variables
|
.variables
|
||||||
|
@ -117,11 +116,66 @@ impl Scope {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Print(expr) => {
|
Stmt::Print(expr) => {
|
||||||
println!("{}", self.eval_expr(expr)?);
|
println!("{}", self.eval_expr(expr)?);
|
||||||
|
}
|
||||||
|
Stmt::VariableDeclaration { iden, init } => {
|
||||||
|
self.variables.insert(
|
||||||
|
iden.0.clone(),
|
||||||
|
Variable {
|
||||||
|
melo: false,
|
||||||
|
value: match init {
|
||||||
|
Some(init) => self.eval_expr(init)?,
|
||||||
|
None => Value::Nul,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Stmt::FunctionDeclaration {
|
||||||
|
iden: _,
|
||||||
|
args: _,
|
||||||
|
body: _,
|
||||||
|
} => todo!(),
|
||||||
|
Stmt::BfFDeclaration { iden: _, body: _ } => todo!(),
|
||||||
|
Stmt::If { cond, body } => {
|
||||||
|
if self.eval_expr(cond)?.into() {
|
||||||
|
self.eval_items(body)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stmt::FunctionCall { iden: _, args: _ } => todo!(),
|
||||||
|
Stmt::Loop { body } => {
|
||||||
|
loop {
|
||||||
|
// For now, loops run forever until they reach an
|
||||||
|
// error.
|
||||||
|
self.eval_items(body)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stmt::VarAssignment { iden, value } => {
|
||||||
|
let value = self.eval_expr(value)?;
|
||||||
|
let record = self.variables.get_mut(&iden.0).ok_or_else(|| Error {
|
||||||
|
kind: ErrorKind::UnknownVariable(iden.0.clone()),
|
||||||
|
position: 0..0,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if record.melo {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::MeloVariable(iden.0.clone()),
|
||||||
|
position: 0..0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
record.value = value;
|
||||||
|
}
|
||||||
|
Stmt::Break => todo!(),
|
||||||
|
Stmt::HopBack => todo!(),
|
||||||
|
Stmt::Melo(iden) => {
|
||||||
|
let record = self.variables.get_mut(&iden.0).ok_or_else(|| Error {
|
||||||
|
kind: ErrorKind::UnknownVariable(iden.0.clone()),
|
||||||
|
position: 0..0,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
record.melo = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,17 +71,23 @@ impl TryFrom<Value> for i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Value> for bool {
|
// Coercions from a value to a boolean always succeed, so every value
|
||||||
type Error = Error;
|
// can be used in an `if` statement. C does things that way, so how
|
||||||
|
// could it possibly be a bad idea?
|
||||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
impl From<Value> for bool {
|
||||||
|
fn from(value: Value) -> Self {
|
||||||
match value {
|
match value {
|
||||||
Value::Bool(b) => Ok(b),
|
// Booleans and abooleans have a trivial conversion.
|
||||||
Value::Abool(b) => Ok(b.into()),
|
Value::Bool(b) => b,
|
||||||
_ => Err(Error {
|
Value::Abool(b) => b.into(),
|
||||||
kind: ErrorKind::TypeError(format!("Expected bool, got {}", value)),
|
// The empty string is falsey, other strings are truthy.
|
||||||
position: 0..0,
|
Value::Str(s) => s.len() != 0,
|
||||||
}),
|
// 0 is falsey, nonzero is truthy.
|
||||||
|
Value::Int(x) => x != 0,
|
||||||
|
// 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue