Reorganize

main
mat ess 2023-07-02 21:37:12 -04:00
parent 056f6d58bf
commit 2ccc3e8ef1
9 changed files with 206 additions and 248 deletions

45
src/cli.rs Normal file
View File

@ -0,0 +1,45 @@
use std::path::PathBuf;
use pico_args::Arguments;
#[derive(Debug)]
pub enum Error {
ArgsError(pico_args::Error),
InvalidCommand(String),
PrintUsage,
}
impl From<pico_args::Error> for Error {
fn from(value: pico_args::Error) -> Self {
Error::ArgsError(value)
}
}
pub enum Command {
Parse { source: PathBuf },
Run { source: PathBuf },
Repl,
}
impl Command {
pub fn from_environment() -> Result<Self, Error> {
let mut args = Arguments::from_env();
if args.contains(["-h", "--help"]) {
return Err(Error::PrintUsage);
}
let cmd = match args.subcommand()?.as_deref() {
Some("parse") => {
let source = args.free_from_str()?;
Command::Parse { source }
}
Some("run") => {
let source = args.free_from_str()?;
Command::Run { source }
}
Some("repl") => Command::Repl,
Some(cmd) => return Err(Error::InvalidCommand(cmd.to_string())),
None => return Err(Error::PrintUsage),
};
Ok(cmd)
}
}

View File

@ -1,150 +0,0 @@
use std::collections::HashMap;
use crate::syntax::{Expression, Name, Program, Statement};
pub type Environment = HashMap<Name, Value>;
#[derive(Debug, Clone)]
pub enum Value {
Boolean(bool),
Integer(i64),
Closure(Vec<Name>, Expression, Environment),
}
pub struct Interpreter {
environment: Environment,
}
#[derive(Debug)]
pub enum InterpreterError {
VariableNotFound(Name),
IncorrectArgumentCount(usize, usize),
NonFunctionCall(Value),
EmptyProgram,
ExecutionError(String),
}
impl From<String> for InterpreterError {
fn from(s: String) -> Self {
InterpreterError::ExecutionError(s)
}
}
impl std::fmt::Display for InterpreterError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
InterpreterError::VariableNotFound(name) => write!(f, "Variable '{}' not found", name),
InterpreterError::IncorrectArgumentCount(expected, actual) => {
write!(f, "Expected {} arguments, but got {}", expected, actual)
}
InterpreterError::NonFunctionCall(value) => {
write!(f, "Function call on non-function value: {:?}", value)
}
InterpreterError::EmptyProgram => write!(f, "Program is empty"),
InterpreterError::ExecutionError(s) => write!(f, "Execution error: {}", s),
}
}
}
type Result<T> = std::result::Result<T, InterpreterError>;
impl Interpreter {
pub fn new() -> Self {
Self {
environment: Environment::new(),
}
}
pub fn interpret_statement(&mut self, statement: &Statement) -> Result<Value> {
match statement {
Statement::Binding(name, expression) => {
let value = self.evaluate(expression)?;
self.environment.insert(name.clone(), value.clone());
Ok(value)
}
Statement::Expression(expression) => self.evaluate(expression),
}
}
pub fn interpret(&mut self, program: &Program) -> Result<Value> {
let mut result = None;
for statement in program {
result = Some(self.interpret_statement(statement)?);
}
match result {
Some(value) => Ok(value),
None => Err(InterpreterError::EmptyProgram),
}
}
fn evaluate(&mut self, expression: &Expression) -> Result<Value> {
match expression {
Expression::Variable(name) => self
.environment
.get(name)
.cloned()
.ok_or_else(|| InterpreterError::VariableNotFound(name.clone())),
Expression::Boolean(value) => Ok(Value::Boolean(*value)),
Expression::Integer(value) => Ok(Value::Integer(*value)),
Expression::Lambda(params, body) => Ok(Value::Closure(
params.clone(),
*body.clone(),
self.environment.clone(),
)),
Expression::Call(function, args) => {
let function_value = self.evaluate(function)?;
match function_value {
Value::Closure(params, body, mut environment) => {
if params.len() != args.len() {
return Err(InterpreterError::IncorrectArgumentCount(
params.len(),
args.len(),
));
}
let mut arg_values = Vec::new();
for arg in args {
arg_values.push(self.evaluate(arg)?);
}
// Bind argument values to parameter names in a new environment
for (param, arg_value) in params.iter().zip(arg_values.into_iter()) {
environment.insert(param.clone(), arg_value);
}
// Evaluate the body expression in the new environment
let mut interpreter = Interpreter { environment };
interpreter.evaluate(&body)
}
_ => Err(InterpreterError::NonFunctionCall(function_value)),
}
}
Expression::Block(statements, expression) => {
let new_environment = self.environment.clone();
for statement in statements {
self.interpret_statement(statement)?;
}
let mut interpreter = Interpreter {
environment: new_environment,
};
interpreter.evaluate(expression)
}
}
}
}
impl Default for Interpreter {
fn default() -> Self {
Self::new()
}
}

View File

@ -1,5 +1,3 @@
use lalrpop_util::lalrpop_mod; pub mod cli;
pub mod phase;
pub mod interpreter;
lalrpop_mod!(pub parser);
pub mod syntax; pub mod syntax;

View File

@ -1,17 +1,46 @@
use std::env::args;
use std::fs::read_to_string; use std::fs::read_to_string;
use std::io::{stdin, stdout, BufRead, Write}; use std::io::{stdin, stdout, BufRead, Write};
use std::path::Path; use std::path::Path;
use std::process::exit; use std::process::exit;
use mul::interpreter::Interpreter; use mul::cli::{Command, Error};
use mul::parser::{ProgramParser, StatementParser}; use mul::phase::parse::{parse_program, parse_repl_line};
fn main() -> Result<(), Error> {
let command = Command::from_environment()?;
match command {
Command::Parse { source } => parse(source.as_path()),
Command::Run { source } => run(source.as_path()),
Command::Repl => repl(),
}
Ok(())
}
fn parse(file: &Path) {
let content = read_to_string(file).expect("Failed to read file");
let program = parse_program(content.as_str()).unwrap_or_else(|e| {
eprintln!("{e}");
exit(1);
});
println!("{program:#?}");
}
fn run(file: &Path) {
let content = read_to_string(file).expect("Failed to read file");
let program = parse_program(content.as_str()).unwrap_or_else(|e| {
eprintln!("{e}");
exit(1);
});
// let v = interpreter.interpret(&program).unwrap_or_else(|e| {
// eprintln!("{e}");
// exit(1);
// });
// println!("{v:?}")
}
fn repl() { fn repl() {
let mut stdin = stdin().lock(); let mut stdin = stdin().lock();
let mut stdout = stdout().lock(); let mut stdout = stdout().lock();
let parser = StatementParser::new();
let mut intepreter = Interpreter::new();
loop { loop {
let mut line = String::new(); let mut line = String::new();
print!("> "); print!("> ");
@ -26,7 +55,7 @@ fn repl() {
if !line.trim_end().ends_with(';') { if !line.trim_end().ends_with(';') {
line.push(';'); line.push(';');
} }
let parsed = match parser.parse(&line) { let parsed = match parse_repl_line(&line) {
Ok(parsed) => parsed, Ok(parsed) => parsed,
Err(lalrpop_util::ParseError::UnrecognizedEOF { .. }) => break, Err(lalrpop_util::ParseError::UnrecognizedEOF { .. }) => break,
Err(e) => { Err(e) => {
@ -34,35 +63,13 @@ fn repl() {
continue; continue;
} }
}; };
match intepreter.interpret_statement(&parsed) { println!("{parsed:?}");
Ok(value) => println!("{value:?}"), // match intepreter.interpret_statement(&parsed) {
Err(e) => { // Ok(value) => println!("{value:?}"),
eprintln!("{e}"); // Err(e) => {
continue; // eprintln!("{e}");
} // continue;
}; // }
} // };
}
fn build(file: &Path) {
let content = read_to_string(file).expect("Failed to read file");
let parser = ProgramParser::new();
let mut interpreter = Interpreter::new();
let program = parser.parse(content.as_str()).unwrap_or_else(|e| {
eprintln!("{e}");
exit(1);
});
let v = interpreter.interpret(&program).unwrap_or_else(|e| {
eprintln!("{e}");
exit(1);
});
println!("{v:?}")
}
fn main() {
if let Some(file) = args().nth(1) {
build(file.as_ref())
} else {
repl()
} }
} }

View File

@ -1,52 +0,0 @@
use crate::syntax::{Expression, Name, Program, Statement};
grammar;
pub Program: Program = Statement*;
pub Statement: Statement = {
"let" <Name> "=" <Expression> ";" => Statement::Binding(<>),
<Block> ";"? => Statement::Expression(<>),
<Simple> ";" => Statement::Expression(<>),
};
pub Expression = {
Simple,
Block,
};
Simple: Expression = {
"|" <parameters:Comma<Name>> "|" <body:Expression> => Expression::Lambda(parameters, Box::new(body)),
Atom,
};
Atom: Expression = {
Name => Expression::Variable(<>),
<callee:Atom> "(" <arguments:Comma<Expression>> ")" => Expression::Call(Box::new(callee), arguments),
Integer => Expression::Integer(<>),
Boolean => Expression::Boolean(<>),
"(" <Expression> ")",
};
Block: Expression =
"{" <statements:Statement*> <result:Expression> "}" => Expression::Block(statements, Box::new(result));
// TODO: decide on identifier syntax
Name: Name = r"[a-zA-Z_]+" => <>.to_string();
Integer: i64 = r"[0-9]+" => <>.parse::<i64>().unwrap();
Boolean: bool = {
"true" => true,
"false" => false,
}
Comma<T>: Vec<T> = {
<mut v:(<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
};

1
src/phase.rs Normal file
View File

@ -0,0 +1 @@
pub mod parse;

15
src/phase/parse.rs Normal file
View File

@ -0,0 +1,15 @@
use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError};
use crate::syntax::{self, Line};
lalrpop_mod!(parser, "/phase/parse/parser.rs");
pub type Result<'input, T> = std::result::Result<T, ParseError<usize, Token<'input>, &'static str>>;
pub fn parse_program(input: &str) -> Result<syntax::Program> {
parser::ProgramParser::new().parse(input)
}
pub fn parse_repl_line(input: &str) -> Result<Line> {
parser::LineParser::new().parse(input)
}

View File

@ -0,0 +1,69 @@
use crate::syntax::{
Binding,
Block,
Expression,
Item,
Line,
Name,
Program,
};
grammar;
pub Program: Program = Item*;
pub Line: Line = {
Item => Line::Item(<>),
Binding => Line::Binding(<>),
<Expression> ";"? => Line::Expression(<>),
}
Item: Item = {
"fn" <name:Name> "(" <parameters:Comma<Name>> ")" <body:Block> ";"? => Item::Fn { <> },
}
Binding: Binding =
"let" <Name> "=" <Expression> ";" => Binding(<>);
Expression: Expression = {
Simple,
Boxed<Block> => Expression::Block(<>),
};
Simple: Expression = {
"|" <parameters:Comma<Name>> "|" <result:Boxed<Expression>> => Expression::Lambda { <> },
Atom,
};
Atom: Expression = {
Name => Expression::Variable(<>),
<callee:Boxed<Atom>> "(" <arguments:Comma<Expression>> ")" => Expression::Call { <> },
Integer => Expression::Integer(<>),
Boolean => Expression::Boolean(<>),
"(" <Expression> ")",
};
Block: Block =
"{" <bindings:Binding*> <result:Expression> "}" => Block { <> };
// TODO: decide on identifier syntax
Name: Name = r"[a-zA-Z_]+" => <>.to_string();
Integer: i64 = r"[0-9]+" => <>.parse::<i64>().unwrap();
Boolean: bool = {
"true" => true,
"false" => false,
}
Boxed<T>: Box<T> = T => Box::new(<>);
Comma<T>: Vec<T> = {
<mut v:(<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
};

View File

@ -1,19 +1,44 @@
pub type Name = String; pub type Name = String;
pub type Program = Vec<Statement>; pub type Program = Vec<Item>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Statement { pub enum Line {
Binding(Name, Expression), Item(Item),
Binding(Binding),
Expression(Expression), Expression(Expression),
} }
#[derive(Debug, Clone)]
pub enum Item {
Fn {
name: Name,
parameters: Vec<Name>,
body: Block,
},
}
#[derive(Debug, Clone)]
pub struct Binding(pub Name, pub Expression);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Expression { pub enum Expression {
Variable(Name), Variable(Name),
Block(Vec<Statement>, Box<Expression>), Block(Box<Block>),
Lambda(Vec<Name>, Box<Expression>), Lambda {
Call(Box<Expression>, Vec<Expression>), parameters: Vec<Name>,
result: Box<Expression>,
},
Call {
callee: Box<Expression>,
arguments: Vec<Expression>,
},
Boolean(bool), Boolean(bool),
Integer(i64), Integer(i64),
} }
#[derive(Debug, Clone)]
pub struct Block {
pub bindings: Vec<Binding>,
pub result: Expression,
}