commit 50329f1d9f5a1b21b8e3f47ce387cd2ca728a3dc Author: Able Date: Fri Nov 15 03:58:29 2024 -0600 Init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5cda058 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "rlisp" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..14b5d43 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rlisp" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..b27116c --- /dev/null +++ b/shell.nix @@ -0,0 +1,65 @@ +{ pkgs ? import { } }: + +pkgs.mkShell rec { + buildInputs = with pkgs; [ + binutils + # linuxPackages.nvidia_x11 + libGLU + libGL + freeglut + rustup + opencv + clang + zlib + udev + ]; + nativeBuildInputs = with pkgs; [ + pkg-config + fontconfig + alsaLib + ffmpeg_7-full + stdenv.cc.cc.lib + xorg.libX11.dev + xorg.libXi + xorg.libXmu + xorg.libXext + xorg.libX11 + xorg.libXv + xorg.libXrandr + xorg.libXcursor + libxkbcommon + udev + openssl + + ]; + + extraCmds = ''''; + RUSTC_VERSION = pkgs.lib.readFile ./rust-toolchain.toml; + # https://github.com/rust-lang/rust-bindgen#environment-variables + LIBCLANG_PATH = + pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ]; + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath nativeBuildInputs; + shellHook = '' + export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin + export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/ + ''; + # Add precompiled library to rustc search path + RUSTFLAGS = (builtins.map (a: "-L ${a}/lib") [ + # add libraries here (e.g. pkgs.libvmi) + ]); + # Add glibc, clang, glib and other headers to bindgen search path + BINDGEN_EXTRA_CLANG_ARGS = + # Includes with normal include path + (builtins.map (a: ''-I"${a}/include"'') [ + # add dev libraries here (e.g. pkgs.libvmi.dev) + pkgs.glibc.dev + ]) + # Includes with special directory paths + ++ [ + '' + -I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"'' + ''-I"${pkgs.glib.dev}/include/glib-2.0"'' + "-I${pkgs.glib.out}/lib/glib-2.0/include/" + ]; + +} diff --git a/src/environ.rs b/src/environ.rs new file mode 100644 index 0000000..0a315e2 --- /dev/null +++ b/src/environ.rs @@ -0,0 +1,38 @@ +use crate::Expr; +use crate::RispError; +use crate::HashMap; +use crate::parse_list_of_floats; + +pub +struct Environ { + pub data: HashMap, +} + + +pub fn default_env() -> Environ { + let mut data: HashMap = HashMap::new(); + data.insert( + "+".to_string(), + Expr::Func( + |args: &[Expr]| -> Result { + let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a); + + Ok(Expr::Number(sum)) + } + ) + ); + data.insert( + "-".to_string(), + Expr::Func( + |args: &[Expr]| -> Result { + let floats = parse_list_of_floats(args)?; + let first = *floats.first().ok_or(RispError::Reason("expected at least one number".to_string()))?; + let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a); + + Ok(Expr::Number(first - sum_of_rest)) + } + ) + ); + + Environ {data} +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5b22136 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,132 @@ +use std::io; +use std::fmt; +use std::num::ParseFloatError; +use std::collections::HashMap;use std::io::Write; + +mod environ; +use environ::Environ; +use environ::default_env; + +mod parser; +use parser::parse; +use parser::parse_eval; +use parser::parse_list_of_floats; + + + +#[derive(Debug)] +enum RispError{ + Reason(String), +} + + +#[derive(Clone)] +enum Expr { + Symbol(String), + Number(f64), + List(Vec), + Func(fn(&[Expr]) -> Result), // bam +} + +impl fmt::Display for Expr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let str = match self { + Expr::Symbol(s) => s.clone(), + Expr::Number(n) => n.to_string(), + Expr::List(list) => { + let xs: Vec = list + .iter() + .map(|x| x.to_string()) + .collect(); + format!("({})", xs.join(",")) + }, + Expr::Func(_) => "Function {}".to_string(), + }; + + write!(f, "{}", str) + } +} + + + +fn eval(exp: &Expr, env: &mut Environ) -> Result { + match exp { + Expr::Symbol(k) => + env.data.get(k) + .ok_or( + RispError::Reason( + format!("unexpected symbol k='{}'", k) + ) + ) + .map(|x| x.clone()) + , + Expr::Number(_a) => Ok(exp.clone()), + Expr::List(list) => { + let first_form = list + .first() + .ok_or(RispError::Reason("expected a non-empty list".to_string()))?; + let arg_forms = &list[1..]; + let first_eval = eval(first_form, env)?; + match first_eval { + Expr::Func(f) => { + let args_eval = arg_forms + .iter() + .map(|x| eval(x, env)) + .collect::, RispError>>(); + f(&args_eval?) + }, + _ => Err( + RispError::Reason("first form must be a function".to_string()) + ), + } + }, + Expr::Func(_) => Err( + RispError::Reason("unexpected form".to_string()) + ), + } +} + + + +fn read_seq<'a>(tokens: &'a [String]) -> Result<(Expr, &'a [String]), RispError> { + let mut res: Vec = vec![]; + let mut xs = tokens; + loop { + let (next_token, rest) = xs + .split_first() + .ok_or(RispError::Reason("could not find closing `)`".to_string())) + ?; + if next_token == ")" { + return Ok((Expr::List(res), rest)) // skip `)`, head to the token after + } + let (exp, new_xs) = parse(&xs)?; + res.push(exp); + xs = new_xs; + } +} + + + +fn slurp_expr() -> String { + let mut expr = String::new(); + + io::stdin().read_line(&mut expr) + .expect("Failed to read line"); + + expr +} + +fn main() { + let env = &mut default_env(); + loop { + print!("risp > "); +let _= std::io::stdout().flush(); + let expr = slurp_expr(); + match parse_eval(expr, env) { + Ok(res) => println!("-> {}", res), + Err(e) => match e { + RispError::Reason(msg) => println!("X> {}", msg), + }, + } + } +} \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..bf1a3d5 --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,65 @@ +use crate::RispError; +use crate::Expr; +use crate::eval; +use crate::Environ; +use crate::read_seq; +use crate::ParseFloatError; + +pub + +fn tokenize(expr: String) -> Vec { + expr + .replace("(", " ( ") + .replace(")", " ) ") + .split_whitespace() + .map(|x| x.to_string()) + .collect() +} + +pub + +fn parse<'a>(tokens: &'a [String]) -> Result<(Expr, &'a [String]), RispError>{ + let (token, rest) = tokens.split_first() + .ok_or( + RispError::Reason("Could not get token".to_string()) + )?; + match &token[..]{ + "("=>{read_seq(rest)} + ")"=>Err(RispError::Reason("unexpected `)`".to_string())), + _ => Ok((parse_atom(token), rest)) + } +} + + +pub + +fn parse_list_of_floats(args: &[Expr]) -> Result, RispError> { + args + .iter() + .map(|x| parse_single_float(x)) + .collect() +} + +fn parse_single_float(exp: &Expr) -> Result { + match exp { + Expr::Number(num) => Ok(*num), + _ => Err(RispError::Reason("expected a number".to_string())), + } +} + + + + +fn parse_atom(token: &str) -> Expr { + let potential_float: Result = token.parse(); + match potential_float { + Ok(v) => Expr::Number(v), + Err(_) => Expr::Symbol(token.to_string().clone()) + } +}pub +fn parse_eval(expr: String, env: &mut Environ) -> Result { + let (parsed_exp, _) = parse(&tokenize(expr))?; + let evaled_exp = eval(&parsed_exp, env)?; + + Ok(evaled_exp) +} \ No newline at end of file