Light formatting as well Signed-off-by: Apache <apache.software.foundation@gmail.com>
379 lines
8.1 KiB
Rust
379 lines
8.1 KiB
Rust
use std::{collections::HashMap, fmt};
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::{Parser, PreLispValue, Tokenizer, TokenizerError};
|
|
|
|
fn quick_parse(source: &str) -> Result<Vec<PreLispValue>, TokenizerError> {
|
|
let mut tokenizer = Tokenizer::new(String::from(source));
|
|
let tokens = tokenizer.tokenize()?;
|
|
let mut parser = Parser::new(tokens);
|
|
Ok(parser.parse())
|
|
}
|
|
|
|
fn var(name: &str) -> PreLispValue {
|
|
PreLispValue::Var(String::from(name))
|
|
}
|
|
fn string(s: &str) -> PreLispValue {
|
|
PreLispValue::String(String::from(s))
|
|
}
|
|
fn list(v: Vec<PreLispValue>, quoted: bool) -> PreLispValue {
|
|
PreLispValue::List(v, quoted)
|
|
}
|
|
fn int(n: i32) -> PreLispValue {
|
|
PreLispValue::Integer(n)
|
|
}
|
|
fn float(n: f32) -> PreLispValue {
|
|
PreLispValue::Float(n)
|
|
}
|
|
|
|
#[test]
|
|
fn test_hello_world() -> Result<(), TokenizerError> {
|
|
assert_eq!(
|
|
vec![list(vec![var("print"), string("Hello, World!")], false)],
|
|
quick_parse(
|
|
"
|
|
(print \"Hello, World!\")
|
|
"
|
|
)?
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_math() -> Result<(), TokenizerError> {
|
|
assert_eq!(
|
|
vec![list(
|
|
vec![
|
|
var("print"),
|
|
list(
|
|
vec![
|
|
var("add"),
|
|
list(
|
|
vec![
|
|
var("div"),
|
|
float(4.5),
|
|
list(vec![var("sub"), float(0.5), float(0.2)], false)
|
|
],
|
|
false
|
|
),
|
|
list(vec![var("mul"), int(21), float(0.05)], false)
|
|
],
|
|
false
|
|
)
|
|
],
|
|
false
|
|
)],
|
|
quick_parse(
|
|
"
|
|
(print
|
|
(add
|
|
(div
|
|
4.5
|
|
(sub 0.5 0.2)
|
|
)
|
|
(mul 21 0.05)
|
|
)
|
|
)
|
|
"
|
|
)?
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
use crate::Token;
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum PreLispValue {
|
|
Nil,
|
|
True,
|
|
False,
|
|
String(String),
|
|
Var(String),
|
|
Integer(i32),
|
|
Float(f32),
|
|
List(Vec<PreLispValue>, bool), // Values, Quoted
|
|
LispFunction(String, Vec<String>, Vec<PreLispValue>),
|
|
}
|
|
|
|
impl fmt::Display for PreLispValue {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
PreLispValue::Nil => write!(f, "nil"),
|
|
PreLispValue::True => write!(f, "true"),
|
|
PreLispValue::False => write!(f, "false"),
|
|
PreLispValue::String(str) => write!(f, "{}", str),
|
|
PreLispValue::LispFunction(name, _, _) => write!(f, "<function {}>", name),
|
|
PreLispValue::Integer(num) => write!(f, "{}", num),
|
|
PreLispValue::Float(num) => write!(f, "{}", num),
|
|
_ => todo!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&str> for PreLispValue {
|
|
fn from(value: &str) -> Self {
|
|
PreLispValue::String(value.to_string())
|
|
}
|
|
}
|
|
impl From<String> for PreLispValue {
|
|
fn from(value: String) -> Self {
|
|
PreLispValue::String(value)
|
|
}
|
|
}
|
|
impl From<i32> for PreLispValue {
|
|
fn from(value: i32) -> Self {
|
|
PreLispValue::Integer(value)
|
|
}
|
|
}
|
|
impl From<f32> for PreLispValue {
|
|
fn from(value: f32) -> Self {
|
|
PreLispValue::Float(value)
|
|
}
|
|
}
|
|
impl From<Vec<PreLispValue>> for PreLispValue {
|
|
fn from(value: Vec<PreLispValue>) -> Self {
|
|
PreLispValue::List(value, false)
|
|
}
|
|
}
|
|
|
|
// TODO: Handle failure
|
|
fn read_exp(open_paren_idx: usize, tokens: &Vec<Token>) -> Option<(Vec<Token>, usize)> /* Option<tokens, close_paren_idx> */
|
|
{
|
|
let mut tkns = Vec::new();
|
|
let mut depth = 0;
|
|
|
|
for i in open_paren_idx..tokens.len() {
|
|
match &tokens[i] {
|
|
Token::OpenParen => {
|
|
if depth != 0 {
|
|
tkns.push(Token::OpenParen);
|
|
}
|
|
depth += 1;
|
|
}
|
|
Token::CloseParen => {
|
|
depth -= 1;
|
|
if depth == 0 {
|
|
return Some((tkns, i));
|
|
} else {
|
|
tkns.push(Token::CloseParen)
|
|
}
|
|
}
|
|
token => {
|
|
tkns.push(token.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
fn parse_exp(tokens: Vec<Token>, quoted: bool) -> PreLispValue {
|
|
let mut values = Vec::new();
|
|
|
|
if tokens.len() < 1 {
|
|
return PreLispValue::List(values, false);
|
|
}
|
|
|
|
let mut quote_next_list = false;
|
|
let mut unquote_next_list = false;
|
|
|
|
let mut i = 0;
|
|
while i < tokens.len() {
|
|
let tkn = &tokens[i];
|
|
match tkn {
|
|
Token::Quote => {
|
|
quote_next_list = true;
|
|
}
|
|
Token::Unquote => {
|
|
unquote_next_list = true;
|
|
}
|
|
Token::OpenParen => {
|
|
let (tkns, close_paren_idx) = read_exp(i, &tokens).unwrap();
|
|
values.push(parse_exp(
|
|
tkns,
|
|
if unquote_next_list {
|
|
false
|
|
} else {
|
|
quote_next_list || quoted
|
|
},
|
|
));
|
|
i = close_paren_idx;
|
|
quote_next_list = false;
|
|
unquote_next_list = false;
|
|
}
|
|
Token::Identifier(name) => match name.as_str() {
|
|
"nil" => values.push(PreLispValue::Nil),
|
|
"true" => values.push(PreLispValue::True),
|
|
"false" => values.push(PreLispValue::False),
|
|
_ => values.push(PreLispValue::Var(name.to_owned())),
|
|
},
|
|
Token::Index(map, idx) => {
|
|
values.push(PreLispValue::List(
|
|
vec![
|
|
PreLispValue::Var(String::from("get")),
|
|
PreLispValue::Var(map.to_owned()),
|
|
PreLispValue::String(idx.to_owned()),
|
|
],
|
|
false,
|
|
));
|
|
}
|
|
Token::Integer(num) => {
|
|
values.push(PreLispValue::Integer(*num));
|
|
}
|
|
Token::Float(num) => {
|
|
values.push(PreLispValue::Float(*num));
|
|
}
|
|
Token::String(str) => {
|
|
values.push(PreLispValue::String(str.to_owned()));
|
|
}
|
|
Token::CloseParen => {
|
|
panic!("Unexpected closing parenthesis");
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
PreLispValue::List(values, quoted)
|
|
}
|
|
|
|
pub struct Parser {
|
|
tokens: Vec<Token>,
|
|
macros: HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
|
}
|
|
|
|
fn add_macro(
|
|
macros: &mut HashMap<String, fn(Vec<PreLispValue>) -> Vec<PreLispValue>>,
|
|
name: impl Into<String>,
|
|
mac: fn(Vec<PreLispValue>) -> Vec<PreLispValue>,
|
|
) {
|
|
macros.insert(name.into(), mac);
|
|
}
|
|
|
|
impl Parser {
|
|
pub fn new(tokens: Vec<Token>) -> Parser {
|
|
let mut macros = HashMap::new();
|
|
|
|
add_macro(&mut macros, "set", |x| {
|
|
if x.len() != 3
|
|
|| (!matches!(x[1], PreLispValue::Var(_))
|
|
&& !matches!(x[1], PreLispValue::String(_)))
|
|
{
|
|
return x;
|
|
}
|
|
|
|
let mut list = x.clone();
|
|
|
|
if let PreLispValue::Var(name) = &list[1] {
|
|
list[1] = PreLispValue::String(name.to_owned())
|
|
}
|
|
|
|
list
|
|
});
|
|
|
|
add_macro(&mut macros, "fn", |x| {
|
|
if x.len() < 3 || x.len() > 4 {
|
|
return x;
|
|
}
|
|
|
|
let mut name: String = String::new();
|
|
let mut args: &Vec<PreLispValue> = &Vec::new();
|
|
let mut list: &Vec<PreLispValue> = &Vec::new();
|
|
|
|
if x.len() == 3 {
|
|
if !matches!(x[1], PreLispValue::List(_, _))
|
|
|| !matches!(x[2], PreLispValue::List(_, _))
|
|
{
|
|
return x;
|
|
}
|
|
name = String::from("Anonymous");
|
|
if let PreLispValue::List(l, _) = &x[1] {
|
|
args = l;
|
|
}
|
|
if let PreLispValue::List(l, _) = &x[2] {
|
|
list = l;
|
|
}
|
|
} else if x.len() == 4 {
|
|
if !matches!(x[1], PreLispValue::Var(_))
|
|
|| !matches!(x[2], PreLispValue::List(_, _))
|
|
|| !matches!(x[3], PreLispValue::List(_, _))
|
|
{
|
|
return x;
|
|
}
|
|
if let PreLispValue::Var(n) = &x[1] {
|
|
name = n.to_owned();
|
|
}
|
|
if let PreLispValue::List(l, _) = &x[2] {
|
|
args = l;
|
|
}
|
|
if let PreLispValue::List(l, _) = &x[3] {
|
|
list = l;
|
|
}
|
|
} else {
|
|
return x;
|
|
}
|
|
|
|
let mut arg_names = Vec::new();
|
|
for arg in args {
|
|
if let PreLispValue::Var(n) = arg {
|
|
arg_names.push(n.to_owned());
|
|
}
|
|
}
|
|
|
|
let func = PreLispValue::LispFunction(name.clone(), arg_names, list.to_owned());
|
|
|
|
if x.len() == 4 {
|
|
return vec![PreLispValue::Var(String::from("set")), name.into(), func];
|
|
}
|
|
|
|
return vec![PreLispValue::Var(String::from("echo")), func];
|
|
});
|
|
|
|
Parser { tokens, macros }
|
|
}
|
|
|
|
fn macro_check(&mut self, input: Vec<PreLispValue>, quoted: bool) -> Vec<PreLispValue> {
|
|
if quoted || input.len() < 1 {
|
|
return input;
|
|
}
|
|
|
|
let mut output = input.clone();
|
|
|
|
for i in 0..output.len() {
|
|
if let PreLispValue::List(elems, quoted) = &output[i] {
|
|
output[i] = PreLispValue::List(self.macro_check(elems.to_vec(), *quoted), *quoted);
|
|
}
|
|
}
|
|
|
|
if let PreLispValue::Var(name) = &output[0] {
|
|
if self.macros.contains_key(name) {
|
|
return self.macros.get(name).unwrap()(output);
|
|
}
|
|
}
|
|
|
|
output
|
|
}
|
|
|
|
pub fn parse(&mut self) -> Vec<PreLispValue> {
|
|
let mut values = Vec::new();
|
|
|
|
let mut i = 0;
|
|
while i < self.tokens.len() {
|
|
match &self.tokens[i] {
|
|
Token::OpenParen => {
|
|
let (tkns, close_paren_idx) = read_exp(i, &self.tokens).unwrap();
|
|
values.push(parse_exp(tkns, false));
|
|
i = close_paren_idx;
|
|
}
|
|
tkn => {
|
|
// TODO: Include more error data
|
|
panic!("Unexpected token {:?}", tkn);
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
self.macro_check(values, false)
|
|
}
|
|
}
|