diff --git a/src/eval.rs b/src/eval.rs index b9b70df..d47788b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -7,13 +7,7 @@ use std::{ use thiserror::Error; use Trampoline::{Continue, Done, Raise}; -use crate::{ - semantic::{ - defmacro::{keyword, soft_vec}, - Expr, Literal, - }, - SrcPos, Term, -}; +use crate::{keyword, soft_vec, Expr, Literal, SrcPos, Term}; #[derive(Clone)] pub struct Definition { @@ -30,20 +24,22 @@ pub struct Frame { pub is_catching_scope: bool, } +/// Closure function. #[derive(Clone)] pub struct Fun { pub parameters: Vec, pub body: Expr, } -impl Fun { - pub fn call(&self, environment: &Environment, arguments: Vec) -> Trampoline { - let _ = environment; - let _ = arguments; - todo!() - } +/// Bail out of the current evaluation with an error. +macro_rules! bail { + ($expr:expr) => { + return $crate::eval::Trampoline::Raise($expr.into()) + }; } +/// A value in the language. It's the lowest level of representation of a +/// value, and is used for both the AST and the runtime. #[derive(Clone)] pub enum Value { Int(u64), @@ -78,28 +74,51 @@ pub struct Keyword { pub is_atom: bool, } -impl From for Keyword { - fn from(name: String) -> Self { - Self { - name, - is_atom: false, - } - } +/// The environment in which evaluation takes place. +pub struct Environment { + pub global: Value, + pub expanded: bool, + pub frames: Arc>>, } -impl From<&str> for Keyword { - fn from(name: &str) -> Self { - Self { - name: name.to_string(), - is_atom: false, +/// Errors that can occur during evaluation. +#[derive(Error, Debug, Clone)] +pub enum EvalError { + #[error("undefined keyword")] + UndefinedKeyword(Keyword), + + #[error("expected fun")] + ExpectedFun, + + #[error("expected atomic")] + ExpectedAtomic, +} + +impl From for Expr { + fn from(value: EvalError) -> Self { + match value { + EvalError::UndefinedKeyword(Keyword { name, .. }) => { + soft_vec!(keyword!("eval.error/expected-keyword"), name) + } + EvalError::ExpectedFun => keyword!("eval.error/expected-fun"), + EvalError::ExpectedAtomic => keyword!("eval.error/expected-atomic"), } } } -pub struct Environment { - pub global: Value, - pub expanded: bool, - pub frames: Arc>>, +/// Errors that can occur during expansion. +#[derive(Error, Debug, Clone)] +pub enum ExpansionError { + #[error("expected keyword")] + ExpectedKeyword, +} + +impl From for Expr { + fn from(error: ExpansionError) -> Self { + match error { + ExpansionError::ExpectedKeyword => keyword!("eval.error/expected-keyword"), + } + } } impl Environment { @@ -143,8 +162,17 @@ impl TryFrom for Keyword { } } +impl Fun { + /// Call the function. + pub fn call(&self, environment: &Environment, arguments: Vec) -> Trampoline { + let _ = environment; + let _ = arguments; + todo!() + } +} + /// Expand apply expressions. -fn apply_expand(apply: crate::semantic::Apply, environment: &Environment) -> Result { +fn apply_expand(apply: crate::Apply, environment: &Environment) -> Result { let callee = apply.callee()?; if let Expr::Literal(Literal(Term::Identifier(k) | Term::Atom(k))) = callee { return match environment.find_definition(k.clone()) { @@ -183,7 +211,7 @@ fn apply_expand(apply: crate::semantic::Apply, environment: &Environment) -> Res } /// Expand fun expressions. -fn fun_expand(fun: crate::semantic::Fun, environment: &Environment) -> Result { +fn fun_expand(fun: crate::Fun, environment: &Environment) -> Result { Ok(Value::Fun(Fun { parameters: fun .parameters()? @@ -198,21 +226,6 @@ fn fun_expand(fun: crate::semantic::Fun, environment: &Environment) -> Result for Expr { - fn from(error: ExpansionError) -> Self { - match error { - ExpansionError::ExpectedKeyword => keyword!("eval.error/expected-keyword"), - } - } -} - impl Expr { /// Expand the expression into a value. pub fn expand(self, environment: &Environment) -> Result { @@ -246,7 +259,7 @@ impl Expr { // Expansion of literal terms, just wrap them in a value. This is // the base case of the expansion. - Expr::Quote(expr) => Ok(Value::Quote(expr.expression()?)), + Expr::Quote(expr) => Ok(Value::Quote(expr.expr()?)), Expr::Literal(Literal(Term::Int(value))) => Ok(Value::Int(value)), Expr::Literal(Literal(Term::String(value))) => Ok(Value::String(value)), Expr::Literal(Literal(Term::Float(_, _))) => todo!(), @@ -267,31 +280,6 @@ impl Expr { } } -/// Errors that can occur during evaluation. -#[derive(Error, Debug, Clone)] -pub enum EvalError { - #[error("undefined keyword")] - UndefinedKeyword(Keyword), - - #[error("expected fun")] - ExpectedFun, - - #[error("expected atomic")] - ExpectedAtomic, -} - -impl From for Expr { - fn from(value: EvalError) -> Self { - match value { - EvalError::UndefinedKeyword(Keyword { name, .. }) => { - soft_vec!(keyword!("eval.error/expected-keyword"), name) - } - EvalError::ExpectedFun => keyword!("eval.error/expected-fun"), - EvalError::ExpectedAtomic => keyword!("eval.error/expected-atomic"), - } - } -} - impl Value { /// Evaluate the expression into a value. pub fn eval(self, environment: &Environment) -> Trampoline { @@ -360,10 +348,20 @@ impl> FromResidual> for Trampoline } } -macro_rules! bail { - ($expr:expr) => { - return $crate::eval::Trampoline::Raise($expr.into()) - }; +impl From for Keyword { + fn from(name: String) -> Self { + Self { + name, + is_atom: false, + } + } } -use bail; +impl From<&str> for Keyword { + fn from(name: &str) -> Self { + Self { + name: name.to_string(), + is_atom: false, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index c5a85f7..c082c51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,8 @@ use std::fmt::Display; pub mod eval; pub mod parser; -pub mod semantic; + +pub type Result = std::result::Result; /// Term is a recursive data structure that represents a list of terms, an atom, an identifier, /// or an integer. @@ -23,27 +24,95 @@ pub enum Term { SrcPos(SrcPos, Box), } -impl Display for Term { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.pretty_print(f, 0) - } +pub trait ExprKind: Sized { + fn try_new(term: Term) -> Result>; } -impl From for Term { - fn from(s: String) -> Self { - Term::String(s) - } +// Abstract-Syntax-Tree (AST) for the Soft programming language. It's the representation +// of abstract terms in the language. +define_ast!(Expr, { + Fun, // (fun* [a b] (+ a b)) + List, // [a b c] or (list a b c) + Apply, // (a b c) or (apply a b c) + Def, // (def* a 123) + Recur, // (recur a) + DefMacro, // (defmacro* a (fun (a b) (+ a b)) + Quote, // '(fun* (a b) (+ a b)) + Literal // 123 | "bla" | :bla | bla +}); + +define_builtin!(DefMacro, "defmacro*", 2); +define_builtin!(Def, "def*", 2); +define_builtin!(Recur, "recur"); +define_builtin!(Fun, "fun*", 2); +define_builtin!(Quote, "'", 2); +define_builtin!(Apply, "apply"); + +/// Semantic errors that can occur during the specialization of an expression. +#[derive(thiserror::Error, Debug, Clone)] +pub enum SemanticError { + #[error("invalid expression")] + InvalidExpression, + + #[error("invalid list")] + InvalidList, + + #[error("invalid arguments")] + InvalidArguments, + + #[error("missing function parameters")] + MissingParameters, + + #[error("missing function body")] + MissingBody, + + #[error("missing application head")] + MissingHead, + + #[error("expected string")] + ExpectedString, + + #[error("expected vector with size {0}")] + ExpectedVectorWithSize(usize), + + #[error("invalid quote expression")] + ExpectedQuoteExpression, } -impl From for Term { - fn from(n: usize) -> Self { - Term::Int(n as u64) +/// Meta information about a term, or any other part of the AST. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct SrcPos { + pub byte: std::ops::Range, + pub file: String, +} + +impl SrcPos { + pub fn next(&mut self, c: char) { + self.byte.end += c.len_utf8(); + } + + pub fn reset(&mut self) { + self.byte.start = self.byte.end; } } -impl From> for Term { - fn from(terms: Vec) -> Self { - Term::List(terms) +impl From for Expr { + fn from(value: SemanticError) -> Self { + match value { + SemanticError::InvalidExpression => keyword!("error/invalid-expression"), + SemanticError::InvalidList => keyword!("error/invalid-list"), + SemanticError::InvalidArguments => keyword!("error/invalid-arguments"), + SemanticError::MissingParameters => keyword!("error/missing-parameters"), + SemanticError::MissingBody => keyword!("error/missing-body"), + SemanticError::MissingHead => keyword!("error/missing-head"), + SemanticError::ExpectedString => keyword!("error/expected-string"), + SemanticError::ExpectedVectorWithSize(size) => { + soft_vec![keyword!("error/expected-vector"), size] + } + SemanticError::ExpectedQuoteExpression => { + keyword!("error/expected-quote-expression") + } + } } } @@ -137,19 +206,348 @@ impl Term { } } -/// Meta information about a term, or any other part of the AST. -#[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct SrcPos { - pub byte: std::ops::Range, - pub file: String, +/// Fun expression constructs, it's a function that has a list of parameters and a body. +pub mod fun { + use super::*; + + impl Fun { + /// Returns a list of parameters that are in the spine of the function. + pub fn parameters(&self) -> Result { + self.0 + .at(1) + .map(List) + .ok_or(SemanticError::MissingParameters) + } + + /// Returns the body of the function. + pub fn body(&self) -> Result { + self.0.at(2).ok_or(SemanticError::MissingBody)?.try_into() + } + } } -impl SrcPos { - pub fn next(&mut self, c: char) { - self.byte.end += c.len_utf8(); +/// List expression construct, it's a list of expressions. +pub mod list { + use SemanticError::*; + + use super::*; + + impl ExprKind for List { + fn try_new(term: Term) -> Result> { + if let Term::Vec(ref vec) | Term::SrcPos(_, box Term::Vec(ref vec)) = term { + let items = vec.clone().into(); + return Ok(Some(List(term.transport(items)).into())); + } + + let (head, tail) = term.split().ok_or(InvalidExpression)?; + if head.is_keyword("list") { + let tail = assert_length(tail, 1)?; + Ok(Some(List(term.transport(tail.into())).into())) + } else { + Ok(None) + } + } } - pub fn reset(&mut self) { - self.byte.start = self.byte.end; + impl List { + /// Returns a list of expressions that are in the spine of the list. + pub fn elements(&self) -> Result> { + self.0 + .spine() + .ok_or(SemanticError::InvalidList)? + .into_iter() + .map(Expr::try_from) + .collect() + } + } +} + +/// Apply expression construct, it's a function application. +pub mod apply { + use super::*; + + impl Apply { + /// Returns the callee of the application. + pub fn callee(&self) -> Result { + self.0.at(0).ok_or(SemanticError::MissingHead)?.try_into() + } + + /// Returns a list of arguments that are in the spine of the application. + pub fn spine(&self) -> Result> { + self.0 + .spine() + .ok_or(SemanticError::InvalidArguments)? + .into_iter() + .skip(1) // Skip the head of the application. + .map(Expr::try_from) + .collect() + } + } +} + +/// Recur expression construct, it's a function application. +pub mod recur { + use super::*; + + impl Recur { + /// Returns a list of arguments that are in the spine of the application. + pub fn spine(&self) -> Result> { + self.0 + .spine() + .ok_or(SemanticError::InvalidArguments)? + .into_iter() + .skip(1) // Skip the head of the application. + .map(Expr::try_from) + .collect() + } + } +} + +/// Define expression construct, it's a definition of a value. +pub mod def { + pub use super::*; + + impl Def { + /// Returns the name of the definition. + pub fn name(&self) -> Result { + self.0 + .at(1) + .ok_or(SemanticError::InvalidExpression)? + .try_into() + } + + /// Returns the value of the definition. + pub fn value(&self) -> Result { + self.0 + .at(2) + .ok_or(SemanticError::InvalidExpression)? + .try_into() + } + } +} + +/// Macro define expression construct, it's a definition of a value. +pub mod defmacro { + pub use super::*; + + impl DefMacro { + /// Returns the name of the definition. + pub fn name(&self) -> Result { + self.0 + .at(1) + .ok_or(SemanticError::InvalidExpression)? + .try_into() + } + + /// Returns the value of the definition. + pub fn value(&self) -> Result { + self.0 + .at(2) + .ok_or(SemanticError::InvalidExpression)? + .try_into() + } + } +} + +/// Quote expression construct, it's a quoted expression. +pub mod quote { + use super::*; + + impl Quote { + /// Returns the quoted expression. + pub fn expr(&self) -> Result { + self.0 + .at(1) + .ok_or(SemanticError::InvalidExpression)? + .try_into() + } + } +} + +/// Literal expression construct, it's a literal value. +pub mod literal { + use super::*; + + impl ExprKind for Literal { + fn try_new(term: Term) -> Result> { + Ok(Some(Expr::Literal(Literal(term)))) + } + } + + impl From for Expr { + fn from(value: String) -> Self { + Expr::Literal(Literal(Term::String(value))) + } + } + + impl From for Expr { + fn from(value: u64) -> Self { + Expr::Literal(Literal(Term::Int(value))) + } + } + + impl From for Expr { + fn from(value: usize) -> Self { + Expr::Literal(Literal(Term::Int(value as u64))) + } + } + + impl Expr { + pub fn new_keyword(keyword: &str) -> Self { + Expr::Literal(Literal(Term::Atom(keyword.to_string()))) + } + + /// Expects a string literal and returns it's value. + pub fn string(&self) -> Result { + match self { + Expr::Literal(Literal(Term::String(string))) => Ok(string.clone()), + _ => Err(SemanticError::ExpectedString), + } + } + } +} + +fn assert_length(list: Vec, length: usize) -> Result, SemanticError> { + if list.len() != length { + Err(SemanticError::ExpectedVectorWithSize(length)) + } else { + Ok(list) + } +} + +impl TryFrom for Expr { + type Error = SemanticError; + + fn try_from(value: Term) -> Result { + DefMacro::try_new(value.clone()) + .or_else(|_| Recur::try_new(value.clone())) + .or_else(|_| Def::try_new(value.clone())) + .or_else(|_| Fun::try_new(value.clone())) + .or_else(|_| Quote::try_new(value.clone())) + .or_else(|_| Apply::try_new(value.clone())) + .or_else(|_| List::try_new(value.clone())) + .or_else(|_| Literal::try_new(value.clone()))? + .ok_or(SemanticError::InvalidExpression) + } +} + +impl Display for Term { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.pretty_print(f, 0) + } +} + +impl From for Term { + fn from(s: String) -> Self { + Term::String(s) + } +} + +impl From for Term { + fn from(n: usize) -> Self { + Term::Int(n as u64) + } +} + +impl From> for Term { + fn from(terms: Vec) -> Self { + Term::List(terms) } } + +#[macro_export] +macro_rules! soft_vec { + ($($expr:expr),*) => { + $crate::Expr::List($crate::List($crate::Term::Vec(vec![$($expr.into()),*]))) + }; +} + +#[macro_export] +macro_rules! keyword { + ($str:literal) => { + $crate::Expr::new_keyword($str) + }; +} + +#[macro_export] +macro_rules! define_builtin { + ($name:ident, $keyword:expr, $length:expr) => { + impl $crate::ExprKind for $name { + fn try_new(term: $crate::Term) -> $crate::Result> { + let (head, tail) = term + .split() + .ok_or($crate::SemanticError::InvalidExpression)?; + if head.is_keyword($keyword) { + let tail = $crate::assert_length(tail, $length)?; + Ok(Some($name(term.transport(tail.into())).into())) + } else { + Ok(None) + } + } + } + }; + ($name:ident, $keyword:expr) => { + impl $crate::ExprKind for $name { + fn try_new(term: $crate::Term) -> $crate::Result> { + let Some((head, tail)) = term.split() else { + return Ok(None); + }; + if head.is_keyword($keyword) { + Ok(Some($name(term.transport(tail.into())).into())) + } else { + Ok(Some($crate::Expr::from($name(term)))) + } + } + } + }; +} + +#[macro_export] +macro_rules! define_ast { + ($(#[$outer:meta])* $name:ident, {$($(#[$field_outer:meta])* $variant:ident),+}) => { + $(#[$outer])* + #[derive(Debug, Clone)] + pub enum $name { + $( + $(#[$field_outer])* + $variant($variant) + ),+ + } + + impl From<$name> for $crate::Term { + fn from(value: $name) -> Self { + match value { + $( + $name::$variant(value) => value.into(), + )+ + } + } + } + + $( + impl From<$variant> for $name { + fn from(value: $variant) -> Self { + $name::$variant(value) + } + } + + $(#[$field_outer])* + #[derive(Debug, Clone)] + pub struct $variant(pub $crate::Term); + + impl From<$variant> for $crate::Term { + fn from(value: $variant) -> Self { + value.0 + } + } + + impl std::ops::Deref for $variant { + type Target = $crate::Term; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + )+ + }; +} diff --git a/src/main.rs b/src/main.rs index f3f9fa7..214a383 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,11 @@ -pub mod repl; +use std::path::PathBuf; use clap::Parser; use miette::IntoDiagnostic; +use rustyline::{ + error::ReadlineError, validate::MatchingBracketValidator, Completer, Editor, Helper, + Highlighter, Hinter, Validator, +}; /// Simple program to greet a person #[derive(Parser, Debug)] @@ -16,6 +20,12 @@ struct Args { repl: bool, } +#[derive(Completer, Helper, Highlighter, Hinter, Validator)] +struct InputValidator { + #[rustyline(Validator)] + brackets: MatchingBracketValidator, +} + fn main() -> miette::Result<()> { // Install the panic handler. bupropion::install(bupropion::BupropionHandlerOpts::new).into_diagnostic()?; @@ -24,8 +34,54 @@ fn main() -> miette::Result<()> { let args = Args::parse(); if args.repl { - repl::repl(); + repl(); } Ok(()) } + +fn get_history_path() -> Option { + let home_env = std::env::var("HOME").ok()?; + let path = format!("{home_env}/.soft.history"); + Some(PathBuf::from(path)) +} + +pub fn repl() { + let mut rl = Editor::new().expect("cannot create repl"); + let path = get_history_path(); + let h = InputValidator { + brackets: MatchingBracketValidator::new(), + }; + + rl.set_helper(Some(h)); + + if let Some(path) = path.clone() { + if rl.load_history(&path).is_err() { + println!("No previous history."); + } + } + + loop { + match rl.readline("> ") { + Ok(line) => { + rl.add_history_entry(line.as_str()).unwrap(); + println!("Line: {}", line) + } + Err(ReadlineError::Interrupted) => { + println!("Interrupted"); + break; + } + Err(ReadlineError::Eof) => { + break; + } + Err(err) => { + println!("Error: {err:?}"); + break; + } + } + } + + if let Some(path) = path { + let _ = rl.append_history(&path); + } +} diff --git a/src/repl.rs b/src/repl.rs deleted file mode 100644 index 5f12cf7..0000000 --- a/src/repl.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::path::PathBuf; - -use rustyline::error::ReadlineError; -use rustyline::validate::MatchingBracketValidator; -use rustyline::Editor; -use rustyline::{Completer, Helper, Highlighter, Hinter, Validator}; - -fn get_history_path() -> Option { - let home_env = std::env::var("HOME").ok()?; - let path = format!("{home_env}/.soft.history"); - Some(PathBuf::from(path)) -} - -#[derive(Completer, Helper, Highlighter, Hinter, Validator)] -struct InputValidator { - #[rustyline(Validator)] - brackets: MatchingBracketValidator, -} - -pub fn repl() { - let mut rl = Editor::new().expect("cannot create repl"); - - let path = get_history_path(); - - let h = InputValidator { - brackets: MatchingBracketValidator::new(), - }; - - rl.set_helper(Some(h)); - - if let Some(path) = path.clone() { - if rl.load_history(&path).is_err() { - println!("No previous history."); - } - } - - loop { - let readline = rl.readline("> "); - - match readline { - Ok(line) => { - rl.add_history_entry(line.as_str()).unwrap(); - println!("Line: {}", line); - } - Err(ReadlineError::Interrupted) => { - println!("Interrupted"); - break; - } - Err(ReadlineError::Eof) => { - break; - } - Err(err) => { - println!("Error: {err:?}"); - break; - } - } - } - - if let Some(path) = path { - let _ = rl.append_history(&path); - } -} diff --git a/src/semantic.rs b/src/semantic.rs deleted file mode 100644 index d2451bc..0000000 --- a/src/semantic.rs +++ /dev/null @@ -1,402 +0,0 @@ -use crate::Term; - -define_ast!(Expr, { - Fun, // (fun* [a b] (+ a b)) - List, // [a b c] or (list a b c) - Apply, // (a b c) or (apply a b c) - Def, // (def* a 123) - Recur, // (recur a) - DefMacro, // (defmacro* a (fun (a b) (+ a b)) - Quote, // '(fun* (a b) (+ a b)) - Literal // 123 | "bla" | :bla | bla -}); - -define_builtin!(DefMacro, "defmacro*", 2); -define_builtin!(Def, "def*", 2); -define_builtin!(Recur, "recur"); -define_builtin!(Fun, "fun*", 2); -define_builtin!(Quote, "'", 2); -define_builtin!(Apply, "apply"); - -pub type Result = std::result::Result; - -/// Fun expression constructs, it's a function that has a list of parameters and a body. -pub mod fun { - use super::*; - - impl Fun { - /// Returns a list of parameters that are in the spine of the function. - pub fn parameters(&self) -> Result { - self.0 - .at(1) - .map(List) - .ok_or(SemanticError::MissingParameters) - } - - /// Returns the body of the function. - pub fn body(&self) -> Result { - self.0.at(2).ok_or(SemanticError::MissingBody)?.try_into() - } - } -} - -/// List expression construct, it's a list of expressions. -pub mod list { - use SemanticError::*; - - use super::*; - - impl ExpressionKind for List { - fn try_new(term: Term) -> Result> { - if let Term::Vec(ref vec) | Term::SrcPos(_, box Term::Vec(ref vec)) = term { - let items = vec.clone().into(); - return Ok(Some(List(term.transport(items)).into())); - } - - let (head, tail) = term.split().ok_or(InvalidExpression)?; - if head.is_keyword("list") { - let tail = assert_length(tail, 1)?; - Ok(Some(List(term.transport(tail.into())).into())) - } else { - Ok(None) - } - } - } - - impl List { - /// Returns a list of expressions that are in the spine of the list. - pub fn elements(&self) -> Result> { - self.0 - .spine() - .ok_or(SemanticError::InvalidList)? - .into_iter() - .map(Expr::try_from) - .collect() - } - } -} - -/// Apply expression construct, it's a function application. -pub mod apply { - use super::*; - - impl Apply { - /// Returns the callee of the application. - pub fn callee(&self) -> Result { - self.0.at(0).ok_or(SemanticError::MissingHead)?.try_into() - } - - /// Returns a list of arguments that are in the spine of the application. - pub fn spine(&self) -> Result> { - self.0 - .spine() - .ok_or(SemanticError::InvalidArguments)? - .into_iter() - .skip(1) // Skip the head of the application. - .map(Expr::try_from) - .collect() - } - } -} - -/// Recur expression construct, it's a function application. -pub mod recur { - use super::*; - - impl Recur { - /// Returns a list of arguments that are in the spine of the application. - pub fn spine(&self) -> Result> { - self.0 - .spine() - .ok_or(SemanticError::InvalidArguments)? - .into_iter() - .skip(1) // Skip the head of the application. - .map(Expr::try_from) - .collect() - } - } -} - -/// Define expression construct, it's a definition of a value. -pub mod def { - pub use super::*; - - impl Def { - /// Returns the name of the definition. - pub fn name(&self) -> Result { - self.0 - .at(1) - .ok_or(SemanticError::InvalidExpression)? - .try_into() - } - - /// Returns the value of the definition. - pub fn value(&self) -> Result { - self.0 - .at(2) - .ok_or(SemanticError::InvalidExpression)? - .try_into() - } - } -} - -/// Macro define expression construct, it's a definition of a value. -pub mod defmacro { - pub use super::*; - - impl DefMacro { - /// Returns the name of the definition. - pub fn name(&self) -> Result { - self.0 - .at(1) - .ok_or(SemanticError::InvalidExpression)? - .try_into() - } - - /// Returns the value of the definition. - pub fn value(&self) -> Result { - self.0 - .at(2) - .ok_or(SemanticError::InvalidExpression)? - .try_into() - } - } -} - -/// Quote expression construct, it's a quoted expression. -pub mod quote { - use super::*; - - impl Quote { - /// Returns the quoted expression. - pub fn expression(&self) -> Result { - self.0 - .at(1) - .ok_or(SemanticError::InvalidExpression)? - .try_into() - } - } -} - -/// Literal expression construct, it's a literal value. -pub mod literal { - use super::*; - - impl ExpressionKind for Literal { - fn try_new(term: Term) -> Result> { - Ok(Some(Expr::Literal(Literal(term)))) - } - } - - impl From for Expr { - fn from(value: String) -> Self { - Expr::Literal(Literal(Term::String(value))) - } - } - - impl From for Expr { - fn from(value: u64) -> Self { - Expr::Literal(Literal(Term::Int(value))) - } - } - - impl From for Expr { - fn from(value: usize) -> Self { - Expr::Literal(Literal(Term::Int(value as u64))) - } - } - - impl Expr { - pub fn new_keyword(keyword: &str) -> Self { - Expr::Literal(Literal(Term::Atom(keyword.to_string()))) - } - - /// Expects a string literal and returns it's value. - pub fn string(&self) -> Result { - match self { - Expr::Literal(Literal(Term::String(string))) => Ok(string.clone()), - _ => Err(SemanticError::ExpectedString), - } - } - } -} - -#[derive(thiserror::Error, Debug, Clone)] -pub enum SemanticError { - #[error("invalid expression")] - InvalidExpression, - - #[error("invalid list")] - InvalidList, - - #[error("invalid arguments")] - InvalidArguments, - - #[error("missing function parameters")] - MissingParameters, - - #[error("missing function body")] - MissingBody, - - #[error("missing application head")] - MissingHead, - - #[error("expected string")] - ExpectedString, - - #[error("expected vector with size {0}")] - ExpectedVectorWithSize(usize), - - #[error("invalid quote expression")] - ExpectedQuoteExpression, -} - -impl From for Expr { - fn from(value: SemanticError) -> Self { - match value { - SemanticError::InvalidExpression => keyword!("error/invalid-expression"), - SemanticError::InvalidList => keyword!("error/invalid-list"), - SemanticError::InvalidArguments => keyword!("error/invalid-arguments"), - SemanticError::MissingParameters => keyword!("error/missing-parameters"), - SemanticError::MissingBody => keyword!("error/missing-body"), - SemanticError::MissingHead => keyword!("error/missing-head"), - SemanticError::ExpectedString => keyword!("error/expected-string"), - SemanticError::ExpectedVectorWithSize(size) => { - soft_vec![keyword!("error/expected-vector"), size] - } - SemanticError::ExpectedQuoteExpression => { - keyword!("error/expected-quote-expression") - } - } - } -} - -fn assert_length(list: Vec, length: usize) -> Result, SemanticError> { - if list.len() != length { - Err(SemanticError::ExpectedVectorWithSize(length)) - } else { - Ok(list) - } -} - -impl TryFrom for Expr { - type Error = SemanticError; - - fn try_from(value: Term) -> Result { - DefMacro::try_new(value.clone()) - .or_else(|_| Recur::try_new(value.clone())) - .or_else(|_| Def::try_new(value.clone())) - .or_else(|_| Fun::try_new(value.clone())) - .or_else(|_| Quote::try_new(value.clone())) - .or_else(|_| Apply::try_new(value.clone())) - .or_else(|_| List::try_new(value.clone())) - .or_else(|_| Literal::try_new(value.clone()))? - .ok_or(SemanticError::InvalidExpression) - } -} - -pub trait ExpressionKind: Sized { - fn try_new(term: Term) -> Result>; -} - -macro_rules! define_builtin { - ($name:ident, $keyword:expr, $length:expr) => { - impl $crate::semantic::ExpressionKind for $name { - fn try_new( - term: $crate::Term, - ) -> $crate::semantic::Result> { - let (head, tail) = term - .split() - .ok_or($crate::semantic::SemanticError::InvalidExpression)?; - if head.is_keyword($keyword) { - let tail = $crate::semantic::assert_length(tail, $length)?; - Ok(Some($name(term.transport(tail.into())).into())) - } else { - Ok(None) - } - } - } - }; - ($name:ident, $keyword:expr) => { - impl $crate::semantic::ExpressionKind for $name { - fn try_new( - term: $crate::Term, - ) -> $crate::semantic::Result> { - let Some((head, tail)) = term.split() else { - return Ok(None); - }; - if head.is_keyword($keyword) { - Ok(Some($name(term.transport(tail.into())).into())) - } else { - Ok(Some($crate::semantic::Expr::from($name(term)))) - } - } - } - }; -} - -macro_rules! define_ast { - ($(#[$outer:meta])* $name:ident, {$($(#[$field_outer:meta])* $variant:ident),+}) => { - $(#[$outer])* - #[derive(Debug, Clone)] - pub enum $name { - $( - $(#[$field_outer])* - $variant($variant) - ),+ - } - - impl From<$name> for $crate::Term { - fn from(value: $name) -> Self { - match value { - $( - $name::$variant(value) => value.into(), - )+ - } - } - } - - $( - impl From<$variant> for $name { - fn from(value: $variant) -> Self { - $name::$variant(value) - } - } - - $(#[$field_outer])* - #[derive(Debug, Clone)] - pub struct $variant(pub $crate::Term); - - impl From<$variant> for $crate::Term { - fn from(value: $variant) -> Self { - value.0 - } - } - - impl std::ops::Deref for $variant { - type Target = $crate::Term; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - )+ - }; -} - -macro_rules! soft_vec { - ($($expr:expr),*) => { - $crate::semantic::Expr::List($crate::semantic::List($crate::Term::Vec(vec![$($expr.into()),*]))) - }; -} - -macro_rules! keyword { - ($str:literal) => { - $crate::semantic::Expr::new_keyword($str) - }; -} - -use define_ast; -use define_builtin; -pub(crate) use keyword; -pub(crate) use soft_vec;