Skip to content

Instantly share code, notes, and snippets.

@mFrankowicz
Last active December 5, 2018 17:02
Show Gist options
  • Save mFrankowicz/aee209ce2eb44a47830e550bd083e76a to your computer and use it in GitHub Desktop.
Save mFrankowicz/aee209ce2eb44a47830e550bd083e76a to your computer and use it in GitHub Desktop.
// 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