Reorganize
parent
056f6d58bf
commit
2ccc3e8ef1
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
79
src/main.rs
79
src/main.rs
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod parse;
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue