Last active
December 5, 2018 17:02
-
-
Save mFrankowicz/aee209ce2eb44a47830e550bd083e76a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// HEADER: | |
// this is my very naive attempt to port the awesome guide: | |
// "Build Your Own Lisp | |
// : Learn C and build your own programming language in 1000 lines of code!" | |
// (http://www.buildyourownlisp.com/) | |
// so we can sort of say: | |
// "Build Your Own Lisp | |
// : Learn Rust and build your own programming language in (1000 + x) lines of code!" | |
// (i hope it's not * x) | |
// The cool thing is that i'm learning some Rust as i port this. kinda. | |
// THOUGHS: | |
// ok... for now i just took some snipsets from tests and benches from Nom's source, | |
// it's working now (kinda), but cleary this whole thing will need a refactor to fit | |
// the reader's and eval's specs. | |
// TODO: learn how to make nice rust documentation! | |
#[macro_use] | |
extern crate nom; | |
use nom::{digit, alphanumeric, multispace}; | |
use std::str; | |
use std::str::FromStr; | |
// TODO: | |
// 1: add full rust number types | |
// 2: add optional type notation, else i32,f32 (eg: 123i64, 123u8, 123f64 ... ) | |
#[derive(Debug, PartialEq)] | |
pub enum NumType { | |
Int(i64), | |
Float(f64), | |
} | |
// TODO: Now that i made a LString, just put a L in front of everything... | |
#[derive(Debug, PartialEq)] | |
pub enum Risp { | |
Number(NumType), | |
LString(String), | |
Symbol(String), | |
Sexpr(Vec<Risp>), | |
Qexpr(Vec<Risp>), | |
} | |
// float64 | |
named!( | |
unsigned_float<f64>, | |
map_res!( | |
map_res!( | |
recognize!(alt!( | |
delimited!(digit, tag!("."), opt!(digit)) | delimited!(opt!(digit), tag!("."), digit) | |
)), | |
str::from_utf8 | |
), | |
FromStr::from_str | |
) | |
); | |
named!( | |
float<f64>, | |
map!( | |
pair!(opt!(alt!(tag!("+") | tag!("-"))), unsigned_float), | |
|(sign, value): (Option<&[u8]>, f64)| sign | |
.and_then(|s| if s[0] == b'-' { Some(-1f64)} else { None }) | |
.unwrap_or(1f64) * value | |
) | |
); | |
// int64 | |
named!( | |
unsigned_int<i64>, | |
map_res!( | |
map_res!( | |
recognize!(digit), str::from_utf8 | |
), | |
FromStr::from_str | |
) | |
); | |
named!( | |
int<i64>, | |
map!( | |
pair!(opt!(alt!(tag!("+") | tag!("-"))), unsigned_int), | |
|(sign, value): (Option<&[u8]>, i64)| sign | |
.and_then(|s| if s[0] == b'-' { Some(-1i64)} else { None }) | |
.unwrap_or(1i64) * value | |
) | |
); | |
// number | |
named!( | |
number<Risp>, | |
alt!( | |
float => {|f| Risp::Number(NumType::Float(f))} | | |
int => {|i| Risp::Number(NumType::Int(i))} | |
) | |
); | |
// string | |
// TODO: | |
// 1: add support to multiple line (eg: """ line 1 | |
// line 2 """) | |
named!( | |
string<&str>, | |
delimited!( | |
char!('\"'), | |
map_res!( | |
escaped!(call!(alphanumeric), '\\', one_of!("\"n\\")), | |
str::from_utf8 | |
), | |
char!('\"') | |
) | |
); | |
// symbol | |
// THOUGHTS: | |
// a symbol can be any sequence of chars listed below, | |
// as we gonna make some builtins symbols like '<', '+', '-' etc... | |
// we must think a better way of handle these special syms. | |
// the question is how to handle this in parse, | |
// if we gonna allow the user to shadow builtin syms like '+' or '<' | |
// we can allow any char to pass here, otherwise we make need separated | |
// list of special syms and the user wont have a option for shadowing | |
// TODO: | |
// 1: make this a more nom's way | |
named!( | |
symbol<&str>, | |
map_res!( | |
is_a!("qwertyuiopasdfghjklçzxcvbnmQWERTYUIOPASDFGHJKLÇZXCVBNM1234567890_-§?°ªº¹²³£¢¬"), | |
str::from_utf8 | |
) | |
); | |
// s-expression | |
// THOUGHTS: | |
// a s-expression is a list of expressions that are evaluated by the interpreter | |
// for this, we have some specific cases: | |
// 1: a empty s-expression evaluates to a empty s-expression, so we'll need | |
// to handle this case in parse. | |
// 2: a single l-value evaluates to itself, this is easy because a single | |
// value just return a vec with one item. | |
// 3: otherwise we just have our tipical vec with the l-values. | |
// | |
named!( | |
sexpr<Vec<Risp>>, | |
delimited!( | |
char!('('), | |
separated_list!(multispace, lval), | |
char!(')') | |
) | |
); | |
// l-values | |
named!( | |
lval<Risp>, | |
alt!( | |
number | | |
string => {|sr| Risp::LString(String::from(sr))} | | |
symbol => {|s| Risp::Symbol(String::from(s))} | | |
sexpr => {|sx| Risp::Sexpr(sx)} | |
) | |
); | |
//TODO: geez! let's write a macro to clean this vec mess! | |
//TODO: learn how to write rust macros | |
#[test] | |
fn test_parse_sexpr() { | |
use Risp::{Number, LString, Symbol, Sexpr}; | |
use NumType::{Float, Int}; | |
let s = sexpr(&b"(sym 1 2.2 \"str\" (sym2))\0"[..]); | |
let vs1 = vec![Symbol("sym2".to_string())]; | |
let vs2 = vec![Symbol("sym".to_string()), Number(Int(1)), Number(Float(2.2)), LString("str".to_string()), Sexpr(vs1)]; | |
assert_eq!(s, Ok((&b"\0"[..], vs2))); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment