Init commit
This commit is contained in:
commit
50329f1d9f
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
@ -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"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "rlisp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
65
shell.nix
Normal file
65
shell.nix
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
{ pkgs ? import <nixpkgs> { } }:
|
||||||
|
|
||||||
|
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/"
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
38
src/environ.rs
Normal file
38
src/environ.rs
Normal file
|
@ -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<String, Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn default_env() -> Environ {
|
||||||
|
let mut data: HashMap<String, Expr> = HashMap::new();
|
||||||
|
data.insert(
|
||||||
|
"+".to_string(),
|
||||||
|
Expr::Func(
|
||||||
|
|args: &[Expr]| -> Result<Expr, RispError> {
|
||||||
|
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<Expr, RispError> {
|
||||||
|
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}
|
||||||
|
}
|
132
src/main.rs
Normal file
132
src/main.rs
Normal file
|
@ -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<Expr>),
|
||||||
|
Func(fn(&[Expr]) -> Result<Expr, RispError>), // 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<String> = 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<Expr, RispError> {
|
||||||
|
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::<Result<Vec<Expr>, 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<Expr> = 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),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
src/parser.rs
Normal file
65
src/parser.rs
Normal file
|
@ -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<String> {
|
||||||
|
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<Vec<f64>, RispError> {
|
||||||
|
args
|
||||||
|
.iter()
|
||||||
|
.map(|x| parse_single_float(x))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_single_float(exp: &Expr) -> Result<f64, RispError> {
|
||||||
|
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<f64, ParseFloatError> = 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<Expr, RispError> {
|
||||||
|
let (parsed_exp, _) = parse(&tokenize(expr))?;
|
||||||
|
let evaled_exp = eval(&parsed_exp, env)?;
|
||||||
|
|
||||||
|
Ok(evaled_exp)
|
||||||
|
}
|
Loading…
Reference in a new issue