Add more documenation, minor tweaks

main
mat ess 2023-07-24 23:46:12 -04:00
parent bcee1cf160
commit bce8a006b0
4 changed files with 51 additions and 30 deletions

View File

@ -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<T> = std::result::Result<T, Error>;
fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> Result<i64> {
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<Value> {
[
("_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<usize> {
match self {
Builtin::Print
@ -93,8 +90,9 @@ impl Builtin {
}
}
pub fn call(&self, arguments: Vec<Value>) -> Result<Value> {
let result = match self {
/// Execute the given builtin on the provided arguments.
pub fn call(&self, arguments: Vec<Value>) -> 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)
}
}
}

View File

@ -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 <FILE> | evaluate <FILE> | 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<OsString>),
}
/// 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<Option<Self>, 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<Option<Self>, Error> {
if args.contains(["-h", "--help"]) {
return Ok(None);
}

View File

@ -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<T> = HashMap<Name, T>;
/// 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<T> = std::result::Result<T, Error>;
/// AST walking interpreter.
#[derive(Debug, Clone)]
pub struct Interpreter {
environment: Dictionary<Value>,
}
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<Value>) -> Self {
Interpreter { environment }
}
/// Run a program. Expects a main function to exist.
pub fn run(&mut self, program: &Program) -> Result<Value> {
for item in program {
self.interpret_item(item)?;
@ -75,7 +83,7 @@ impl Interpreter {
}
}
pub fn interpret_item(&mut self, item: &Item) -> Result<Value> {
fn interpret_item(&mut self, item: &Item) -> Result<Value> {
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<Value> {
match entry {
InteractiveEntry::Item(item) => self.interpret_item(item),
@ -103,7 +112,7 @@ impl Interpreter {
}
}
pub fn evaluate(&mut self, term: &Expression) -> Result<Value> {
fn evaluate(&mut self, term: &Expression) -> Result<Value> {
match term {
Expression::Variable(name) => self
.environment
@ -158,7 +167,7 @@ impl Interpreter {
.iter()
.map(|term| self.evaluate(term))
.collect::<Result<Vec<_>>>()?;
b.call(arguments).map_err(Error::BuiltinError)
Ok(b.call(arguments))
}
_ => Err(Error::NotAFunction),
}

View File

@ -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<T, ParseError<usize, Token<'input>, &'static str>>;
/// Parse a series of items.
pub fn parse_program(input: &str) -> Result<Program> {
parser::ProgramParser::new().parse(input)
}
/// Parse a fragment of code.
pub fn parse_interactive_entry(input: &str) -> Result<InteractiveEntry> {
parser::InteractiveEntryParser::new().parse(input)
}