From 40c747619e257e83e12bbdf4d74c9c5bf9bad299 Mon Sep 17 00:00:00 2001 From: Caleb Jones Date: Wed, 14 Dec 2016 01:38:53 -0500 Subject: [PATCH] Add an extremely basic s-expression parser using nom --- .gitignore | 2 ++ Cargo.toml | 7 ++++ src/lib.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6cfd35a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sexp2" +version = "0.1.0" +authors = ["Caleb Jones "] + +[dependencies] +nom = "^2.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b8982f4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,100 @@ +//! A lightweight S-expression parser intended for language implementation. + +#![warn(missing_docs)] +#![deny(unsafe_code)] + +#[macro_use] +extern crate nom; + +use nom::{digit, multispace, IResult}; + +#[derive(Debug, PartialEq, Clone, PartialOrd)] +pub enum Atom { + /// A value representing a symbol. A symbol is an atomic unit + Sym(String), + /// A value representing a string literal. + Str(String), + /// A value representing an integer. Any number containing no decimal point + /// will be parsed as an `Int`. + Int(i64), + /// A value representing a float. Any number containing a decimal point will + /// be parsed as a `Float`. + Float(f64), +} + +#[derive(Debug, PartialEq, Clone, PartialOrd)] +pub enum Sexp { + /// A wrapper around the atom type + Atom { + atom: Atom, + }, + /// A list of subexpressions + List { + list: Vec, + } +} + +pub fn parse(input: &str) -> Result { + match sexp(input) { + IResult::Done(_, res) => Ok(res), + _ => Err(()), + } +} + +named!(sexp<&str, Sexp>, + alt!( + list => { |list| Sexp::List { list: list } } + | atom => { |atom| Sexp::Atom { atom: atom } } + ) +); + +named!(list<&str, Vec >, + do_parse!( + opt!(multispace) >> + tag_s!("(") >> + entries: many0!(sexp) >> + opt!(multispace) >> + tag_s!(")") >> + (entries) + ) +); + +named!(atom<&str, Atom>, alt!(string | symbol | number)); + +named!(string<&str, Atom>, + do_parse!( + opt!(multispace) >> + tag_s!("\"") >> + contents: take_until_s!("\"") >> + tag_s!("\"") >> + opt!(multispace) >> + (Atom::Str(contents.into())) + ) +); + +named!(symbol<&str, Atom>, + do_parse!( + peek!(not!(digit)) >> + name: take_while1_s!(valid_ident_char) >> + (Atom::Sym(name.into())) + ) +); + +fn valid_ident_char(c: char) -> bool { + !c.is_whitespace() && c != '"' && c != '(' && c != ')' +} + +named!(number<&str, Atom>, + do_parse!( + opt!(multispace) >> + integral: digit >> + (Atom::Int(integral.chars().fold(0, |i, c| i * 10 + c as i64 - '0' as i64))) + ) +); + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + } +} -- 2.43.2