about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast/mod.rs166
-rw-r--r--src/codegen/llvm.rs282
-rw-r--r--src/codegen/mod.rs26
-rw-r--r--src/commands/compile.rs28
-rw-r--r--src/commands/eval.rs30
-rw-r--r--src/commands/mod.rs5
-rw-r--r--src/common/env.rs40
-rw-r--r--src/common/error.rs50
-rw-r--r--src/common/mod.rs4
-rw-r--r--src/compiler.rs84
-rw-r--r--src/interpreter/error.rs16
-rw-r--r--src/interpreter/mod.rs125
-rw-r--r--src/interpreter/value.rs134
-rw-r--r--src/main.rs31
-rw-r--r--src/parser/mod.rs445
15 files changed, 1466 insertions, 0 deletions
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
new file mode 100644
index 000000000000..2dcf955fe67c
--- /dev/null
+++ b/src/ast/mod.rs
@@ -0,0 +1,166 @@
+use std::borrow::Cow;
+use std::convert::TryFrom;
+use std::fmt::{self, Display, Formatter};
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct InvalidIdentifier<'a>(Cow<'a, str>);
+
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub struct Ident<'a>(pub Cow<'a, str>);
+
+impl<'a> From<&'a Ident<'a>> for &'a str {
+    fn from(id: &'a Ident<'a>) -> Self {
+        id.0.as_ref()
+    }
+}
+
+impl<'a> Display for Ident<'a> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+impl<'a> Ident<'a> {
+    pub fn to_owned(&self) -> Ident<'static> {
+        Ident(Cow::Owned(self.0.clone().into_owned()))
+    }
+
+    pub fn from_str_unchecked(s: &'a str) -> Self {
+        debug_assert!(is_valid_identifier(s));
+        Self(Cow::Borrowed(s))
+    }
+
+    pub fn from_string_unchecked(s: String) -> Self {
+        debug_assert!(is_valid_identifier(&s));
+        Self(Cow::Owned(s))
+    }
+}
+
+pub fn is_valid_identifier<S>(s: &S) -> bool
+where
+    S: AsRef<str> + ?Sized,
+{
+    s.as_ref()
+        .chars()
+        .any(|c| !c.is_alphanumeric() || !"_".contains(c))
+}
+
+impl<'a> TryFrom<&'a str> for Ident<'a> {
+    type Error = InvalidIdentifier<'a>;
+
+    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
+        if is_valid_identifier(s) {
+            Ok(Ident(Cow::Borrowed(s)))
+        } else {
+            Err(InvalidIdentifier(Cow::Borrowed(s)))
+        }
+    }
+}
+
+impl<'a> TryFrom<String> for Ident<'a> {
+    type Error = InvalidIdentifier<'static>;
+
+    fn try_from(s: String) -> Result<Self, Self::Error> {
+        if is_valid_identifier(&s) {
+            Ok(Ident(Cow::Owned(s)))
+        } else {
+            Err(InvalidIdentifier(Cow::Owned(s)))
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum BinaryOperator {
+    /// `+`
+    Add,
+
+    /// `-`
+    Sub,
+
+    /// `*`
+    Mul,
+
+    /// `/`
+    Div,
+
+    /// `^`
+    Pow,
+
+    /// `==`
+    Equ,
+
+    /// `!=`
+    Neq,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum UnaryOperator {
+    /// !
+    Not,
+
+    /// -
+    Neg,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Literal {
+    Int(u64),
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Expr<'a> {
+    Ident(Ident<'a>),
+
+    Literal(Literal),
+
+    UnaryOp {
+        op: UnaryOperator,
+        rhs: Box<Expr<'a>>,
+    },
+
+    BinaryOp {
+        lhs: Box<Expr<'a>>,
+        op: BinaryOperator,
+        rhs: Box<Expr<'a>>,
+    },
+
+    Let {
+        bindings: Vec<(Ident<'a>, Expr<'a>)>,
+        body: Box<Expr<'a>>,
+    },
+
+    If {
+        condition: Box<Expr<'a>>,
+        then: Box<Expr<'a>>,
+        else_: Box<Expr<'a>>,
+    },
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Fun<'a> {
+    pub name: Ident<'a>,
+    pub args: Vec<Ident<'a>>,
+    pub body: Expr<'a>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Decl<'a> {
+    Fun(Fun<'a>),
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Type {
+    Int,
+    Float,
+    Bool,
+}
+
+impl Display for Type {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Int => f.write_str("int"),
+            Self::Float => f.write_str("float"),
+            Self::Bool => f.write_str("bool"),
+        }
+    }
+}
diff --git a/src/codegen/llvm.rs b/src/codegen/llvm.rs
new file mode 100644
index 000000000000..ff92c3373176
--- /dev/null
+++ b/src/codegen/llvm.rs
@@ -0,0 +1,282 @@
+use std::path::Path;
+use std::result;
+
+use inkwell::basic_block::BasicBlock;
+use inkwell::builder::Builder;
+pub use inkwell::context::Context;
+use inkwell::module::Module;
+use inkwell::support::LLVMString;
+use inkwell::types::FunctionType;
+use inkwell::values::{BasicValueEnum, FunctionValue};
+use inkwell::IntPredicate;
+use thiserror::Error;
+
+use crate::ast::{BinaryOperator, Decl, Expr, Fun, Ident, Literal, UnaryOperator};
+use crate::common::env::Env;
+
+#[derive(Debug, PartialEq, Eq, Error)]
+pub enum Error {
+    #[error("Undefined variable {0}")]
+    UndefinedVariable(Ident<'static>),
+
+    #[error("LLVM Error: {0}")]
+    LLVMError(String),
+}
+
+impl From<LLVMString> for Error {
+    fn from(s: LLVMString) -> Self {
+        Self::LLVMError(s.to_string())
+    }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+pub struct Codegen<'ctx, 'ast> {
+    context: &'ctx Context,
+    pub module: Module<'ctx>,
+    builder: Builder<'ctx>,
+    env: Env<'ast, BasicValueEnum<'ctx>>,
+    function: Option<FunctionValue<'ctx>>,
+}
+
+impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
+    pub fn new(context: &'ctx Context, module_name: &str) -> Self {
+        let module = context.create_module(module_name);
+        let builder = context.create_builder();
+        Self {
+            context,
+            module,
+            builder,
+            env: Default::default(),
+            function: None,
+        }
+    }
+
+    pub fn new_function<'a>(
+        &'a mut self,
+        name: &str,
+        ty: FunctionType<'ctx>,
+    ) -> &'a FunctionValue<'ctx> {
+        self.function = Some(self.module.add_function(name, ty, None));
+        let basic_block = self.append_basic_block("entry");
+        self.builder.position_at_end(basic_block);
+        self.function.as_ref().unwrap()
+    }
+
+    pub fn finish_function(&self, res: &BasicValueEnum<'ctx>) {
+        self.builder.build_return(Some(res));
+    }
+
+    pub fn append_basic_block(&self, name: &str) -> BasicBlock<'ctx> {
+        self.context
+            .append_basic_block(self.function.unwrap(), name)
+    }
+
+    pub fn codegen_expr(&mut self, expr: &'ast Expr<'ast>) -> Result<BasicValueEnum<'ctx>> {
+        match expr {
+            Expr::Ident(id) => self
+                .env
+                .resolve(id)
+                .cloned()
+                .ok_or_else(|| Error::UndefinedVariable(id.to_owned())),
+            Expr::Literal(Literal::Int(i)) => {
+                let ty = self.context.i64_type();
+                Ok(BasicValueEnum::IntValue(ty.const_int(*i, false)))
+            }
+            Expr::UnaryOp { op, rhs } => {
+                let rhs = self.codegen_expr(rhs)?;
+                match op {
+                    UnaryOperator::Not => unimplemented!(),
+                    UnaryOperator::Neg => Ok(BasicValueEnum::IntValue(
+                        self.builder.build_int_neg(rhs.into_int_value(), "neg"),
+                    )),
+                }
+            }
+            Expr::BinaryOp { lhs, op, rhs } => {
+                let lhs = self.codegen_expr(lhs)?;
+                let rhs = self.codegen_expr(rhs)?;
+                match op {
+                    BinaryOperator::Add => {
+                        Ok(BasicValueEnum::IntValue(self.builder.build_int_add(
+                            lhs.into_int_value(),
+                            rhs.into_int_value(),
+                            "add",
+                        )))
+                    }
+                    BinaryOperator::Sub => {
+                        Ok(BasicValueEnum::IntValue(self.builder.build_int_sub(
+                            lhs.into_int_value(),
+                            rhs.into_int_value(),
+                            "add",
+                        )))
+                    }
+                    BinaryOperator::Mul => {
+                        Ok(BasicValueEnum::IntValue(self.builder.build_int_sub(
+                            lhs.into_int_value(),
+                            rhs.into_int_value(),
+                            "add",
+                        )))
+                    }
+                    BinaryOperator::Div => {
+                        Ok(BasicValueEnum::IntValue(self.builder.build_int_signed_div(
+                            lhs.into_int_value(),
+                            rhs.into_int_value(),
+                            "add",
+                        )))
+                    }
+                    BinaryOperator::Pow => unimplemented!(),
+                    BinaryOperator::Equ => {
+                        Ok(BasicValueEnum::IntValue(self.builder.build_int_compare(
+                            IntPredicate::EQ,
+                            lhs.into_int_value(),
+                            rhs.into_int_value(),
+                            "eq",
+                        )))
+                    }
+                    BinaryOperator::Neq => todo!(),
+                }
+            }
+            Expr::Let { bindings, body } => {
+                self.env.push();
+                for (id, val) in bindings {
+                    let val = self.codegen_expr(val)?;
+                    self.env.set(id, val);
+                }
+                let res = self.codegen_expr(body);
+                self.env.pop();
+                res
+            }
+            Expr::If {
+                condition,
+                then,
+                else_,
+            } => {
+                let then_block = self.append_basic_block("then");
+                let else_block = self.append_basic_block("else");
+                let join_block = self.append_basic_block("join");
+                let condition = self.codegen_expr(condition)?;
+                self.builder.build_conditional_branch(
+                    condition.into_int_value(),
+                    then_block,
+                    else_block,
+                );
+                self.builder.position_at_end(then_block);
+                let then_res = self.codegen_expr(then)?;
+                self.builder.build_unconditional_branch(join_block);
+
+                self.builder.position_at_end(else_block);
+                let else_res = self.codegen_expr(else_)?;
+                self.builder.build_unconditional_branch(join_block);
+
+                self.builder.position_at_end(join_block);
+                let phi = self.builder.build_phi(self.context.i64_type(), "join");
+                phi.add_incoming(&[(&then_res, then_block), (&else_res, else_block)]);
+                Ok(phi.as_basic_value())
+            }
+        }
+    }
+
+    pub fn codegen_decl(&mut self, decl: &'ast Decl<'ast>) -> Result<()> {
+        match decl {
+            Decl::Fun(Fun { name, args, body }) => {
+                let i64_type = self.context.i64_type();
+                self.new_function(
+                    name.into(),
+                    i64_type.fn_type(
+                        args.iter()
+                            .map(|_| i64_type.into())
+                            .collect::<Vec<_>>()
+                            .as_slice(),
+                        false,
+                    ),
+                );
+                self.env.push();
+                for (i, arg) in args.iter().enumerate() {
+                    self.env
+                        .set(arg, self.function.unwrap().get_nth_param(i as u32).unwrap());
+                }
+                let res = self.codegen_expr(body)?;
+                self.env.pop();
+                self.finish_function(&res);
+                Ok(())
+            }
+        }
+    }
+
+    pub fn codegen_main(&mut self, expr: &'ast Expr<'ast>) -> Result<()> {
+        self.new_function("main", self.context.i64_type().fn_type(&[], false));
+        let res = self.codegen_expr(expr)?;
+        self.finish_function(&res);
+        Ok(())
+    }
+
+    pub fn print_to_file<P>(&self, path: P) -> Result<()>
+    where
+        P: AsRef<Path>,
+    {
+        Ok(self.module.print_to_file(path)?)
+    }
+
+    pub fn binary_to_file<P>(&self, path: P) -> Result<()>
+    where
+        P: AsRef<Path>,
+    {
+        if self.module.write_bitcode_to_path(path.as_ref()) {
+            Ok(())
+        } else {
+            Err(Error::LLVMError(
+                "Error writing bitcode to output path".to_owned(),
+            ))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use inkwell::execution_engine::JitFunction;
+    use inkwell::OptimizationLevel;
+
+    use super::*;
+
+    fn jit_eval<T>(expr: &str) -> anyhow::Result<T> {
+        let expr = crate::parser::expr(expr).unwrap().1;
+
+        let context = Context::create();
+        let mut codegen = Codegen::new(&context, "test");
+        let execution_engine = codegen
+            .module
+            .create_jit_execution_engine(OptimizationLevel::None)
+            .unwrap();
+
+        codegen.new_function("test", context.i64_type().fn_type(&[], false));
+        let res = codegen.codegen_expr(&expr)?;
+        codegen.finish_function(&res);
+
+        unsafe {
+            let fun: JitFunction<unsafe extern "C" fn() -> T> =
+                execution_engine.get_function("test")?;
+            Ok(fun.call())
+        }
+    }
+
+    #[test]
+    fn add_literals() {
+        assert_eq!(jit_eval::<i64>("1 + 2").unwrap(), 3);
+    }
+
+    #[test]
+    fn variable_shadowing() {
+        assert_eq!(
+            jit_eval::<i64>("let x = 1 in (let x = 2 in x) + x").unwrap(),
+            3
+        );
+    }
+
+    #[test]
+    fn eq() {
+        assert_eq!(
+            jit_eval::<i64>("let x = 1 in if x == 1 then 2 else 4").unwrap(),
+            2
+        );
+    }
+}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
new file mode 100644
index 000000000000..4620b6b48e84
--- /dev/null
+++ b/src/codegen/mod.rs
@@ -0,0 +1,26 @@
+pub mod llvm;
+
+use inkwell::execution_engine::JitFunction;
+use inkwell::OptimizationLevel;
+pub use llvm::*;
+
+use crate::ast::Expr;
+use crate::common::Result;
+
+pub fn jit_eval<T>(expr: &Expr) -> Result<T> {
+    let context = Context::create();
+    let mut codegen = Codegen::new(&context, "eval");
+    let execution_engine = codegen
+        .module
+        .create_jit_execution_engine(OptimizationLevel::None)
+        .map_err(Error::from)?;
+    codegen.new_function("eval", context.i64_type().fn_type(&[], false));
+    let res = codegen.codegen_expr(&expr)?;
+    codegen.finish_function(&res);
+
+    unsafe {
+        let fun: JitFunction<unsafe extern "C" fn() -> T> =
+            execution_engine.get_function("eval").unwrap();
+        Ok(fun.call())
+    }
+}
diff --git a/src/commands/compile.rs b/src/commands/compile.rs
new file mode 100644
index 000000000000..e16b8c87a659
--- /dev/null
+++ b/src/commands/compile.rs
@@ -0,0 +1,28 @@
+use std::path::PathBuf;
+
+use clap::Clap;
+
+use crate::common::Result;
+use crate::compiler::{self, CompilerOptions};
+
+#[derive(Clap)]
+pub struct Compile {
+    file: PathBuf,
+
+    #[clap(short = 'o')]
+    out_file: PathBuf,
+
+    #[clap(flatten)]
+    options: CompilerOptions,
+}
+
+impl Compile {
+    pub fn run(self) -> Result<()> {
+        eprintln!(
+            ">>> {} -> {}",
+            &self.file.to_string_lossy(),
+            self.out_file.to_string_lossy()
+        );
+        compiler::compile_file(&self.file, &self.out_file, &self.options)
+    }
+}
diff --git a/src/commands/eval.rs b/src/commands/eval.rs
new file mode 100644
index 000000000000..112bee64625b
--- /dev/null
+++ b/src/commands/eval.rs
@@ -0,0 +1,30 @@
+use clap::Clap;
+
+use crate::codegen;
+use crate::interpreter;
+use crate::parser;
+use crate::Result;
+
+/// Evaluate an expression and print its result
+#[derive(Clap)]
+pub struct Eval {
+    /// JIT-compile with LLVM instead of interpreting
+    #[clap(long)]
+    jit: bool,
+
+    /// Expression to evaluate
+    expr: String,
+}
+
+impl Eval {
+    pub fn run(self) -> Result<()> {
+        let (_, parsed) = parser::expr(&self.expr)?;
+        let result = if self.jit {
+            codegen::jit_eval::<i64>(&parsed)?.into()
+        } else {
+            interpreter::eval(&parsed)?
+        };
+        println!("{}", result);
+        Ok(())
+    }
+}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
new file mode 100644
index 000000000000..9c0038dabfb1
--- /dev/null
+++ b/src/commands/mod.rs
@@ -0,0 +1,5 @@
+pub mod compile;
+pub mod eval;
+
+pub use compile::Compile;
+pub use eval::Eval;
diff --git a/src/common/env.rs b/src/common/env.rs
new file mode 100644
index 000000000000..8b5cde49e9e4
--- /dev/null
+++ b/src/common/env.rs
@@ -0,0 +1,40 @@
+use std::collections::HashMap;
+
+use crate::ast::Ident;
+
+/// A lexical environment
+#[derive(Debug, PartialEq, Eq)]
+pub struct Env<'ast, V>(Vec<HashMap<&'ast Ident<'ast>, V>>);
+
+impl<'ast, V> Default for Env<'ast, V> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<'ast, V> Env<'ast, V> {
+    pub fn new() -> Self {
+        Self(vec![Default::default()])
+    }
+
+    pub fn push(&mut self) {
+        self.0.push(Default::default());
+    }
+
+    pub fn pop(&mut self) {
+        self.0.pop();
+    }
+
+    pub fn set(&mut self, k: &'ast Ident<'ast>, v: V) {
+        self.0.last_mut().unwrap().insert(k, v);
+    }
+
+    pub fn resolve<'a>(&'a self, k: &'ast Ident<'ast>) -> Option<&'a V> {
+        for ctx in self.0.iter().rev() {
+            if let Some(res) = ctx.get(k) {
+                return Some(res);
+            }
+        }
+        None
+    }
+}
diff --git a/src/common/error.rs b/src/common/error.rs
new file mode 100644
index 000000000000..f3f3023ceaf8
--- /dev/null
+++ b/src/common/error.rs
@@ -0,0 +1,50 @@
+use std::{io, result};
+
+use thiserror::Error;
+
+use crate::{codegen, interpreter, parser};
+
+#[derive(Error, Debug)]
+pub enum Error {
+    #[error(transparent)]
+    IOError(#[from] io::Error),
+
+    #[error("Error parsing input: {0}")]
+    ParseError(#[from] parser::Error),
+
+    #[error("Error evaluating expression: {0}")]
+    EvalError(#[from] interpreter::Error),
+
+    #[error("Compile error: {0}")]
+    CodegenError(#[from] codegen::Error),
+
+    #[error("{0}")]
+    Message(String),
+}
+
+impl From<String> for Error {
+    fn from(s: String) -> Self {
+        Self::Message(s)
+    }
+}
+
+impl<'a> From<nom::Err<nom::error::Error<&'a str>>> for Error {
+    fn from(e: nom::Err<nom::error::Error<&'a str>>) -> Self {
+        use nom::error::Error as NomError;
+        use nom::Err::*;
+
+        Self::ParseError(match e {
+            Incomplete(i) => Incomplete(i),
+            Error(NomError { input, code }) => Error(NomError {
+                input: input.to_owned(),
+                code,
+            }),
+            Failure(NomError { input, code }) => Failure(NomError {
+                input: input.to_owned(),
+                code,
+            }),
+        })
+    }
+}
+
+pub type Result<T> = result::Result<T, Error>;
diff --git a/src/common/mod.rs b/src/common/mod.rs
new file mode 100644
index 000000000000..af5974a116fb
--- /dev/null
+++ b/src/common/mod.rs
@@ -0,0 +1,4 @@
+pub(crate) mod env;
+pub(crate) mod error;
+
+pub use error::{Error, Result};
diff --git a/src/compiler.rs b/src/compiler.rs
new file mode 100644
index 000000000000..5f8e1ef4fa03
--- /dev/null
+++ b/src/compiler.rs
@@ -0,0 +1,84 @@
+use std::fmt::{self, Display};
+use std::path::Path;
+use std::str::FromStr;
+use std::{fs, result};
+
+use clap::Clap;
+use test_strategy::Arbitrary;
+
+use crate::codegen::{self, Codegen};
+use crate::common::Result;
+use crate::parser;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Arbitrary)]
+pub enum OutputFormat {
+    LLVM,
+    Bitcode,
+}
+
+impl Default for OutputFormat {
+    fn default() -> Self {
+        Self::Bitcode
+    }
+}
+
+impl FromStr for OutputFormat {
+    type Err = String;
+
+    fn from_str(s: &str) -> result::Result<Self, Self::Err> {
+        match s {
+            "llvm" => Ok(Self::LLVM),
+            "binary" => Ok(Self::Bitcode),
+            _ => Err(format!(
+                "Invalid output format {}, expected one of {{llvm, binary}}",
+                s
+            )),
+        }
+    }
+}
+
+impl Display for OutputFormat {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            OutputFormat::LLVM => f.write_str("llvm"),
+            OutputFormat::Bitcode => f.write_str("binary"),
+        }
+    }
+}
+
+#[derive(Clap, Debug, PartialEq, Eq, Default)]
+pub struct CompilerOptions {
+    #[clap(long, short = 'f', default_value)]
+    format: OutputFormat,
+}
+
+pub fn compile_file(input: &Path, output: &Path, options: &CompilerOptions) -> Result<()> {
+    let src = fs::read_to_string(input)?;
+    let (_, decls) = parser::toplevel(&src)?; // TODO: statements
+    let context = codegen::Context::create();
+    let mut codegen = Codegen::new(
+        &context,
+        &input
+            .file_stem()
+            .map_or("UNKNOWN".to_owned(), |s| s.to_string_lossy().into_owned()),
+    );
+    for decl in &decls {
+        codegen.codegen_decl(decl)?;
+    }
+    match options.format {
+        OutputFormat::LLVM => codegen.print_to_file(output)?,
+        OutputFormat::Bitcode => codegen.binary_to_file(output)?,
+    }
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use test_strategy::proptest;
+
+    #[proptest]
+    fn output_format_display_from_str_round_trip(of: OutputFormat) {
+        assert_eq!(OutputFormat::from_str(&of.to_string()), Ok(of));
+    }
+}
diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs
new file mode 100644
index 000000000000..e0299d180553
--- /dev/null
+++ b/src/interpreter/error.rs
@@ -0,0 +1,16 @@
+use std::result;
+
+use thiserror::Error;
+
+use crate::ast::{Ident, Type};
+
+#[derive(Debug, PartialEq, Eq, Error)]
+pub enum Error {
+    #[error("Undefined variable {0}")]
+    UndefinedVariable(Ident<'static>),
+
+    #[error("Unexpected type {actual}, expected type {expected}")]
+    InvalidType { actual: Type, expected: Type },
+}
+
+pub type Result<T> = result::Result<T, Error>;
diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs
new file mode 100644
index 000000000000..adff3568c2c3
--- /dev/null
+++ b/src/interpreter/mod.rs
@@ -0,0 +1,125 @@
+mod error;
+mod value;
+
+pub use self::error::{Error, Result};
+pub use self::value::Value;
+use crate::ast::{BinaryOperator, Expr, Ident, Literal, UnaryOperator};
+use crate::common::env::Env;
+
+#[derive(Debug, Default)]
+pub struct Interpreter<'a> {
+    env: Env<'a, Value>,
+}
+
+impl<'a> Interpreter<'a> {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    fn resolve(&self, var: &'a Ident<'a>) -> Result<Value> {
+        self.env
+            .resolve(var)
+            .cloned()
+            .ok_or_else(|| Error::UndefinedVariable(var.to_owned()))
+    }
+
+    pub fn eval(&mut self, expr: &'a Expr<'a>) -> Result<Value> {
+        match expr {
+            Expr::Ident(id) => self.resolve(id),
+            Expr::Literal(Literal::Int(i)) => Ok((*i).into()),
+            Expr::UnaryOp { op, rhs } => {
+                let rhs = self.eval(rhs)?;
+                match op {
+                    UnaryOperator::Neg => -rhs,
+                    _ => unimplemented!(),
+                }
+            }
+            Expr::BinaryOp { lhs, op, rhs } => {
+                let lhs = self.eval(lhs)?;
+                let rhs = self.eval(rhs)?;
+                match op {
+                    BinaryOperator::Add => lhs + rhs,
+                    BinaryOperator::Sub => lhs - rhs,
+                    BinaryOperator::Mul => lhs * rhs,
+                    BinaryOperator::Div => lhs / rhs,
+                    BinaryOperator::Pow => todo!(),
+                    BinaryOperator::Equ => Ok(lhs.eq(&rhs).into()),
+                    BinaryOperator::Neq => todo!(),
+                }
+            }
+            Expr::Let { bindings, body } => {
+                self.env.push();
+                for (id, val) in bindings {
+                    let val = self.eval(val)?;
+                    self.env.set(id, val);
+                }
+                let res = self.eval(body)?;
+                self.env.pop();
+                Ok(res)
+            }
+            Expr::If {
+                condition,
+                then,
+                else_,
+            } => {
+                let condition = self.eval(condition)?;
+                if *(condition.into_type::<bool>()?) {
+                    self.eval(then)
+                } else {
+                    self.eval(else_)
+                }
+            }
+        }
+    }
+}
+
+pub fn eval<'a>(expr: &'a Expr<'a>) -> Result<Value> {
+    let mut interpreter = Interpreter::new();
+    interpreter.eval(expr)
+}
+
+#[cfg(test)]
+mod tests {
+    use std::convert::TryFrom;
+
+    use super::value::{TypeOf, Val};
+    use super::*;
+    use BinaryOperator::*;
+
+    fn int_lit(i: u64) -> Box<Expr<'static>> {
+        Box::new(Expr::Literal(Literal::Int(i)))
+    }
+
+    fn parse_eval<T>(src: &str) -> T
+    where
+        for<'a> &'a T: TryFrom<&'a Val>,
+        T: Clone + TypeOf,
+    {
+        let expr = crate::parser::expr(src).unwrap().1;
+        let res = eval(&expr).unwrap();
+        res.into_type::<T>().unwrap().clone()
+    }
+
+    #[test]
+    fn simple_addition() {
+        let expr = Expr::BinaryOp {
+            lhs: int_lit(1),
+            op: Mul,
+            rhs: int_lit(2),
+        };
+        let res = eval(&expr).unwrap();
+        assert_eq!(*res.into_type::<i64>().unwrap(), 2);
+    }
+
+    #[test]
+    fn variable_shadowing() {
+        let res = parse_eval::<i64>("let x = 1 in (let x = 2 in x) + x");
+        assert_eq!(res, 3);
+    }
+
+    #[test]
+    fn conditional_with_equals() {
+        let res = parse_eval::<i64>("let x = 1 in if x == 1 then 2 else 4");
+        assert_eq!(res, 2);
+    }
+}
diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs
new file mode 100644
index 000000000000..69e4d4ffeb96
--- /dev/null
+++ b/src/interpreter/value.rs
@@ -0,0 +1,134 @@
+use std::convert::TryFrom;
+use std::fmt::{self, Display};
+use std::ops::{Add, Div, Mul, Neg, Sub};
+use std::rc::Rc;
+
+use derive_more::{Deref, From, TryInto};
+
+use super::{Error, Result};
+use crate::ast::Type;
+
+#[derive(Debug, PartialEq, From, TryInto)]
+#[try_into(owned, ref)]
+pub enum Val {
+    Int(i64),
+    Float(f64),
+    Bool(bool),
+}
+
+impl From<u64> for Val {
+    fn from(i: u64) -> Self {
+        Self::from(i as i64)
+    }
+}
+
+impl Display for Val {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Val::Int(x) => x.fmt(f),
+            Val::Float(x) => x.fmt(f),
+            Val::Bool(x) => x.fmt(f),
+        }
+    }
+}
+
+impl Val {
+    pub fn type_(&self) -> Type {
+        match self {
+            Val::Int(_) => Type::Int,
+            Val::Float(_) => Type::Float,
+            Val::Bool(_) => Type::Bool,
+        }
+    }
+
+    pub fn into_type<'a, T>(&'a self) -> Result<&'a T>
+    where
+        T: TypeOf + 'a + Clone,
+        &'a T: TryFrom<&'a Self>,
+    {
+        <&T>::try_from(self).map_err(|_| Error::InvalidType {
+            actual: self.type_(),
+            expected: <T as TypeOf>::type_of(),
+        })
+    }
+}
+
+#[derive(Debug, PartialEq, Clone, Deref)]
+pub struct Value(Rc<Val>);
+
+impl Display for Value {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl<T> From<T> for Value
+where
+    Val: From<T>,
+{
+    fn from(x: T) -> Self {
+        Self(Rc::new(x.into()))
+    }
+}
+
+impl Neg for Value {
+    type Output = Result<Value>;
+
+    fn neg(self) -> Self::Output {
+        Ok((-self.into_type::<i64>()?).into())
+    }
+}
+
+impl Add for Value {
+    type Output = Result<Value>;
+
+    fn add(self, rhs: Self) -> Self::Output {
+        Ok((self.into_type::<i64>()? + rhs.into_type::<i64>()?).into())
+    }
+}
+
+impl Sub for Value {
+    type Output = Result<Value>;
+
+    fn sub(self, rhs: Self) -> Self::Output {
+        Ok((self.into_type::<i64>()? - rhs.into_type::<i64>()?).into())
+    }
+}
+
+impl Mul for Value {
+    type Output = Result<Value>;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        Ok((self.into_type::<i64>()? * rhs.into_type::<i64>()?).into())
+    }
+}
+
+impl Div for Value {
+    type Output = Result<Value>;
+
+    fn div(self, rhs: Self) -> Self::Output {
+        Ok((self.into_type::<f64>()? / rhs.into_type::<f64>()?).into())
+    }
+}
+
+pub trait TypeOf {
+    fn type_of() -> Type;
+}
+
+impl TypeOf for i64 {
+    fn type_of() -> Type {
+        Type::Int
+    }
+}
+
+impl TypeOf for bool {
+    fn type_of() -> Type {
+        Type::Bool
+    }
+}
+
+impl TypeOf for f64 {
+    fn type_of() -> Type {
+        Type::Float
+    }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 000000000000..c31fda32dbc4
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,31 @@
+use clap::Clap;
+
+pub mod ast;
+pub mod codegen;
+pub(crate) mod commands;
+pub(crate) mod common;
+pub mod compiler;
+pub mod interpreter;
+pub mod parser;
+
+pub use common::{Error, Result};
+
+#[derive(Clap)]
+struct Opts {
+    #[clap(subcommand)]
+    subcommand: Command,
+}
+
+#[derive(Clap)]
+enum Command {
+    Eval(commands::Eval),
+    Compile(commands::Compile),
+}
+
+fn main() -> anyhow::Result<()> {
+    let opts = Opts::parse();
+    match opts.subcommand {
+        Command::Eval(eval) => Ok(eval.run()?),
+        Command::Compile(compile) => Ok(compile.run()?),
+    }
+}
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
new file mode 100644
index 000000000000..811450da0d61
--- /dev/null
+++ b/src/parser/mod.rs
@@ -0,0 +1,445 @@
+use nom::character::complete::{digit1, multispace0, multispace1};
+use nom::error::{ErrorKind, ParseError};
+use nom::{
+    alt, char, complete, delimited, do_parse, flat_map, many0, map, named, parse_to,
+    separated_list0, separated_list1, tag, tuple,
+};
+use pratt::{Affix, Associativity, PrattParser, Precedence};
+
+use crate::ast::{BinaryOperator, Decl, Expr, Fun, Ident, Literal, UnaryOperator};
+
+pub type Error = nom::Err<nom::error::Error<String>>;
+
+#[derive(Debug)]
+enum TokenTree<'a> {
+    Prefix(UnaryOperator),
+    // Postfix(char),
+    Infix(BinaryOperator),
+    Primary(Expr<'a>),
+    Group(Vec<TokenTree<'a>>),
+}
+
+named!(prefix(&str) -> TokenTree, map!(alt!(
+    complete!(char!('-')) => { |_| UnaryOperator::Neg } |
+    complete!(char!('!')) => { |_| UnaryOperator::Not }
+), TokenTree::Prefix));
+
+named!(infix(&str) -> TokenTree, map!(alt!(
+    complete!(tag!("==")) => { |_| BinaryOperator::Equ } |
+    complete!(tag!("!=")) => { |_| BinaryOperator::Neq } |
+    complete!(char!('+')) => { |_| BinaryOperator::Add } |
+    complete!(char!('-')) => { |_| BinaryOperator::Sub } |
+    complete!(char!('*')) => { |_| BinaryOperator::Mul } |
+    complete!(char!('/')) => { |_| BinaryOperator::Div } |
+    complete!(char!('^')) => { |_| BinaryOperator::Pow }
+), TokenTree::Infix));
+
+named!(primary(&str) -> TokenTree, alt!(
+    do_parse!(
+        multispace0 >>
+        char!('(') >>
+        multispace0 >>
+        group: group >>
+        multispace0 >>
+        char!(')') >>
+        multispace0 >>
+            (TokenTree::Group(group))
+    ) |
+    delimited!(multispace0, simple_expr, multispace0) => { |s| TokenTree::Primary(s) }
+));
+
+named!(
+    rest(&str) -> Vec<(TokenTree, Vec<TokenTree>, TokenTree)>,
+    many0!(tuple!(
+        infix,
+        delimited!(multispace0, many0!(prefix), multispace0),
+        primary
+        // many0!(postfix)
+    ))
+);
+
+named!(group(&str) -> Vec<TokenTree>, do_parse!(
+    prefix: many0!(prefix)
+        >> primary: primary
+        // >> postfix: many0!(postfix)
+        >> rest: rest
+        >> ({
+            let mut res = prefix;
+            res.push(primary);
+            // res.append(&mut postfix);
+            for (infix, mut prefix, primary/*, mut postfix*/) in rest {
+                res.push(infix);
+                res.append(&mut prefix);
+                res.push(primary);
+                // res.append(&mut postfix);
+            }
+            res
+        })
+));
+
+fn token_tree(i: &str) -> nom::IResult<&str, Vec<TokenTree>> {
+    group(i)
+}
+
+struct ExprParser;
+
+impl<'a, I> PrattParser<I> for ExprParser
+where
+    I: Iterator<Item = TokenTree<'a>>,
+{
+    type Error = pratt::NoError;
+    type Input = TokenTree<'a>;
+    type Output = Expr<'a>;
+
+    fn query(&mut self, input: &Self::Input) -> Result<Affix, Self::Error> {
+        use BinaryOperator::*;
+        use UnaryOperator::*;
+
+        Ok(match input {
+            TokenTree::Infix(Add) => Affix::Infix(Precedence(6), Associativity::Left),
+            TokenTree::Infix(Sub) => Affix::Infix(Precedence(6), Associativity::Left),
+            TokenTree::Infix(Mul) => Affix::Infix(Precedence(7), Associativity::Left),
+            TokenTree::Infix(Div) => Affix::Infix(Precedence(7), Associativity::Left),
+            TokenTree::Infix(Pow) => Affix::Infix(Precedence(8), Associativity::Right),
+            TokenTree::Infix(Equ) => Affix::Infix(Precedence(4), Associativity::Right),
+            TokenTree::Infix(Neq) => Affix::Infix(Precedence(4), Associativity::Right),
+            TokenTree::Prefix(Neg) => Affix::Prefix(Precedence(6)),
+            TokenTree::Prefix(Not) => Affix::Prefix(Precedence(6)),
+            TokenTree::Primary(_) => Affix::Nilfix,
+            TokenTree::Group(_) => Affix::Nilfix,
+        })
+    }
+
+    fn primary(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> {
+        Ok(match input {
+            TokenTree::Primary(expr) => expr,
+            TokenTree::Group(group) => self.parse(&mut group.into_iter()).unwrap(),
+            _ => unreachable!(),
+        })
+    }
+
+    fn infix(
+        &mut self,
+        lhs: Self::Output,
+        op: Self::Input,
+        rhs: Self::Output,
+    ) -> Result<Self::Output, Self::Error> {
+        let op = match op {
+            TokenTree::Infix(op) => op,
+            _ => unreachable!(),
+        };
+        Ok(Expr::BinaryOp {
+            lhs: Box::new(lhs),
+            op,
+            rhs: Box::new(rhs),
+        })
+    }
+
+    fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> Result<Self::Output, Self::Error> {
+        let op = match op {
+            TokenTree::Prefix(op) => op,
+            _ => unreachable!(),
+        };
+
+        Ok(Expr::UnaryOp {
+            op,
+            rhs: Box::new(rhs),
+        })
+    }
+
+    fn postfix(
+        &mut self,
+        _lhs: Self::Output,
+        _op: Self::Input,
+    ) -> Result<Self::Output, Self::Error> {
+        unreachable!()
+    }
+}
+
+fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
+where
+    E: ParseError<&'a str>,
+{
+    let mut chars = i.chars();
+    if let Some(f) = chars.next() {
+        if f.is_alphabetic() || f == '_' {
+            let mut idx = 1;
+            for c in chars {
+                if !(c.is_alphanumeric() || c == '_') {
+                    break;
+                }
+                idx += 1;
+            }
+            Ok((&i[idx..], Ident::from_str_unchecked(&i[..idx])))
+        } else {
+            Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy)))
+        }
+    } else {
+        Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Eof)))
+    }
+}
+
+named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::Int));
+
+named!(literal(&str) -> Expr, map!(alt!(int), Expr::Literal));
+
+named!(binding(&str) -> (Ident, Expr), do_parse!(
+    multispace0
+        >> ident: ident
+        >> multispace0
+        >> char!('=')
+        >> multispace0
+        >> expr: expr
+        >> (ident, expr)
+));
+
+named!(let_(&str) -> Expr, do_parse!(
+    tag!("let")
+        >> multispace0
+        >> bindings: separated_list1!(alt!(char!(';') | char!('\n')), binding)
+        >> multispace0
+        >> tag!("in")
+        >> multispace0
+        >> body: expr
+        >> (Expr::Let {
+            bindings,
+            body: Box::new(body)
+        })
+));
+
+named!(if_(&str) -> Expr, do_parse! (
+    tag!("if")
+        >> multispace0
+        >> condition: expr
+        >> multispace0
+        >> tag!("then")
+        >> multispace0
+        >> then: expr
+        >> multispace0
+        >> tag!("else")
+        >> multispace0
+        >> else_: expr
+        >> (Expr::If {
+            condition: Box::new(condition),
+            then: Box::new(then),
+            else_: Box::new(else_)
+        })
+));
+
+named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident));
+
+named!(simple_expr(&str) -> Expr, alt!(
+    let_ |
+    if_ |
+    literal |
+    ident_expr
+));
+
+named!(pub expr(&str) -> Expr, alt!(
+    map!(token_tree, |tt| {
+        ExprParser.parse(&mut tt.into_iter()).unwrap()
+    }) |
+    simple_expr));
+
+//////
+
+named!(fun(&str) -> Fun, do_parse!(
+    tag!("fn")
+        >> multispace0
+        >> name: ident
+        >> multispace1
+        >> args: separated_list0!(multispace1, ident)
+        >> multispace0
+        >> char!('=')
+        >> multispace0
+        >> body: expr
+        >> (Fun {
+            name,
+            args,
+            body
+        })
+));
+
+named!(pub decl(&str) -> Decl, alt!(
+    fun => { |f| Decl::Fun(f) }
+));
+
+named!(pub toplevel(&str) -> Vec<Decl>, separated_list0!(multispace1, decl));
+
+#[cfg(test)]
+mod tests {
+    use std::convert::{TryFrom, TryInto};
+
+    use super::*;
+    use BinaryOperator::*;
+    use Expr::{BinaryOp, If, Let, UnaryOp};
+    use UnaryOperator::*;
+
+    fn ident_expr(s: &str) -> Box<Expr> {
+        Box::new(Expr::Ident(Ident::try_from(s).unwrap()))
+    }
+
+    macro_rules! test_parse {
+        ($parser: ident, $src: expr) => {{
+            let (rem, res) = $parser($src).unwrap();
+            assert!(
+                rem.is_empty(),
+                "non-empty remainder: \"{}\", parsed: {:?}",
+                rem,
+                res
+            );
+            res
+        }};
+    }
+
+    mod operators {
+        use super::*;
+
+        #[test]
+        fn mul_plus() {
+            let (rem, res) = expr("x*y+z").unwrap();
+            assert!(rem.is_empty());
+            assert_eq!(
+                res,
+                BinaryOp {
+                    lhs: Box::new(BinaryOp {
+                        lhs: ident_expr("x"),
+                        op: Mul,
+                        rhs: ident_expr("y")
+                    }),
+                    op: Add,
+                    rhs: ident_expr("z")
+                }
+            )
+        }
+
+        #[test]
+        fn mul_plus_ws() {
+            let (rem, res) = expr("x * y    +    z").unwrap();
+            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
+            assert_eq!(
+                res,
+                BinaryOp {
+                    lhs: Box::new(BinaryOp {
+                        lhs: ident_expr("x"),
+                        op: Mul,
+                        rhs: ident_expr("y")
+                    }),
+                    op: Add,
+                    rhs: ident_expr("z")
+                }
+            )
+        }
+
+        #[test]
+        fn unary() {
+            let (rem, res) = expr("x * -z").unwrap();
+            assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
+            assert_eq!(
+                res,
+                BinaryOp {
+                    lhs: ident_expr("x"),
+                    op: Mul,
+                    rhs: Box::new(UnaryOp {
+                        op: Neg,
+                        rhs: ident_expr("z"),
+                    })
+                }
+            )
+        }
+
+        #[test]
+        fn mul_literal() {
+            let (rem, res) = expr("x * 3").unwrap();
+            assert!(rem.is_empty());
+            assert_eq!(
+                res,
+                BinaryOp {
+                    lhs: ident_expr("x"),
+                    op: Mul,
+                    rhs: Box::new(Expr::Literal(Literal::Int(3))),
+                }
+            )
+        }
+
+        #[test]
+        fn equ() {
+            let res = test_parse!(expr, "x * 7 == 7");
+            assert_eq!(
+                res,
+                BinaryOp {
+                    lhs: Box::new(BinaryOp {
+                        lhs: ident_expr("x"),
+                        op: Mul,
+                        rhs: Box::new(Expr::Literal(Literal::Int(7)))
+                    }),
+                    op: Equ,
+                    rhs: Box::new(Expr::Literal(Literal::Int(7)))
+                }
+            )
+        }
+    }
+
+    #[test]
+    fn let_complex() {
+        let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");
+        assert_eq!(
+            res,
+            Let {
+                bindings: vec![
+                    (
+                        Ident::try_from("x").unwrap(),
+                        Expr::Literal(Literal::Int(1))
+                    ),
+                    (
+                        Ident::try_from("y").unwrap(),
+                        Expr::BinaryOp {
+                            lhs: ident_expr("x"),
+                            op: Mul,
+                            rhs: Box::new(Expr::Literal(Literal::Int(7)))
+                        }
+                    )
+                ],
+                body: Box::new(Expr::BinaryOp {
+                    lhs: Box::new(Expr::BinaryOp {
+                        lhs: ident_expr("x"),
+                        op: Add,
+                        rhs: ident_expr("y"),
+                    }),
+                    op: Mul,
+                    rhs: Box::new(Expr::Literal(Literal::Int(4))),
+                })
+            }
+        )
+    }
+
+    #[test]
+    fn if_simple() {
+        let res = test_parse!(expr, "if x == 8 then 9 else 20");
+        assert_eq!(
+            res,
+            If {
+                condition: Box::new(BinaryOp {
+                    lhs: ident_expr("x"),
+                    op: Equ,
+                    rhs: Box::new(Expr::Literal(Literal::Int(8))),
+                }),
+                then: Box::new(Expr::Literal(Literal::Int(9))),
+                else_: Box::new(Expr::Literal(Literal::Int(20)))
+            }
+        )
+    }
+
+    #[test]
+    fn fn_decl() {
+        let res = test_parse!(decl, "fn id x = x");
+        assert_eq!(
+            res,
+            Decl::Fun(Fun {
+                name: "id".try_into().unwrap(),
+                args: vec!["x".try_into().unwrap()],
+                body: *ident_expr("x"),
+            })
+        )
+    }
+}