diff --git a/src/builtins.rs b/src/builtins.rs index 267349b..82ef8f8 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -1,9 +1,10 @@ -use std::fmt::Write; +//! Language builtins, wired into the implementation. -use thiserror::Error; +use std::fmt::Write; use crate::evaluate::{Dictionary, Value}; +/// Builtin functions. #[derive(Debug, Copy, Clone)] pub enum Builtin { Print, @@ -16,18 +17,10 @@ pub enum Builtin { LessThan, } -#[derive(Debug, Error)] -pub enum Error { - #[error("Tried to {0} a non-integer: {1}")] - NonInteger(&'static str, Value), -} - -type Result = std::result::Result; - -fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> Result { +fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> i64 { move |value| match value { - Value::Integer(i) => Ok(i), - _ => Err(Error::NonInteger(op_name, value)), + Value::Integer(i) => i, + _ => panic!("got non-integer during {op_name}"), } } @@ -37,8 +30,8 @@ macro_rules! int_op { $arguments .into_iter() .map(unwrap_int_value($op_name)) - .reduce(|acc, v| Ok(acc? $op v?)) - .unwrap()?, + .reduce(|acc, v| acc $op v) + .unwrap(), ) }; } @@ -47,13 +40,14 @@ macro_rules! int_cmp { ($arguments:expr, $op:tt) => {{ let mut arguments = $arguments; let unwrap = unwrap_int_value("compare"); - let y = unwrap(arguments.pop().unwrap())?; - let x = unwrap(arguments.pop().unwrap())?; + let y = unwrap(arguments.pop().unwrap()); + let x = unwrap(arguments.pop().unwrap()); Value::Boolean(x $op y) }}; } impl Builtin { + /// A mapping from runtime names to builtin sentinels. pub fn dictionary() -> Dictionary { [ ("_print", Builtin::Print), @@ -69,6 +63,7 @@ impl Builtin { .into() } + /// Minimum # of required arguments for a builtin. pub fn min_parameters(&self) -> usize { match self { Builtin::Print => 0, @@ -81,6 +76,8 @@ impl Builtin { | Builtin::LessThan => 2, } } + + /// Maximum # of parameters allowed, if any. pub fn max_parameters(&self) -> Option { match self { Builtin::Print @@ -93,8 +90,9 @@ impl Builtin { } } - pub fn call(&self, arguments: Vec) -> Result { - let result = match self { + /// Execute the given builtin on the provided arguments. + pub fn call(&self, arguments: Vec) -> Value { + match self { Builtin::Print => { let mut output = String::new(); let mut arguments = arguments.into_iter().peekable(); @@ -126,7 +124,6 @@ impl Builtin { } Builtin::GreaterThan => int_cmp!(arguments, >), Builtin::LessThan => int_cmp!(arguments, <), - }; - Ok(result) + } } } diff --git a/src/cli.rs b/src/cli.rs index f3c5810..9991df4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,12 @@ -use std::{ffi::OsString, path::PathBuf}; +//! Command line interface for the language binary. + +use std::ffi::OsString; +use std::path::PathBuf; use pico_args::Arguments; use thiserror::Error; +/// Usage help text. pub const USAGE: &str = " USAGE: mul [-h|--help] (parse | evaluate | repl) @@ -16,6 +20,7 @@ COMMANDS: repl Start the Read-Evaluate-Print-Loop "; +/// Errors that may occur during command line parsing. #[derive(Debug, Error)] pub enum Error { #[error("Problem while parsing arguments, {0}")] @@ -26,6 +31,7 @@ pub enum Error { UnrecognizedArguments(Vec), } +/// Possible top level commands. pub enum Command { Parse { path: PathBuf }, Run { path: PathBuf }, @@ -33,8 +39,13 @@ pub enum Command { } impl Command { + /// Parse the command line from the current environment. pub fn from_environment() -> Result, Error> { - let mut args = Arguments::from_env(); + Self::from_arguments(Arguments::from_env()) + } + + /// Parse a command from raw arguments. + pub fn from_arguments(mut args: Arguments) -> Result, Error> { if args.contains(["-h", "--help"]) { return Ok(None); } diff --git a/src/evaluate.rs b/src/evaluate.rs index 27ed287..4b1ea8d 100644 --- a/src/evaluate.rs +++ b/src/evaluate.rs @@ -1,12 +1,17 @@ -use std::{collections::HashMap, fmt::Display}; +//! Tree-walk interpreter for syntax. + +use std::collections::HashMap; +use std::fmt::Display; use thiserror::Error; -use crate::builtins::{self, Builtin}; +use crate::builtins::Builtin; use crate::syntax::{Block, Expression, InteractiveEntry, Item, Name, Program}; +/// Mapping of names to some T. pub type Dictionary = HashMap; +/// Runtime values. #[derive(Debug, Clone)] pub enum Value { Builtin(Builtin), @@ -32,6 +37,7 @@ impl Display for Value { } } +/// Errors that may occur during evaluation. #[derive(Debug, Error)] pub enum Error { #[error("The name '{0} was not bound to any value in this context")] @@ -42,28 +48,30 @@ pub enum Error { ArgumentsMismatch(usize, usize), #[error("No function called main to run")] MissingMainFunction, - #[error("Problem with builtin call: {0}")] - BuiltinError(#[from] builtins::Error), } type Result = std::result::Result; +/// AST walking interpreter. #[derive(Debug, Clone)] pub struct Interpreter { environment: Dictionary, } impl Interpreter { + /// Create a fresh interpreter populated with builtins. pub fn new() -> Self { Interpreter { environment: Builtin::dictionary(), } } + /// Create a nested interpreter given an existing environment. fn nested(environment: Dictionary) -> Self { Interpreter { environment } } + /// Run a program. Expects a main function to exist. pub fn run(&mut self, program: &Program) -> Result { for item in program { self.interpret_item(item)?; @@ -75,7 +83,7 @@ impl Interpreter { } } - pub fn interpret_item(&mut self, item: &Item) -> Result { + fn interpret_item(&mut self, item: &Item) -> Result { match item { Item::Fn { name, @@ -93,6 +101,7 @@ impl Interpreter { } } + /// Evaluate a code fragment from an interactive context. pub fn evaluate_interactive_entry(&mut self, entry: &InteractiveEntry) -> Result { match entry { InteractiveEntry::Item(item) => self.interpret_item(item), @@ -103,7 +112,7 @@ impl Interpreter { } } - pub fn evaluate(&mut self, term: &Expression) -> Result { + fn evaluate(&mut self, term: &Expression) -> Result { match term { Expression::Variable(name) => self .environment @@ -158,7 +167,7 @@ impl Interpreter { .iter() .map(|term| self.evaluate(term)) .collect::>>()?; - b.call(arguments).map_err(Error::BuiltinError) + Ok(b.call(arguments)) } _ => Err(Error::NotAFunction), } diff --git a/src/parse.rs b/src/parse.rs index 6152f99..8efe763 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,3 +1,5 @@ +//! Transform text into syntax. + use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError}; use crate::syntax::{InteractiveEntry, Program}; @@ -6,10 +8,12 @@ lalrpop_mod!(parser, "/parse/parser.rs"); type Result<'input, T> = std::result::Result, &'static str>>; +/// Parse a series of items. pub fn parse_program(input: &str) -> Result { parser::ProgramParser::new().parse(input) } +/// Parse a fragment of code. pub fn parse_interactive_entry(input: &str) -> Result { parser::InteractiveEntryParser::new().parse(input) }