Просмотр исходного кода

zkas/compiler: Implement initial bincode generation.

parazyd 4 лет назад
Родитель
Сommit
ba41f1b863
5 измененных файлов с 105 добавлено и 26 удалено
  1. 5 4
      zkas/src/ast.rs
  2. 97 20
      zkas/src/compiler.rs
  3. 0 1
      zkas/src/main.rs
  4. 2 1
      zkas/src/opcode.rs
  5. 1 0
      zkas/src/types.rs

+ 5 - 4
zkas/src/ast.rs

@@ -2,11 +2,12 @@ use indexmap::IndexMap;
 
 use crate::{lexer::Token, opcode::Opcode, types::Type};
 
-#[derive(PartialEq, Clone, Debug)]
+#[derive(Copy, PartialEq, Clone, Debug)]
+#[repr(u8)]
 pub enum StatementType {
-    Assignment,
-    Call,
-    Noop,
+    Assignment = 0x00,
+    Call = 0x01,
+    Noop = 0xff,
 }
 
 pub enum Var {

+ 97 - 20
zkas/src/compiler.rs

@@ -1,6 +1,8 @@
-use std::str::Chars;
+use std::{io, io::Write, process, str::Chars};
 
-use crate::ast::{Constants, Statements, Variables, Witnesses};
+use termion::{color, style};
+
+use crate::ast::{Constants, StatementType, Statements, Witnesses};
 
 pub struct Compiler {
     file: String,
@@ -8,7 +10,6 @@ pub struct Compiler {
     constants: Constants,
     witnesses: Witnesses,
     statements: Statements,
-    stack: Variables,
     debug_info: bool,
 }
 
@@ -19,36 +20,112 @@ impl Compiler {
         constants: Constants,
         witnesses: Witnesses,
         statements: Statements,
-        stack: Variables,
         debug_info: bool,
     ) -> Self {
         // For nice error reporting, we'll load everything into a string
         // vector so we have references to lines.
         let lines = source.as_str().lines().map(|x| x.to_string()).collect();
-        Compiler {
-            file: filename.to_string(),
-            lines,
-            constants,
-            witnesses,
-            statements,
-            stack,
-            debug_info,
-        }
+        Compiler { file: filename.to_string(), lines, constants, witnesses, statements, debug_info }
     }
 
+    // TODO: varint encoding
     pub fn compile(&self) -> Vec<u8> {
-        if self.debug_info {
-            return self.compile_with_debug_info()
+        let mut bincode = vec![];
+
+        let mut stack_idx: u64 = 0;
+
+        // Temporary stack vector for lookups
+        let mut tmp_stack = vec![];
+
+        bincode.extend_from_slice(b".constant");
+        for i in &self.constants {
+            tmp_stack.push(i.name.as_str());
+            bincode.extend_from_slice(i.name.as_bytes());
+            bincode.push(i.typ as u8);
+            bincode.extend_from_slice(&stack_idx.to_le_bytes());
+            stack_idx += 1;
+        }
+
+        bincode.extend_from_slice(b".contract");
+        for i in &self.witnesses {
+            tmp_stack.push(i.name.as_str());
+            bincode.push(i.typ as u8);
+            bincode.extend_from_slice(&stack_idx.to_le_bytes());
+            stack_idx += 1;
+        }
+
+        bincode.extend_from_slice(b".circuit");
+        for i in &self.statements {
+            match i.typ {
+                StatementType::Assignment => {
+                    tmp_stack.push(&i.variable.as_ref().unwrap().name);
+                    stack_idx += 1;
+                }
+                // In case of a simple call, we don't append anything to the stack
+                StatementType::Call => {}
+                _ => unreachable!(),
+            }
+
+            bincode.push(i.typ as u8);
+            bincode.push(i.opcode as u8);
+            bincode.extend_from_slice(&i.args.len().to_le_bytes());
+
+            for arg in &i.args {
+                if let Some(found) = Compiler::lookup_stack(&tmp_stack, &arg.name) {
+                    bincode.extend_from_slice(&found.to_le_bytes());
+                    continue
+                }
+
+                self.error(
+                    format!("Failed finding a stack reference for `{}`", arg.name),
+                    arg.line,
+                    arg.column,
+                );
+            }
+        }
+
+        // If we're not doing debug info, we're done here and can return.
+        if !self.debug_info {
+            return bincode
+        }
+
+        // TODO: Otherwise, we proceed appending debug info
+
+        bincode
+    }
+
+    fn lookup_stack(stack: &[&str], name: &str) -> Option<u64> {
+        for (idx, n) in stack.iter().enumerate() {
+            if n == &name {
+                return Some(idx.try_into().unwrap())
+            }
         }
 
-        self.compile_without_debug_info()
+        None
     }
 
-    fn compile_with_debug_info(&self) -> Vec<u8> {
-        vec![]
+    fn error(&self, msg: String, ln: usize, col: usize) {
+        let err_msg = format!("{} (line {}, column {})", msg, ln, col);
+        let dbg_msg = format!("{}:{}:{}: {}", self.file, ln, col, self.lines[ln - 1]);
+        let pad = dbg_msg.split(": ").next().unwrap().len() + col + 2;
+        let caret = format!("{:width$}^", "", width = pad);
+        let msg = format!("{}\n{}\n{}\n", err_msg, dbg_msg, caret);
+        Compiler::abort(&msg);
     }
 
-    fn compile_without_debug_info(&self) -> Vec<u8> {
-        vec![]
+    fn abort(msg: &str) {
+        let stderr = io::stderr();
+        let mut handle = stderr.lock();
+        write!(
+            handle,
+            "{}{}Compiler error:{} {}",
+            style::Bold,
+            color::Fg(color::Red),
+            style::Reset,
+            msg,
+        )
+        .unwrap();
+        handle.flush().unwrap();
+        process::exit(1);
     }
 }

+ 0 - 1
zkas/src/main.rs

@@ -53,7 +53,6 @@ fn main() -> Result<()> {
         analyzer.constants,
         analyzer.witnesses,
         analyzer.statements,
-        analyzer.stack,
         !cli.strip,
     );
 

+ 2 - 1
zkas/src/opcode.rs

@@ -1,7 +1,8 @@
 use crate::types::Type;
 
 /// Opcodes supported by the VM
-#[derive(Clone, Debug)]
+#[derive(Copy, Clone, Debug)]
+#[repr(u8)]
 pub enum Opcode {
     EcAdd = 0x00,
     EcMul = 0x01,

+ 1 - 0
zkas/src/types.rs

@@ -1,5 +1,6 @@
 /// Types supported by the VM
 #[derive(Copy, Clone, PartialEq, Debug)]
+#[repr(u8)]
 pub enum Type {
     EcPoint = 0x00,