~vpzom/four

41c1a735006ba88c0ec82e0c326ffe3b1d74a664 — Colin Reeder 3 years ago 1d176ff
rustfmt
4 files changed, 202 insertions(+), 82 deletions(-)

M src/eval.rs
M src/lib.rs
M src/main.rs
M src/parse.rs
M src/eval.rs => src/eval.rs +160 -54
@@ 1,11 1,11 @@
use {Context, ExprContent, Location, ParsedExpr, Value};
use std;
use {Context, ExprContent, Location, ParsedExpr, Value};

#[derive(Debug)]
pub enum EvalError {
    UnrecognizedOperation(Location, i32),
    WrongType(Location, String),
    InvalidParameters(Location, String)
    InvalidParameters(Location, String),
}

pub fn eval(expr: &ParsedExpr, ctx: &Context) -> Result<Value, EvalError> {


@@ 16,15 16,15 @@ pub fn eval(expr: &ParsedExpr, ctx: &Context) -> Result<Value, EvalError> {
            let op = eval(&args[0], ctx)?;
            if let Value::I32(id) = op {
                exec_op(args[0].pos.clone(), id, &args[1..], ctx)
            }
            else if let Value::Function(f) = op {
            } else if let Value::Function(f) = op {
                exec_fn(&f, &args[1..], ctx)
            }
            else if op == Value::Nil {
            } else if op == Value::Nil {
                exec_get(args[0].pos.clone(), &args[1..], ctx)
            }
            else {
                return Err(EvalError::WrongType(args[0].pos.clone(), format!("Unexpected operation type {:?}", op.get_type())));
            } else {
                return Err(EvalError::WrongType(
                    args[0].pos.clone(),
                    format!("Unexpected operation type {:?}", op.get_type()),
                ));
            }
        }
    }


@@ 32,17 32,31 @@ pub fn eval(expr: &ParsedExpr, ctx: &Context) -> Result<Value, EvalError> {

fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result<Value, EvalError> {
    match id {
        0 => { // function declaration
        0 => {
            // function declaration
            if args.len() != 1 {
                return Err(EvalError::InvalidParameters(pos, format!("function declaration operation requires at exactly 1 parameter, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "function declaration operation requires at exactly 1 parameter, got {}",
                        args.len()
                    ),
                ));
            }

            let body = args[0].clone();
            Ok(Value::Function(Box::new(body)))
        },
        1 => { // multiply
        }
        1 => {
            // multiply
            if args.len() != 2 {
                return Err(EvalError::InvalidParameters(pos, format!("multiply operation requires exactly 2 parameters, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "multiply operation requires exactly 2 parameters, got {}",
                        args.len()
                    ),
                ));
            }

            let a = eval(&args[0], ctx)?;


@@ 52,14 66,33 @@ fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result
                (Value::Nil, b) => b,
                (a, Value::Nil) => a,
                (Value::I32(a), Value::I32(b)) => Value::I32(a * b),
                (Value::String(a), Value::I32(b)) | (Value::I32(b), Value::String(a)) => Value::String(a.repeat(b.max(0) as usize)),
                (Value::String(_), Value::String(_)) => return Err(EvalError::InvalidParameters(pos, format!("multiply operation cannot be applied to two strings"))),
                (Value::Function(_), _) | (_, Value::Function(_)) => return Err(EvalError::InvalidParameters(pos, format!("multiply operation cannot be applied to functions")))
                (Value::String(a), Value::I32(b)) | (Value::I32(b), Value::String(a)) => {
                    Value::String(a.repeat(b.max(0) as usize))
                }
                (Value::String(_), Value::String(_)) => {
                    return Err(EvalError::InvalidParameters(
                        pos,
                        format!("multiply operation cannot be applied to two strings"),
                    ))
                }
                (Value::Function(_), _) | (_, Value::Function(_)) => {
                    return Err(EvalError::InvalidParameters(
                        pos,
                        format!("multiply operation cannot be applied to functions"),
                    ))
                }
            })
        },
        4 => { // add
        }
        4 => {
            // add
            if args.len() != 2 {
                return Err(EvalError::InvalidParameters(pos, format!("add operation requires exactly 2 parameters, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "add operation requires exactly 2 parameters, got {}",
                        args.len()
                    ),
                ));
            }

            let a = eval(&args[0], ctx)?;


@@ 72,12 105,24 @@ fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result
                (Value::String(a), Value::I32(b)) => Value::String(format!("{}{}", a, b)),
                (Value::I32(a), Value::String(b)) => Value::String(format!("{}{}", a, b)),
                (Value::String(a), Value::String(b)) => Value::String(format!("{}{}", a, b)),
                (Value::Function(_), _) | (_, Value::Function(_)) => return Err(EvalError::InvalidParameters(pos, format!("add operation cannot be applied to functions")))
                (Value::Function(_), _) | (_, Value::Function(_)) => {
                    return Err(EvalError::InvalidParameters(
                        pos,
                        format!("add operation cannot be applied to functions"),
                    ))
                }
            })
        },
        8 => { // divide
        }
        8 => {
            // divide
            if args.len() != 2 {
                return Err(EvalError::InvalidParameters(pos, format!("divide operation requires exactly 2 parameters, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "divide operation requires exactly 2 parameters, got {}",
                        args.len()
                    ),
                ));
            }

            let a = eval(&args[0], ctx)?;


@@ 87,13 132,30 @@ fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result
                (Value::Nil, _) => Value::Nil,
                (_, Value::Nil) => Value::Nil,
                (Value::I32(a), Value::I32(b)) => Value::I32(a / b),
                (Value::Function(_), _) | (_, Value::Function(_)) => return Err(EvalError::InvalidParameters(pos, format!("divide operation cannot be applied to functions"))),
                (Value::String(_), _) | (_, Value::String(_)) => return Err(EvalError::InvalidParameters(pos, format!("divide operation cannot be applied to strings")))
                (Value::Function(_), _) | (_, Value::Function(_)) => {
                    return Err(EvalError::InvalidParameters(
                        pos,
                        format!("divide operation cannot be applied to functions"),
                    ))
                }
                (Value::String(_), _) | (_, Value::String(_)) => {
                    return Err(EvalError::InvalidParameters(
                        pos,
                        format!("divide operation cannot be applied to strings"),
                    ))
                }
            })
        },
        9 => { // character from string
        }
        9 => {
            // character from string
            if args.len() != 2 {
                return Err(EvalError::InvalidParameters(pos, format!("character of string operation requires exactly 2 parameters, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "character of string operation requires exactly 2 parameters, got {}",
                        args.len()
                    ),
                ));
            }

            let a = eval(&args[0], ctx)?;


@@ 103,20 165,33 @@ fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result
                (Value::String(a), Value::I32(i)) => {
                    match if i < 0 {
                        a.chars().rev().nth((-i) as usize)
                    }
                    else {
                    } else {
                        a.chars().nth(i as usize)
                    } {
                        Some(c) => Value::String(c.to_string()),
                        None => Value::Nil
                        None => Value::Nil,
                    }
                },
                (a, b) => return Err(EvalError::InvalidParameters(pos, format!("character from string operation requires (String, I32), got ({:?}, {:?})", a.get_type(), b.get_type())))
                }
                (a, b) => return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "character from string operation requires (String, I32), got ({:?}, {:?})",
                        a.get_type(),
                        b.get_type()
                    ),
                )),
            })
        },
        12 => { // conditional
        }
        12 => {
            // conditional
            if args.len() != 3 {
                return Err(EvalError::InvalidParameters(pos, format!("conditional operation requires exactly 3 parameters, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "conditional operation requires exactly 3 parameters, got {}",
                        args.len()
                    ),
                ));
            }

            let cond = eval(&args[0], ctx)?;


@@ 126,10 201,17 @@ fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result
            };

            eval(&args[if cond { 1 } else { 2 }], ctx)
        },
        16 => { // subtract
        }
        16 => {
            // subtract
            if args.len() != 2 {
                return Err(EvalError::InvalidParameters(pos, format!("subtract operation requires exactly 2 parameters, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "subtract operation requires exactly 2 parameters, got {}",
                        args.len()
                    ),
                ));
            }

            let a = eval(&args[0], ctx)?;


@@ 139,13 221,30 @@ fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result
                (Value::Nil, _) => Value::Nil,
                (_, Value::Nil) => Value::Nil,
                (Value::I32(a), Value::I32(b)) => Value::I32(a - b),
                (Value::Function(_), _) | (_, Value::Function(_)) => return Err(EvalError::InvalidParameters(pos, format!("subtract operation cannot be applied to functions"))),
                (Value::String(_), _) | (_, Value::String(_)) => return Err(EvalError::InvalidParameters(pos, format!("subtract operation cannot be applied to strings")))
                (Value::Function(_), _) | (_, Value::Function(_)) => {
                    return Err(EvalError::InvalidParameters(
                        pos,
                        format!("subtract operation cannot be applied to functions"),
                    ))
                }
                (Value::String(_), _) | (_, Value::String(_)) => {
                    return Err(EvalError::InvalidParameters(
                        pos,
                        format!("subtract operation cannot be applied to strings"),
                    ))
                }
            })
        },
        24 => { // char code to string
        }
        24 => {
            // char code to string
            if args.len() != 1 {
                return Err(EvalError::InvalidParameters(pos, format!("char code to string operation requires exactly 1 parameter, got {}", args.len())));
                return Err(EvalError::InvalidParameters(
                    pos,
                    format!(
                        "char code to string operation requires exactly 1 parameter, got {}",
                        args.len()
                    ),
                ));
            }

            let a = eval(&args[0], ctx)?;


@@ 153,14 252,16 @@ fn exec_op(pos: Location, id: i32, args: &[ParsedExpr], ctx: &Context) -> Result
            if let Value::I32(code) = a {
                Ok(match std::char::from_u32(code as u32) {
                    Some(c) => Value::String(c.to_string()),
                    None => Value::Nil
                    None => Value::Nil,
                })
            } else {
                Err(EvalError::InvalidParameters(
                    pos,
                    format!("Only integers can be used as codepoints"),
                ))
            }
            else {
                Err(EvalError::InvalidParameters(pos, format!("Only integers can be used as codepoints")))
            }
        },
        _ => Err(EvalError::UnrecognizedOperation(pos, id))
        }
        _ => Err(EvalError::UnrecognizedOperation(pos, id)),
    }
}



@@ 174,15 275,20 @@ fn exec_fn(f: &ParsedExpr, args: &[ParsedExpr], ctx: &Context) -> Result<Value, 
            new_ctx.set(Value::I32(idx as i32), arg);
        }
        eval(f, &new_ctx)
    }
    else {
    } else {
        eval(f, ctx)
    }
}

fn exec_get(pos: Location, args: &[ParsedExpr], ctx: &Context) -> Result<Value, EvalError> {
    if args.len() != 1 {
        return Err(EvalError::InvalidParameters(pos, format!("get operation requires exactly 1 parameter, got {}", args.len())));
        return Err(EvalError::InvalidParameters(
            pos,
            format!(
                "get operation requires exactly 1 parameter, got {}",
                args.len()
            ),
        ));
    }

    let key = eval(&args[0], ctx)?;

M src/lib.rs => src/lib.rs +15 -19
@@ 1,11 1,11 @@
#[macro_use]
extern crate vec1;

pub mod parse;
pub mod eval;
pub mod parse;

pub use parse::{parse, ParseError};
pub use eval::{eval, EvalError};
pub use parse::{parse, ParseError};

use std::collections::HashMap;



@@ 16,13 16,13 @@ pub struct Location(pub usize);
pub enum ExprContent {
    Four,
    Call(vec1::Vec1<ParsedExpr>),
    Nil
    Nil,
}

#[derive(Clone, Hash, Eq, PartialEq, Debug)]
pub struct ParsedExpr {
    content: ExprContent,
    pos: Location
    pos: Location,
}

impl std::fmt::Debug for ExprContent {


@@ 35,7 35,7 @@ impl std::fmt::Debug for ExprContent {
                    write!(f, "{:?}", a)?;
                }
                write!(f, ")")?;
            },
            }
            ExprContent::Nil => {
                write!(f, "()")?;
            }


@@ 46,10 46,7 @@ impl std::fmt::Debug for ExprContent {

impl ParsedExpr {
    pub(crate) fn new(loc: Location, content: ExprContent) -> Self {
        Self {
            pos: loc,
            content
        }
        Self { pos: loc, content }
    }
}



@@ 58,7 55,7 @@ pub enum Value {
    I32(i32),
    Nil,
    Function(Box<ParsedExpr>),
    String(String)
    String(String),
}

impl std::fmt::Display for Value {


@@ 67,7 64,7 @@ impl std::fmt::Display for Value {
            Value::I32(v) => write!(f, "{}", v),
            Value::Nil => write!(f, "Nil"),
            Value::Function(expr) => write!(f, "function: {:?}", expr.content),
            Value::String(v) => write!(f, "{}", v)
            Value::String(v) => write!(f, "{}", v),
        }
    }
}


@@ 77,7 74,7 @@ pub enum ValueType {
    I32,
    Nil,
    Function,
    String
    String,
}

impl Value {


@@ 86,7 83,7 @@ impl Value {
            Value::I32(_) => ValueType::I32,
            Value::Nil => ValueType::Nil,
            Value::Function(_) => ValueType::Function,
            Value::String(_) => ValueType::String
            Value::String(_) => ValueType::String,
        }
    }
}


@@ 94,7 91,7 @@ impl Value {
#[derive(Default)]
pub struct Context<'a> {
    parent: Option<&'a Context<'a>>,
    fields: HashMap<Value, Value>
    fields: HashMap<Value, Value>,
}

impl<'a> Context<'a> {


@@ 110,10 107,9 @@ impl<'a> Context<'a> {
    }

    pub fn get(&self, key: &Value) -> Option<&Value> {
        self.fields.get(key)
            .or_else(|| match self.parent {
                Some(parent) => parent.get(key),
                None => None
            })
        self.fields.get(key).or_else(|| match self.parent {
            Some(parent) => parent.get(key),
            None => None,
        })
    }
}

M src/main.rs => src/main.rs +8 -1
@@ 5,5 5,12 @@ fn main() {
    for (index, arg) in std::env::args().skip(1).enumerate() {
        ctx.set(four::Value::I32(index as i32), four::Value::String(arg));
    }
    println!("{}", four::eval(&four::parse(std::io::stdin()).expect("Failed to parse"), &ctx).expect("Runtime failure"));
    println!(
        "{}",
        four::eval(
            &four::parse(std::io::stdin()).expect("Failed to parse"),
            &ctx
        )
        .expect("Runtime failure")
    );
}

M src/parse.rs => src/parse.rs +19 -8
@@ 6,7 6,7 @@ use {ExprContent, Location, ParsedExpr};
pub enum ParseError {
    TrailingCharacters(u8),
    UnexpectedEof,
    IOError(std::io::Error)
    IOError(std::io::Error),
}

impl From<std::io::Error> for ParseError {


@@ 22,17 22,21 @@ pub fn parse(mut src: impl std::io::Read) -> Result<ParsedExpr, ParseError> {
    match next {
        Next::EndFile => Err(ParseError::UnexpectedEof),
        Next::EndParen => Err(ParseError::TrailingCharacters(b')')),
        Next::Expr(expr) => Ok(expr)
        Next::Expr(expr) => Ok(expr),
    }
}

enum Next {
    Expr(ParsedExpr),
    EndParen,
    EndFile
    EndFile,
}

fn parse_next<R: std::io::Read>(pos: &mut usize, src: &mut R, buf: &mut [u8; 1]) -> Result<Next, ParseError> {
fn parse_next<R: std::io::Read>(
    pos: &mut usize,
    src: &mut R,
    buf: &mut [u8; 1],
) -> Result<Next, ParseError> {
    loop {
        let ret = src.read(buf)?;
        if ret < 1 {


@@ 40,7 44,10 @@ fn parse_next<R: std::io::Read>(pos: &mut usize, src: &mut R, buf: &mut [u8; 1])
        }
        *pos += 1;
        if buf[0] == b'4' {
            return Ok(Next::Expr(ParsedExpr::new(Location(*pos), ExprContent::Four)));
            return Ok(Next::Expr(ParsedExpr::new(
                Location(*pos),
                ExprContent::Four,
            )));
        }
        if buf[0] == b')' {
            return Ok(Next::EndParen);


@@ 51,18 58,22 @@ fn parse_next<R: std::io::Read>(pos: &mut usize, src: &mut R, buf: &mut [u8; 1])
    }
}

fn parse_call<R: std::io::Read>(pos: &mut usize, src: &mut R, buf: &mut [u8; 1]) -> Result<ParsedExpr, ParseError> {
fn parse_call<R: std::io::Read>(
    pos: &mut usize,
    src: &mut R,
    buf: &mut [u8; 1],
) -> Result<ParsedExpr, ParseError> {
    let mut args = match parse_next(pos, src, buf)? {
        Next::EndFile => return Err(ParseError::UnexpectedEof),
        Next::EndParen => return Ok(ParsedExpr::new(Location(*pos), ExprContent::Nil)),
        Next::Expr(expr) => vec1![expr]
        Next::Expr(expr) => vec1![expr],
    };

    loop {
        match parse_next(pos, src, buf)? {
            Next::EndFile => return Err(ParseError::UnexpectedEof),
            Next::EndParen => return Ok(ParsedExpr::new(Location(*pos), ExprContent::Call(args))),
            Next::Expr(expr) => args.push(expr)
            Next::Expr(expr) => args.push(expr),
        }
    }
}