about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/compiler.rs31
-rw-r--r--tvix/eval/src/eval.rs2
-rw-r--r--tvix/eval/src/opcode.rs6
-rw-r--r--tvix/eval/src/value.rs25
-rw-r--r--tvix/eval/src/vm.rs51
5 files changed, 110 insertions, 5 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs
index 4a23db3dcb..65b97cd56c 100644
--- a/tvix/eval/src/compiler.rs
+++ b/tvix/eval/src/compiler.rs
@@ -6,17 +6,18 @@ use crate::errors::EvalResult;
 use crate::opcode::OpCode;
 use crate::value::Value;
 use rnix;
+use rnix::types::TypedNode;
 
 struct Compiler {
     chunk: Chunk,
 }
 
 impl Compiler {
-    fn compile(&mut self, node: &rnix::SyntaxNode) -> EvalResult<()> {
+    fn compile(&mut self, node: rnix::SyntaxNode) -> EvalResult<()> {
         match node.kind() {
             // Root of a file contains no content, it's just a marker
             // type.
-            rnix::SyntaxKind::NODE_ROOT => self.compile(&node.first_child().expect("TODO")),
+            rnix::SyntaxKind::NODE_ROOT => self.compile(node.first_child().expect("TODO")),
 
             // Literals contain a single token comprising of the
             // literal itself.
@@ -25,6 +26,11 @@ impl Compiler {
                 self.compile_literal(token)
             }
 
+            rnix::SyntaxKind::NODE_BIN_OP => {
+                let op = rnix::types::BinOp::cast(node).expect("TODO (should not be possible)");
+                self.compile_binop(op)
+            }
+
             kind => {
                 println!("visiting unsupported node: {:?}", kind);
                 Ok(())
@@ -52,6 +58,25 @@ impl Compiler {
             rnix::NixValue::Path(_, _) => todo!(),
         }
     }
+
+    fn compile_binop(&mut self, op: rnix::types::BinOp) -> EvalResult<()> {
+        self.compile(op.lhs().unwrap())?;
+        self.compile(op.rhs().unwrap())?;
+
+        use rnix::types::BinOpKind;
+
+        let opcode = match op.operator().unwrap() {
+            BinOpKind::Add => OpCode::OpAdd,
+            BinOpKind::Sub => OpCode::OpSub,
+            BinOpKind::Mul => OpCode::OpMul,
+            BinOpKind::Div => OpCode::OpDiv,
+
+            _ => todo!(),
+        };
+
+        self.chunk.add_op(opcode);
+        Ok(())
+    }
 }
 
 pub fn compile(ast: rnix::AST) -> EvalResult<Chunk> {
@@ -59,7 +84,7 @@ pub fn compile(ast: rnix::AST) -> EvalResult<Chunk> {
         chunk: Chunk::default(),
     };
 
-    c.compile(&ast.node())?;
+    c.compile(ast.node())?;
 
     Ok(c.chunk)
 }
diff --git a/tvix/eval/src/eval.rs b/tvix/eval/src/eval.rs
index 0c4900e7e5..efa016ca98 100644
--- a/tvix/eval/src/eval.rs
+++ b/tvix/eval/src/eval.rs
@@ -12,7 +12,7 @@ pub fn interpret(code: String) -> EvalResult<String> {
     }
 
     let mut out = String::new();
-    writeln!(out, "{}", ast.root().dump()).ok();
+    println!("{}", ast.root().dump());
 
     let code = crate::compiler::compile(ast)?;
     writeln!(out, "code: {:?}", code).ok();
diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs
index b43f75efaa..307b695f6d 100644
--- a/tvix/eval/src/opcode.rs
+++ b/tvix/eval/src/opcode.rs
@@ -16,4 +16,10 @@ pub enum OpCode {
     OpNull,
     OpTrue,
     OpFalse,
+
+    // Simple binary operators
+    OpAdd,
+    OpSub,
+    OpMul,
+    OpDiv,
 }
diff --git a/tvix/eval/src/value.rs b/tvix/eval/src/value.rs
index 037284b144..cbfff55b49 100644
--- a/tvix/eval/src/value.rs
+++ b/tvix/eval/src/value.rs
@@ -8,3 +8,28 @@ pub enum Value {
     Integer(i64),
     Float(f64),
 }
+
+impl Value {
+    pub fn is_number(&self) -> bool {
+        match self {
+            Value::Integer(_) => true,
+            Value::Float(_) => true,
+            _ => false,
+        }
+    }
+
+    pub fn type_of(&self) -> &'static str {
+        match self {
+            Value::Null => "null",
+            Value::Bool(_) => "bool",
+            Value::Integer(_) => "int",
+            Value::Float(_) => "float",
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum NumberPair {
+    Floats(f64, f64),
+    Integer(i64, i64),
+}
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index e35cd99b1e..077acfcc1b 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -1,7 +1,12 @@
 //! This module implements the virtual (or abstract) machine that runs
 //! Tvix bytecode.
 
-use crate::{chunk::Chunk, errors::EvalResult, opcode::OpCode, value::Value};
+use crate::{
+    chunk::Chunk,
+    errors::{Error, EvalResult},
+    opcode::OpCode,
+    value::{NumberPair, Value},
+};
 
 pub struct VM {
     ip: usize,
@@ -18,6 +23,30 @@ impl VM {
         self.stack.pop().expect("TODO")
     }
 
+    fn pop_number_pair(&mut self) -> EvalResult<NumberPair> {
+        let v2 = self.pop();
+        let v1 = self.pop();
+
+        match (v1, v2) {
+            (Value::Integer(i1), Value::Integer(i2)) => Ok(NumberPair::Integer(i1, i2)),
+
+            (Value::Float(f1), Value::Float(f2)) => Ok(NumberPair::Floats(f1, f2)),
+
+            (Value::Integer(i1), Value::Float(f2)) => Ok(NumberPair::Floats(i1 as f64, f2)),
+
+            (Value::Float(f1), Value::Integer(i2)) => Ok(NumberPair::Floats(f1, i2 as f64)),
+
+            _ => Err(Error::TypeError {
+                expected: "number (either int or float)",
+                actual: if v1.is_number() {
+                    v2.type_of()
+                } else {
+                    v1.type_of()
+                },
+            }),
+        }
+    }
+
     fn inc_ip(&mut self) -> OpCode {
         let op = self.chunk.code[self.ip];
         self.ip += 1;
@@ -32,6 +61,26 @@ impl VM {
                     self.push(c);
                 }
 
+                OpCode::OpAdd => match self.pop_number_pair()? {
+                    NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 + f2)),
+                    NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 + i2)),
+                },
+
+                OpCode::OpSub => match self.pop_number_pair()? {
+                    NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 - f2)),
+                    NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 - i2)),
+                },
+
+                OpCode::OpMul => match self.pop_number_pair()? {
+                    NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 * f2)),
+                    NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 * i2)),
+                },
+
+                OpCode::OpDiv => match self.pop_number_pair()? {
+                    NumberPair::Floats(f1, f2) => self.push(Value::Float(f1 / f2)),
+                    NumberPair::Integer(i1, i2) => self.push(Value::Integer(i1 / i2)),
+                },
+
                 OpCode::OpNull => todo!(),
                 OpCode::OpTrue => todo!(),
                 OpCode::OpFalse => todo!(),