diff --git a/schemius/src/core/builtins/special_forms.rs b/schemius/src/core/builtins/special_forms.rs index 77c0855..4bbd5c1 100644 --- a/schemius/src/core/builtins/special_forms.rs +++ b/schemius/src/core/builtins/special_forms.rs @@ -1,5 +1,7 @@ use std::time::Instant; +use crate::core::constants::tokens; + use super::{ eval, r_eval, s_list::SList, @@ -298,11 +300,12 @@ pub fn r_quasiquote(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput Ok(expr) => match expr { SExpr::List(list) => { let s_list = SExpr::List(list.clone()); - let quasiquotes = s_list.find_symbol("quasiquote"); + let quasiquotes = s_list.find_symbol(tokens::QUASIQUOTE_EXPLICIT); let mapping = s_list.matching_brackets().unwrap(); - let unquotes = s_list.find_symbol("unquote"); - let unquotes_splicing = s_list.find_symbol("unquote-splicing"); + let unquotes = s_list.find_symbol(tokens::UNQUOTE_EXPLICIT); + let unquotes_splicing = + s_list.find_symbol(tokens::UNQUOTE_SPLICING_EXPLICIT); let mut unquotes = if let Some(unquotes) = unquotes { unquotes.iter().map(|x| (false, *x)).collect() } else { diff --git a/schemius/src/core/constants.rs b/schemius/src/core/constants.rs new file mode 100644 index 0000000..26c693d --- /dev/null +++ b/schemius/src/core/constants.rs @@ -0,0 +1,44 @@ +pub mod tokens { + pub const EOF: &str = "EOF"; + pub const OPEN_PAREN: &str = "("; + pub const CLOSED_PAREN: &str = ")"; + pub const OPEN_BRACKET: &str = "["; + pub const CLOSED_BRACKET: &str = "]"; + pub const PREFIX: &str = "#"; + pub const VECTOR_OPEN: &str = "#("; + pub const QUOTE: &str = "'"; + pub const QUOTE_EXPLICIT: &str = "quote"; + pub const QUASIQUOTE: &str = "`"; + pub const QUASIQUOTE_EXPLICIT: &str = "quasiquote"; + pub const DOT: &str = "."; + pub const UNQUOTE: &str = ","; + pub const UNQUOTE_EXPLICIT: &str = "unquote"; + pub const UNQUOTE_SPLICING: &str = ",@"; + pub const UNQUOTE_SPLICING_EXPLICIT: &str = "unquote-splicing"; + pub const TRUE: &str = "#t"; + pub const FALSE: &str = "#f"; + pub const PREFIX_CHAR: &str = "#\\"; + pub const PREFIX_STRING: &str = "\""; + pub const SUFFIX_STRING: &str = "\""; + pub const PREFIX_COMMENT: &str = ";"; + pub const PREFIX_BINARY: &str = "#b"; + pub const PREFIX_OCTAL: &str = "#o"; + pub const PREFIX_HEX: &str = "#x"; + pub const PREFIX_DECIMAL: &str = "#d"; + pub const PREFIX_EXACT: &str = "#e"; + pub const PREFIX_INEXACT: &str = "#i"; + pub const POSITIVE_INFINITY: &str = "+inf.0"; + pub const NEGATIVE_INFINITY: &str = "-inf.0"; + pub const POSITIVE_NAN: &str = "+nan.0"; + pub const NEGATIVE_NAN: &str = "-nan.0"; +} + +pub mod numbers { + pub const AVOGADRO: f64 = 6.0221515e23; + pub const BOLTZMANN: f64 = 1.380650e23; + pub const EULER: f64 = 2.718281828459045; + pub const GOLDEN_RATIO: f64 = 1.618033988749895; + pub const GRAVITATIONAL_CONSTANT: f64 = 6.67300e-11; + pub const PI: f64 = 3.141592653589793; + pub const PLANCK: f64 = 6.626068e-34; +} diff --git a/schemius/src/core/interpreter.rs b/schemius/src/core/interpreter.rs index 0677cb3..f5d684a 100644 --- a/schemius/src/core/interpreter.rs +++ b/schemius/src/core/interpreter.rs @@ -4,6 +4,7 @@ use std::io::{self, Write}; use crate::core::reader; use crate::scheme::prelude::PRELUDE; +use super::constants::tokens; use super::environment::Environment; use super::evaluator::EvalOutput; use super::{evaluator::Evaluator, s_expression::SExpr}; @@ -56,7 +57,7 @@ impl Interpreter { let next = next_line(self).unwrap_or("".to_string()); - if next.is_empty() || next.starts_with(';') { + if next.is_empty() || next.starts_with(tokens::PREFIX_COMMENT) { continue; } @@ -88,11 +89,13 @@ impl Interpreter { self.current_expression = next_line(self)?; self.read(next_line); - if self.current_expression.is_empty() || self.current_expression.starts_with(';') { + if self.current_expression.is_empty() + || self.current_expression.starts_with(tokens::PREFIX_COMMENT) + { continue; } - if self.current_expression == "EOF" { + if self.current_expression == tokens::EOF { break; } @@ -122,10 +125,10 @@ impl Interpreter { fn is_preliminarily_validated(&self, expression_string: &str) -> bool { // TODO: Judge the validity of the approach and extend the function if it's worth - !((expression_string.trim().starts_with('(') - && !expression_string.trim_end().ends_with(')')) - || (expression_string.trim().starts_with('[') - && !expression_string.trim_end().ends_with(']'))) + !((expression_string.trim().starts_with(tokens::OPEN_PAREN) + && !expression_string.trim_end().ends_with(tokens::CLOSED_PAREN)) + || (expression_string.trim().starts_with(tokens::OPEN_BRACKET) + && !expression_string.trim_end().ends_with(tokens::CLOSED_BRACKET))) } pub fn eval_expression(&mut self, expression_string: String) -> EvalOutput { @@ -175,7 +178,7 @@ fn read_line_from_file(interpreter: &mut Interpreter) -> Result Ok(line) } else { - Ok(String::from("EOF")) + Ok(String::from(tokens::EOF)) } } diff --git a/schemius/src/core/mod.rs b/schemius/src/core/mod.rs index b58c2a5..f99f6ce 100644 --- a/schemius/src/core/mod.rs +++ b/schemius/src/core/mod.rs @@ -1,5 +1,6 @@ mod accessor; mod builtins; +mod constants; mod environment; mod evaluator; pub mod interpreter; diff --git a/schemius/src/core/reader.rs b/schemius/src/core/reader.rs index 7452970..345ea10 100644 --- a/schemius/src/core/reader.rs +++ b/schemius/src/core/reader.rs @@ -2,7 +2,7 @@ use lazy_static::lazy_static; use num::Num; use regex::Regex; -use super::{accessor::Accessor, s_expression::*}; +use super::{accessor::Accessor, constants::tokens, s_expression::*}; lazy_static! { static ref TOKEN_REGEX: Regex = @@ -27,9 +27,9 @@ pub fn read(line: &mut String) -> Result { fn has_balanced_parentheses(s: &str) -> bool { let mut balance = 0; for c in s.chars() { - match c { - '(' | '[' => balance += 1, - ')' | ']' => balance -= 1, + match c.to_string().as_str() { + tokens::OPEN_PAREN | tokens::OPEN_BRACKET => balance += 1, + tokens::CLOSED_PAREN | tokens::CLOSED_BRACKET => balance -= 1, _ => {} } if balance < 0 { @@ -58,17 +58,18 @@ fn advance(line: &mut String, string_token: &String) -> Result { let opening_token = string_token.as_str(); match opening_token { - "(" | "[" | "#(" => { + tokens::OPEN_PAREN | tokens::OPEN_BRACKET | tokens::VECTOR_OPEN => { let mut new_list = VectorImplementation::new(); loop { let token: String = init(line); - if (opening_token == "(" && token == ")") || (opening_token == "[" && token == "]") + if (opening_token == tokens::OPEN_PAREN && token == tokens::CLOSED_PAREN) + || (opening_token == tokens::OPEN_BRACKET && token == tokens::CLOSED_BRACKET) { - if new_list.len() == 3 && token != "]" { + if new_list.len() == 3 && token != tokens::CLOSED_BRACKET { if let SExpr::Symbol(sym) = &new_list.s_ref(1).unwrap() { - if sym.as_str() == "." { + if sym.as_str() == tokens::DOT { return Ok(SExpr::Pair(SchemePair::new(( Box::new(new_list.s_ref(0).unwrap().clone()), Box::new(new_list.s_ref(2).unwrap().clone()), @@ -80,7 +81,7 @@ fn advance(line: &mut String, string_token: &String) -> Result { return Ok(SExpr::List(SchemeList::new(ListImplementation::from_iter( new_list, )))); - } else if opening_token == "#(" && token == ")" { + } else if opening_token == tokens::VECTOR_OPEN && token == tokens::CLOSED_PAREN { return Ok(SExpr::Vector(SchemeVector::new(VectorImplementation::from( new_list, )))); @@ -95,24 +96,26 @@ fn advance(line: &mut String, string_token: &String) -> Result { fn parse_token(line: &mut String, token: &str) -> Result { match token { - "#t" => Ok(SExpr::Boolean(true)), - "#f" => Ok(SExpr::Boolean(false)), - "-nan.0" | "+nan.0" => Ok(SExpr::Number(SNumber::Float(NativeFloat::NAN))), - "-inf.0" => Ok(SExpr::Number(SNumber::Float(NativeFloat::NEG_INFINITY))), - "+inf.0" => Ok(SExpr::Number(SNumber::Float(NativeFloat::INFINITY))), + tokens::TRUE => Ok(SExpr::Boolean(true)), + tokens::FALSE => Ok(SExpr::Boolean(false)), + tokens::NEGATIVE_NAN | tokens::POSITIVE_NAN => { + Ok(SExpr::Number(SNumber::Float(NativeFloat::NAN))) + } + tokens::NEGATIVE_INFINITY => Ok(SExpr::Number(SNumber::Float(NativeFloat::NEG_INFINITY))), + tokens::POSITIVE_INFINITY => Ok(SExpr::Number(SNumber::Float(NativeFloat::INFINITY))), token if token.starts_with('"') => { Ok(SExpr::String(SchemeString::new(token.get(1..token.len() - 1).unwrap().to_string()))) } - "'" | "`" | "," | ",@" => { + tokens::QUOTE | tokens::QUASIQUOTE | tokens::UNQUOTE | tokens::UNQUOTE_SPLICING => { let internal_token = init(line); let quoted = advance(line, &internal_token)?; let mut vec = ListImplementation::new(); let string_token = match token { - "'" => "quote".to_string(), - "`" => "quasiquote".to_string(), - "," => "unquote".to_string(), - ",@" => "unquote-splicing".to_string(), + tokens::QUOTE => tokens::QUOTE_EXPLICIT.to_string(), + tokens::QUASIQUOTE => tokens::QUASIQUOTE_EXPLICIT.to_string(), + tokens::UNQUOTE => tokens::UNQUOTE_EXPLICIT.to_string(), + tokens::UNQUOTE_SPLICING => tokens::UNQUOTE_SPLICING_EXPLICIT.to_string(), _ => token.to_string(), }; @@ -120,7 +123,7 @@ fn parse_token(line: &mut String, token: &str) -> Result { vec.push(quoted); Ok(SExpr::List(SchemeList::new(vec))) } - token if token.starts_with(r#"#\"#) => { + token if token.starts_with(tokens::PREFIX_CHAR) => { match token.len() { 3 => Ok(SExpr::Char(token.chars().last().unwrap())), _ => { @@ -133,7 +136,7 @@ fn parse_token(line: &mut String, token: &str) -> Result { } _ => { let n_prefixes = if token.len() > 2 - && token.chars().nth(0) == Some('#') + && token.chars().nth(0) == tokens::PREFIX.chars().next() && token.chars().nth(1).is_some_and(|c| c.is_alphabetic()) { if token.len() > 4 @@ -150,24 +153,32 @@ fn parse_token(line: &mut String, token: &str) -> Result { let (has_prefix, radix, is_exact) = if n_prefixes == 2 { match (&token[0..2], &token[2..4]) { - ("#b", "#e") | ("#e", "#b") => (true, 2, Some(true)), - ("#b", "#i") | ("#i", "#b") => (true, 2, Some(false)), - ("#o", "#e") | ("#e", "#o") => (true, 8, Some(true)), - ("#o", "#i") | ("#i", "#o") => (true, 8, Some(false)), - ("#d", "#e") | ("#e", "#d") => (true, 10, Some(true)), - ("#d", "#i") | ("#i", "#d") => (true, 10, Some(false)), - ("#x", "#e") | ("#e", "#x") => (true, 16, Some(true)), - ("#x", "#i") | ("#i", "#x") => (true, 16, Some(false)), + (tokens::PREFIX_BINARY, tokens::PREFIX_EXACT) + | (tokens::PREFIX_EXACT, tokens::PREFIX_BINARY) => (true, 2, Some(true)), + (tokens::PREFIX_BINARY, tokens::PREFIX_INEXACT) + | (tokens::PREFIX_INEXACT, tokens::PREFIX_BINARY) => (true, 2, Some(false)), + (tokens::PREFIX_OCTAL, tokens::PREFIX_EXACT) + | (tokens::PREFIX_EXACT, tokens::PREFIX_OCTAL) => (true, 8, Some(true)), + (tokens::PREFIX_OCTAL, tokens::PREFIX_INEXACT) + | (tokens::PREFIX_INEXACT, tokens::PREFIX_OCTAL) => (true, 8, Some(false)), + (tokens::PREFIX_DECIMAL, tokens::PREFIX_EXACT) + | (tokens::PREFIX_EXACT, tokens::PREFIX_DECIMAL) => (true, 10, Some(true)), + (tokens::PREFIX_DECIMAL, tokens::PREFIX_INEXACT) + | (tokens::PREFIX_INEXACT, tokens::PREFIX_DECIMAL) => (true, 10, Some(false)), + (tokens::PREFIX_HEX, tokens::PREFIX_EXACT) + | (tokens::PREFIX_EXACT, tokens::PREFIX_HEX) => (true, 16, Some(true)), + (tokens::PREFIX_HEX, tokens::PREFIX_INEXACT) + | (tokens::PREFIX_INEXACT, tokens::PREFIX_HEX) => (true, 16, Some(false)), _ => (false, 10, None), } } else if n_prefixes == 1 { match &token[0..2] { - "#b" => (true, 2, None), - "#o" => (true, 8, None), - "#d" => (true, 10, None), - "#x" => (true, 16, None), - "#e" => (true, 10, Some(true)), - "#i" => (true, 10, Some(false)), + tokens::PREFIX_BINARY => (true, 2, None), + tokens::PREFIX_OCTAL => (true, 8, None), + tokens::PREFIX_DECIMAL => (true, 10, None), + tokens::PREFIX_HEX => (true, 16, None), + tokens::PREFIX_EXACT => (true, 10, Some(true)), + tokens::PREFIX_INEXACT => (true, 10, Some(false)), _ => (false, 10, None), } } else { diff --git a/schemius/src/core/s_expression/mod.rs b/schemius/src/core/s_expression/mod.rs index 138ed87..947a5d0 100644 --- a/schemius/src/core/s_expression/mod.rs +++ b/schemius/src/core/s_expression/mod.rs @@ -4,7 +4,7 @@ pub mod s_procedure; use cfg_if::cfg_if; -use super::accessor::*; +use super::{accessor::*, constants::tokens}; use std::{collections::LinkedList, fmt, result, vec}; pub use self::{s_list::*, s_number::*, s_procedure::*}; @@ -46,23 +46,18 @@ pub enum SExpr { Ok, } -pub struct Bracket; - -impl Bracket { - pub const LEFT_ROUND: &'static str = "("; - pub const LEFT_SQUARE: &'static str = "["; - pub const RIGHT_ROUND: &'static str = ")"; - pub const RIGHT_SQUARE: &'static str = "]"; -} - impl fmt::Display for SExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { SExpr::Symbol(ref val) => write!(f, "{}", val), - SExpr::Char(val) => write!(f, "#\\{}", val), + SExpr::Char(val) => write!(f, "{}{}", tokens::PREFIX_CHAR, val), SExpr::Number(val) => write!(f, "{}", val), - SExpr::Boolean(val) => write!(f, "#{}", if *val { "t" } else { "f" }), - SExpr::String(ref val) => write!(f, "\"{}\"", *val.access()), + SExpr::Boolean(val) => { + write!(f, "{}", if *val { tokens::TRUE } else { tokens::FALSE }) + } + SExpr::String(ref val) => { + write!(f, "{}{}{}", tokens::PREFIX_STRING, *val.access(), tokens::SUFFIX_STRING) + } SExpr::Procedure(app) => match app { Procedure::SpecialForm(_) => write!(f, "#"), Procedure::Primitive(_) => write!(f, "#"), @@ -70,7 +65,7 @@ impl fmt::Display for SExpr { }, SExpr::Pair(val) => { let borrowed_val = val.access(); - write!(f, "({} . {})", borrowed_val.0, borrowed_val.1) + write!(f, "({} {} {})", borrowed_val.0, tokens::DOT, borrowed_val.1) } SExpr::List(ref val) => write!( f, @@ -112,7 +107,7 @@ impl SExpr { pub fn quote(&self) -> Result { Ok(SExpr::List(SchemeList::new(ListImplementation::from_iter([ - SExpr::Symbol("quote".to_string()), + SExpr::Symbol(tokens::QUOTE_EXPLICIT.to_string()), self.clone(), ])))) } @@ -146,8 +141,8 @@ impl SExpr { #[allow(dead_code)] fn is_left_bracket(&self) -> Result { - if self.symbol_is(Bracket::LEFT_ROUND).unwrap() - || self.symbol_is(Bracket::LEFT_SQUARE).unwrap() + if self.symbol_is(tokens::OPEN_PAREN).unwrap() + || self.symbol_is(tokens::OPEN_BRACKET).unwrap() { Ok(true) } else { @@ -157,8 +152,8 @@ impl SExpr { #[allow(dead_code)] fn is_right_bracket(&self) -> Result { - if self.symbol_is(Bracket::RIGHT_ROUND).unwrap() - || self.symbol_is(Bracket::RIGHT_SQUARE).unwrap() + if self.symbol_is(tokens::CLOSED_PAREN).unwrap() + || self.symbol_is(tokens::CLOSED_BRACKET).unwrap() { Ok(true) } else { @@ -188,10 +183,10 @@ impl SExpr { } pub fn is_quote(&self) -> Result { - Ok(self.symbol_is("'").unwrap() - || self.symbol_is("quote").unwrap() - || self.symbol_is("`").unwrap() - || self.symbol_is("quasiquote").unwrap()) + Ok(self.symbol_is(tokens::QUOTE).unwrap() + || self.symbol_is(tokens::QUOTE_EXPLICIT).unwrap() + || self.symbol_is(tokens::QUASIQUOTE).unwrap() + || self.symbol_is(tokens::QUASIQUOTE_EXPLICIT).unwrap()) } pub fn is_quoted_list(&self) -> Result { @@ -387,7 +382,7 @@ impl SExpr { match self { SExpr::List(list) => { let list = list.access(); - if !list.s_car().unwrap().symbol_is("(").unwrap() { + if !list.s_car().unwrap().symbol_is(tokens::OPEN_PAREN).unwrap() { return None; } @@ -398,7 +393,7 @@ impl SExpr { .enumerate() .filter(|x| { (pairs.is_empty() || pairs.iter().all(|(_, right)| right != &x.0)) - && x.1.symbol_is(")").unwrap() + && x.1.symbol_is(tokens::CLOSED_PAREN).unwrap() }) .min_by(|x, y| (x.0).cmp(&y.0)) .map(|x| x.0) @@ -408,7 +403,7 @@ impl SExpr { .enumerate() .filter(|x| { (pairs.is_empty() || pairs.iter().all(|(left, _)| left != &x.0)) - && x.1.symbol_is("(").unwrap() + && x.1.symbol_is(tokens::OPEN_PAREN).unwrap() }) .filter(|x| x.0 < right) .max_by(|x, y| (x.0).cmp(&y.0)) @@ -446,7 +441,7 @@ impl SExpr { Ok(SExpr::List(list)) => { let borrowed_list = list.access(); - if borrowed_list.s_car().unwrap().symbol_is("(").unwrap() { + if borrowed_list.s_car().unwrap().symbol_is(tokens::OPEN_PAREN).unwrap() { return None; } @@ -471,7 +466,7 @@ impl SExpr { match self { SExpr::List(list) => { let mut new_list = ListImplementation::new(); - new_list.push(SExpr::Symbol(String::from("("))); + new_list.push(SExpr::Symbol(String::from(tokens::OPEN_PAREN))); list.access().iter().for_each(|item| match item { SExpr::List(_) => { @@ -482,7 +477,7 @@ impl SExpr { other => new_list.push(other.clone()), }); - new_list.push(SExpr::Symbol(String::from(")"))); + new_list.push(SExpr::Symbol(String::from(tokens::CLOSED_PAREN))); Ok(SExpr::List(SchemeList::new(new_list.clone()))) } @@ -490,7 +485,7 @@ impl SExpr { let pair = pair.access(); SExpr::List(SchemeList::new(ListImplementation::from_iter([ *pair.0.clone(), - SExpr::Symbol(".".to_string()), + SExpr::Symbol(tokens::DOT.to_string()), *pair.1.clone(), ]))) .with_explicit_parens() @@ -506,7 +501,7 @@ impl SExpr { let mut list_without_parens = VectorImplementation::new(); list.access().iter().for_each(|expr| list_without_parens.push(expr.clone())); - if !list_without_parens.first().unwrap().symbol_is("(").unwrap() { + if !list_without_parens.first().unwrap().symbol_is(tokens::OPEN_PAREN).unwrap() { return Ok(self.clone()); } @@ -520,7 +515,7 @@ impl SExpr { match list_without_parens .iter() .enumerate() - .filter(|x| x.1.symbol_is(")").unwrap()) + .filter(|x| x.1.symbol_is(tokens::CLOSED_PAREN).unwrap()) .min_by(|x, y| (x.0).cmp(&y.0)) .map(|x| x.0) { @@ -528,7 +523,7 @@ impl SExpr { match list_without_parens .iter() .enumerate() - .filter(|x| x.1.symbol_is("(").unwrap()) + .filter(|x| x.1.symbol_is(tokens::OPEN_PAREN).unwrap()) .filter(|x| x.0 < r) .max_by(|x, y| (x.0).cmp(&y.0)) .map(|x| x.0) diff --git a/schemius/src/core/s_expression/s_number.rs b/schemius/src/core/s_expression/s_number.rs index 1ad9eb8..192ef9c 100644 --- a/schemius/src/core/s_expression/s_number.rs +++ b/schemius/src/core/s_expression/s_number.rs @@ -8,6 +8,8 @@ use std::{ use cfg_if::cfg_if; use num::{integer::Roots, BigInt, BigRational, Complex, One, ToPrimitive, Zero}; +use crate::core::constants::numbers; + cfg_if! { if #[cfg(i32)] { pub type NativeInt = i32; @@ -46,13 +48,13 @@ pub enum SNumber { pub struct NumericalConstant; impl NumericalConstant { - pub const AVOGADRO: SNumber = SNumber::Float(6.0221515e23); - pub const BOLTZMANN: SNumber = SNumber::Float(1.380650e23); - pub const EULER: SNumber = SNumber::Float(2.718281828459045); - pub const GOLDEN_RATIO: SNumber = SNumber::Float(1.618033988749895); - pub const GRAVITATIONAL_CONSTANT: SNumber = SNumber::Float(6.67300e-11); - pub const PI: SNumber = SNumber::Float(3.141592653589793); - pub const PLANCK: SNumber = SNumber::Float(6.626068e-34); + pub const AVOGADRO: SNumber = SNumber::Float(numbers::AVOGADRO); + pub const BOLTZMANN: SNumber = SNumber::Float(numbers::BOLTZMANN); + pub const EULER: SNumber = SNumber::Float(numbers::EULER); + pub const GOLDEN_RATIO: SNumber = SNumber::Float(numbers::GOLDEN_RATIO); + pub const GRAVITATIONAL_CONSTANT: SNumber = SNumber::Float(numbers::GRAVITATIONAL_CONSTANT); + pub const PI: SNumber = SNumber::Float(numbers::PI); + pub const PLANCK: SNumber = SNumber::Float(numbers::PLANCK); } pub trait NativeCasts {