mfp (minimum fizzbuzzable programming language
This commit is contained in:
parent
addec372fe
commit
8329545560
4 changed files with 194 additions and 12 deletions
47
examples/fizzbuzz.puyo
Normal file
47
examples/fizzbuzz.puyo
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
fn nl() (put 10);
|
||||||
|
|
||||||
|
fn zz() (
|
||||||
|
put 122;
|
||||||
|
put 122;
|
||||||
|
);
|
||||||
|
|
||||||
|
fn Fizz() (
|
||||||
|
put 70;
|
||||||
|
put 105;
|
||||||
|
zz();
|
||||||
|
);
|
||||||
|
|
||||||
|
fn Buzz() (
|
||||||
|
put 66;
|
||||||
|
put 117;
|
||||||
|
zz();
|
||||||
|
);
|
||||||
|
|
||||||
|
fn fizzbuzz1([n]) (
|
||||||
|
if (n % 15 = 0) [
|
||||||
|
Fizz(); Buzz();
|
||||||
|
] [
|
||||||
|
if (n % 3 = 0) [
|
||||||
|
Fizz();
|
||||||
|
] [
|
||||||
|
if (n % 5 = 0) [
|
||||||
|
Buzz();
|
||||||
|
] [
|
||||||
|
print n;
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
nl();
|
||||||
|
);
|
||||||
|
|
||||||
|
i := 1;
|
||||||
|
|
||||||
|
loop [
|
||||||
|
fizzbuzz1 i;
|
||||||
|
i := i + 1;
|
||||||
|
|
||||||
|
if (i > 100) [
|
||||||
|
exit 0;
|
||||||
|
] [];
|
||||||
|
];
|
||||||
80
src/eval.rs
80
src/eval.rs
|
|
@ -1,4 +1,10 @@
|
||||||
use std::collections::HashMap;
|
// TODO: usable closures
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
io::{self, Write},
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Ast, BinOp, Ident},
|
ast::{Ast, BinOp, Ident},
|
||||||
|
|
@ -21,6 +27,7 @@ pub enum Error {
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines, reason = "you're right")]
|
||||||
fn eval(expr: &Ast, local_env: &mut Map<Val>, return_env: &mut Map<Val>) -> Result<Val> {
|
fn eval(expr: &Ast, local_env: &mut Map<Val>, return_env: &mut Map<Val>) -> Result<Val> {
|
||||||
match expr {
|
match expr {
|
||||||
Ast::Var(ident) => match ident {
|
Ast::Var(ident) => match ident {
|
||||||
|
|
@ -61,10 +68,12 @@ fn eval(expr: &Ast, local_env: &mut Map<Val>, return_env: &mut Map<Val>) -> Resu
|
||||||
let mut outputs = outputs.clone();
|
let mut outputs = outputs.clone();
|
||||||
outputs.reverse();
|
outputs.reverse();
|
||||||
|
|
||||||
|
let env = local_env.clone();
|
||||||
|
|
||||||
Ok(Val::Function(Box::new(Function {
|
Ok(Val::Function(Box::new(Function {
|
||||||
inputs,
|
inputs,
|
||||||
outputs,
|
outputs,
|
||||||
env: local_env.clone(),
|
env,
|
||||||
body: FunctionBody::Ast(Ast::Block(body.clone())),
|
body: FunctionBody::Ast(Ast::Block(body.clone())),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +115,11 @@ fn eval(expr: &Ast, local_env: &mut Map<Val>, return_env: &mut Map<Val>) -> Resu
|
||||||
BinOp::Sub => lhs - rhs,
|
BinOp::Sub => lhs - rhs,
|
||||||
BinOp::Mul => lhs * rhs,
|
BinOp::Mul => lhs * rhs,
|
||||||
BinOp::Div => lhs / rhs,
|
BinOp::Div => lhs / rhs,
|
||||||
_ => todo!(),
|
BinOp::Mod => lhs % rhs,
|
||||||
|
BinOp::Pow => lhs.pow(&rhs),
|
||||||
|
BinOp::Eq => Val::from(lhs == rhs),
|
||||||
|
BinOp::Lt => Val::from(lhs < rhs),
|
||||||
|
BinOp::Le => Val::from(lhs <= rhs),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +152,56 @@ fn stdenv() -> Map<Val> {
|
||||||
|
|
||||||
res.insert(
|
res.insert(
|
||||||
"println".to_owned(),
|
"println".to_owned(),
|
||||||
Val::prim(&["x"], &[], |local_env, _| println!("{:?}", local_env["x"])),
|
Val::prim(&["x"], &[], |local_env, _| println!("{}", local_env["x"])),
|
||||||
|
);
|
||||||
|
|
||||||
|
res.insert(
|
||||||
|
"print".to_owned(),
|
||||||
|
Val::prim(&["x"], &[], |local_env, _| print!("{}", local_env["x"])),
|
||||||
|
);
|
||||||
|
|
||||||
|
res.insert(
|
||||||
|
"put".to_owned(),
|
||||||
|
Val::prim(&["x"], &[], |local_env, _| {
|
||||||
|
let Some(Val::Number(n)) = local_env.get("x") else {
|
||||||
|
panic!("where is it (put)");
|
||||||
|
};
|
||||||
|
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||||
|
let n = (n.abs() % 256.) as u8;
|
||||||
|
io::stdout().write_all(&[n]).unwrap();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
res.insert(
|
||||||
|
"if".to_owned(),
|
||||||
|
Val::prim(&["cond", "then", "else"], &["x"], |loc, ret| {
|
||||||
|
if loc["cond"] == Val::Number(0.0) {
|
||||||
|
ret.insert("x".to_owned(), loc.get_mut("else").unwrap().run().unwrap());
|
||||||
|
} else {
|
||||||
|
ret.insert("x".to_owned(), loc.get_mut("then").unwrap().run().unwrap());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
res.insert(
|
||||||
|
"loop".to_owned(),
|
||||||
|
Val::prim(&["body"], &[], |loc, _| {
|
||||||
|
loop {
|
||||||
|
loc.get_mut("body").unwrap().run().unwrap();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
res.insert(
|
||||||
|
"exit".to_owned(),
|
||||||
|
Val::prim(&["code"], &[], |loc, _| {
|
||||||
|
let Val::Number(n) = loc["code"] else {
|
||||||
|
panic!("exit code must be a number");
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
process::exit(n.floor() as i32);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
res
|
res
|
||||||
|
|
@ -156,6 +218,16 @@ pub fn run(program: &[Ast]) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Val {
|
||||||
|
fn run(&mut self) -> Result<Val> {
|
||||||
|
if let Val::Function(func) = self {
|
||||||
|
func.run()
|
||||||
|
} else {
|
||||||
|
Ok(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn call(&mut self, val: Val) -> Result<Option<Val>> {
|
pub fn call(&mut self, val: Val) -> Result<Option<Val>> {
|
||||||
match val {
|
match val {
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ mod tests;
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let src = &env::args().nth(1).expect("give me an argument now");
|
let src = &env::args().nth(1).expect("give me an argument now");
|
||||||
let res = parse(src);
|
let res = parse(src);
|
||||||
eprintln!("-- ast");
|
eprintln!("-- ast");
|
||||||
for stmt in &res {
|
for stmt in &res {
|
||||||
eprintln!("{stmt};");
|
eprintln!("{stmt};");
|
||||||
}
|
}
|
||||||
eprintln!("-- /ast");
|
eprintln!("-- output");
|
||||||
run(&res)?;
|
run(&res)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
src/val.rs
75
src/val.rs
|
|
@ -1,8 +1,13 @@
|
||||||
use std::{collections::HashMap, fmt, ops, string::ToString};
|
use std::{
|
||||||
|
cmp::{self, Ordering},
|
||||||
|
collections::HashMap,
|
||||||
|
fmt, ops,
|
||||||
|
string::ToString,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{ast::Ast, map::Map, symbol::Symbol};
|
use crate::{ast::Ast, map::Map, symbol::Symbol};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Val {
|
pub enum Val {
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Function(Box<Function>),
|
Function(Box<Function>),
|
||||||
|
|
@ -15,10 +20,10 @@ impl Default for Val {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Val {
|
impl fmt::Display for Val {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Number(n) => write!(f, "{n:?}"),
|
Self::Number(n) => write!(f, "{n}"),
|
||||||
// TODO: ok
|
// TODO: ok
|
||||||
Self::Function(fun) => write!(
|
Self::Function(fun) => write!(
|
||||||
f,
|
f,
|
||||||
|
|
@ -87,6 +92,57 @@ impl ops::Div for Val {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ops::Rem for Val {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn rem(self, rhs: Self) -> Self::Output {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Val::Number(a), Val::Number(b)) => Val::Number(a % b),
|
||||||
|
(lhs, rhs) => panic!("how do i div these {lhs:?} {rhs:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl cmp::PartialEq for Val {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Number(a), Self::Number(b)) => a == b,
|
||||||
|
(Self::Map(a), Self::Map(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// yes this is a lie shut up
|
||||||
|
impl Eq for Val {}
|
||||||
|
|
||||||
|
impl cmp::PartialOrd for Val {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
match (self, other) {
|
||||||
|
(Val::Number(a), Val::Number(b)) => a.partial_cmp(b),
|
||||||
|
(Val::Map(a), Val::Map(b)) => {
|
||||||
|
if a == b {
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
} else {
|
||||||
|
for (k, v) in a.iter() {
|
||||||
|
if b.get(k).is_none_or(|v2| v != v2) {
|
||||||
|
return Some(Ordering::Greater);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Ordering::Less)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for Val {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Val::Number(f64::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
/// Inputs in reverse order (fn ([a b c])) -> vec![c b a]
|
/// Inputs in reverse order (fn ([a b c])) -> vec![c b a]
|
||||||
|
|
@ -107,10 +163,17 @@ pub enum FunctionBody {
|
||||||
impl Val {
|
impl Val {
|
||||||
pub fn prim(inputs: &[&str], outputs: &[&str], run: PrimFn) -> Val {
|
pub fn prim(inputs: &[&str], outputs: &[&str], run: PrimFn) -> Val {
|
||||||
Self::Function(Box::new(Function {
|
Self::Function(Box::new(Function {
|
||||||
inputs: inputs.iter().map(ToString::to_string).collect(),
|
inputs: inputs.iter().rev().map(ToString::to_string).collect(),
|
||||||
outputs: outputs.iter().map(ToString::to_string).collect(),
|
outputs: outputs.iter().rev().map(ToString::to_string).collect(),
|
||||||
body: FunctionBody::Prim(run),
|
body: FunctionBody::Prim(run),
|
||||||
env: HashMap::new(),
|
env: HashMap::new(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pow(&self, rhs: &Self) -> Self {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Val::Number(a), Val::Number(b)) => Val::Number(a.powf(*b)),
|
||||||
|
(lhs, rhs) => panic!("how do i pow these {lhs:?} {rhs:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue