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

zkas/analyzer: Rewrite analyzer.

Luther Blissett 4 лет назад
Родитель
Сommit
ecbedd8894
1 измененных файлов с 247 добавлено и 167 удалено
  1. 247 167
      src/zkas/analyzer.rs

+ 247 - 167
src/zkas/analyzer.rs

@@ -1,22 +1,17 @@
-use std::{
-    io::{stdin, stdout, Read, Write},
-    str::Chars,
-};
+use std::str::Chars;
 
 use super::{
-    ast::{
-        Constant, Constants, StatementType, Statements, Var, Variable, Variables, Witness,
-        Witnesses,
-    },
+    ast::{Arg, Constant, Literal, Statement, StatementType, Var, Variable, Witness},
     error::ErrorEmitter,
-    types::Type,
+    VarType,
 };
 
 pub struct Analyzer {
-    pub constants: Constants,
-    pub witnesses: Witnesses,
-    pub statements: Statements,
-    pub stack: Variables,
+    pub constants: Vec<Constant>,
+    pub witnesses: Vec<Witness>,
+    pub statements: Vec<Statement>,
+    pub literals: Vec<Literal>,
+    pub stack: Vec<Variable>,
     error: ErrorEmitter,
 }
 
@@ -24,211 +19,302 @@ impl Analyzer {
     pub fn new(
         filename: &str,
         source: Chars,
-        constants: Constants,
-        witnesses: Witnesses,
-        statements: Statements,
+        constants: Vec<Constant>,
+        witnesses: Vec<Witness>,
+        statements: Vec<Statement>,
     ) -> Self {
         // For nice error reporting, we'll load everything into a string
         // vector so we have references to lines.
         let lines: Vec<String> = source.as_str().lines().map(|x| x.to_string()).collect();
         let error = ErrorEmitter::new("Semantic", filename, lines);
 
-        Analyzer { constants, witnesses, statements, stack: vec![], error }
+        Self { constants, witnesses, statements, literals: vec![], stack: vec![], error }
     }
 
     pub fn analyze_types(&mut self) {
-        // To work around the pedantic safety, we'll make new vectors and
-        // then replace the `statements` and `stack` vectors from the
-        // `Analyzer` object when we're done.
+        // To work around the pedantic safety, we'll make new vectors and then
+        // replace the `statements` and `stack` vectors from the `Analyzer`
+        // object when we are done.
         let mut statements = vec![];
         let mut stack = vec![];
 
         for statement in &self.statements {
+            //println!("{:?}", statement);
             let mut stmt = statement.clone();
 
             let (return_types, arg_types) = statement.opcode.arg_types();
-            let mut args = vec![];
+            let mut rhs = vec![];
 
-            // For variable length args, we implement `BaseArray` and `ScalarArray`.
-            // It's kinda ugly.
-            if arg_types[0] == Type::BaseArray || arg_types[0] == Type::ScalarArray {
-                if statement.args.is_empty() {
+            // This handling is kinda limiting, but it'll do for now.
+            if !(arg_types[0] == VarType::BaseArray || arg_types[0] == VarType::ScalarArray) {
+                // Check that number of args is correct
+                if statement.rhs.len() != arg_types.len() {
                     self.error.abort(
                         &format!(
-                            "Passed no arguments to `{:?}` call. Expected at least 1.",
-                            statement.opcode
+                            "Incorrect number of arguments for statement. Expected {}, got {}.",
+                            arg_types.len(),
+                            statement.rhs.len()
                         ),
                         statement.line,
                         1,
                     );
                 }
+            } else {
+                // In case of arrays, check there's at least one element.
+                if statement.rhs.is_empty() {
+                    self.error.abort(
+                        "Expected at least one element for statement using arrays.",
+                        statement.line,
+                        1,
+                    );
+                }
+            }
 
-                for i in &statement.args {
-                    if let Some(v) = self.lookup_var(&i.name) {
-                        let var_type = match v {
-                            Var::Constant(c) => c.typ,
-                            Var::Witness(c) => c.typ,
-                            Var::Variable(c) => c.typ,
-                        };
+            for (idx, arg) in statement.rhs.iter().enumerate() {
+                // In case an argument is a function call, we will first
+                // convert it to another statement that will get executed
+                // before this one. An important assumption is that this
+                // opcode has a return value. When executed we will push
+                // this value onto the stack and use it as a reference to
+                // the actual statement we're parsing at this moment.
+                // TODO: FIXME: This needs a recursive algorithm, as this
+                //              only allows a single nested function.
+                if let Arg::Func(func) = arg {
+                    let (f_return_types, f_arg_types) = func.opcode.arg_types();
+                    if f_return_types.is_empty() {
+                        self.error.abort(
+                            &format!(
+                                "Used a function argument which doesn't have a return value: {:?}",
+                                func.opcode
+                            ),
+                            statement.line,
+                            1,
+                        );
+                    }
+
+                    let v = Variable {
+                        name: func.lhs.clone().unwrap().name,
+                        typ: f_return_types[0],
+                        line: func.lhs.clone().unwrap().line,
+                        column: func.lhs.clone().unwrap().column,
+                    };
 
-                        if arg_types[0] == Type::BaseArray && var_type != Type::Base {
+                    // FIXME: Needs better *Array handling.
+                    if arg_types[0] == VarType::BaseArray {
+                        if f_return_types[0] != VarType::Base {
                             self.error.abort(
                                 &format!(
-                                    "Incorrect argument type. Expected `{:?}`, got `{:?}`",
-                                    arg_types[0],
-                                    Type::Base,
+                                    "Function passed as argument returns wrong type. Expected `{:?}`, got `{:?}`.",
+                                    VarType::Base,
+                                    f_return_types[0],
                                 ),
-                                i.line,
-                                i.column,
+                                v.line,
+                                v.column,
                             );
                         }
-
-                        if arg_types[0] == Type::ScalarArray && var_type != Type::Scalar {
+                    } else if arg_types[0] == VarType::ScalarArray {
+                        if f_return_types[0] != VarType::Scalar {
                             self.error.abort(
                                 &format!(
-                                    "Incorrect argument type. Expected `{:?}`, got `{:?}`",
-                                    arg_types[0],
-                                    Type::Scalar,
+                                    "Function passed as argument returns wrong type. Expected `{:?}`, got `{:?}`.",
+                                    VarType::Scalar,
+                                    f_return_types[0],
                                 ),
-                                i.line,
-                                i.column,
+                                v.line,
+                                v.column,
                             );
                         }
-
-                        let mut arg = i.clone();
-                        arg.typ = var_type;
-                        args.push(arg);
-                    } else {
+                    } else if f_return_types[0] != arg_types[idx] {
                         self.error.abort(
-                            &format!("Unknown argument reference `{}`.", i.name),
-                            i.line,
-                            i.column,
+                            &format!(
+                                "Function passed as argument returns wrong type. Expected `{:?}`, got `{:?}`.",
+                                arg_types[idx],
+                                f_return_types[0],
+                            ),
+                            v.line,
+                            v.column,
                         );
+                    } else {
+                        panic!();
                     }
-                }
-            } else {
-                if statement.args.len() != arg_types.len() {
-                    self.error.abort(
-                        &format!(
-                            "Incorrent number of args to `{:?}` call. Expected {}, got {}",
-                            statement.opcode,
-                            arg_types.len(),
-                            statement.args.len()
-                        ),
-                        statement.line,
-                        1,
-                    );
-                }
 
-                for (idx, i) in statement.args.iter().enumerate() {
-                    if let Some(v) = self.lookup_var(&i.name) {
-                        let var_type = match v {
-                            Var::Constant(c) => c.typ,
-                            Var::Witness(c) => c.typ,
-                            Var::Variable(c) => c.typ,
-                        };
+                    // Replace the statement function call with the variable from
+                    // the statement we just created to represent this nest.
+                    stmt.rhs[idx] = Arg::Var(v.clone());
+
+                    let mut rhs_inner = vec![];
+                    for i in &func.rhs {
+                        if let Arg::Var(v) = i {
+                            if let Some(var_ref) = self.lookup_var(&v.name) {
+                                let (var_type, ln, col) = match var_ref {
+                                    Var::Constant(c) => (c.typ, c.line, c.column),
+                                    Var::Witness(c) => (c.typ, c.line, c.column),
+                                    Var::Variable(c) => (c.typ, c.line, c.column),
+                                };
+
+                                if var_type != f_arg_types[idx] {
+                                    self.error.abort(
+                                        &format!(
+                                            "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
+                                            f_arg_types[idx], var_type
+                                        ),
+                                        ln,
+                                        col,
+                                    );
+                                }
+
+                                // Apply the proper type.
+                                let mut v_new = v.clone();
+                                v_new.typ = var_type;
+                                rhs_inner.push(Arg::Var(v_new));
+
+                                continue
+                            }
 
-                        if var_type != arg_types[idx] {
                             self.error.abort(
-                                &format!(
-                                    "Incorrect argument type. Expected `{:?}`, got `{:?}`",
-                                    arg_types[idx], var_type,
-                                ),
-                                i.line,
-                                i.column,
+                                &format!("Unknown variable reference `{}`.", v.name),
+                                v.line,
+                                v.column,
                             );
+                        } else {
+                            unimplemented!()
                         }
+                    }
 
-                        let mut arg = i.clone();
-                        arg.typ = var_type;
-                        args.push(arg);
-                    } else {
+                    let s = Statement {
+                        typ: func.typ,
+                        opcode: func.opcode,
+                        lhs: Some(v.clone()),
+                        rhs: rhs_inner,
+                        line: func.line,
+                    };
+
+                    // The lhs of the inner function call becomes rhs of the outer one.
+                    rhs.push(Arg::Var(v.clone()));
+
+                    // Add this to the list of statements.
+                    statements.push(s);
+
+                    // We replace self.stack here so we can do proper stack lookups.
+                    stack.push(v.clone());
+                    self.stack = stack.clone();
+
+                    //println!("{:#?}", stack);
+                    //println!("{:#?}", statements);
+                    continue
+                } // <-- Arg::Func
+
+                // The literals get pushed on their own "stack", and
+                // then the compiler will reference them by their own
+                // index when it comes to running the statement that
+                // requires the literal type.
+                if let Arg::Lit(v) = arg {
+                    // Match this literal type to a VarType for
+                    // type checking.
+                    let var_type = v.typ.to_vartype();
+                    if var_type != arg_types[idx] {
                         self.error.abort(
-                            &format!("Unknown argument reference `{}`.", i.name),
-                            i.line,
-                            i.column,
+                            &format!(
+                                "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
+                                arg_types[idx], var_type
+                            ),
+                            v.line,
+                            v.column,
                         );
                     }
-                }
-            }
 
-            match statement.typ {
-                StatementType::Assignment => {
-                    // Currently we just support a single return type.
-                    let mut var = statement.variable.clone().unwrap();
-                    var.typ = return_types[0];
-                    stmt.variable = Some(var.clone());
-                    stack.push(var.clone());
-                    self.stack = stack.clone();
-                    stmt.args = args;
-                    statements.push(stmt);
-                }
-                StatementType::Call => {
-                    stmt.args = args;
-                    statements.push(stmt);
+                    self.literals.push(v.clone());
+                    rhs.push(Arg::Lit(v.clone()));
+                    continue
                 }
-                _ => unreachable!(),
-            }
-        }
 
-        self.statements = statements;
-    }
+                if let Arg::Var(v) = arg {
+                    // Look up variable and check if type is correct.
+                    if let Some(s_var) = self.lookup_var(&v.name) {
+                        let (var_type, _ln, _col) = match s_var {
+                            Var::Constant(c) => (c.typ, c.line, c.column),
+                            Var::Witness(c) => (c.typ, c.line, c.column),
+                            Var::Variable(c) => (c.typ, c.line, c.column),
+                        };
 
-    pub fn analyze_semantic(&mut self) {
-        let mut stack = vec![];
+                        // FIXME: Better array handling
+                        if arg_types[0] == VarType::BaseArray {
+                            if var_type != VarType::Base {
+                                self.error.abort(
+                                    &format!(
+                                        "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
+                                        VarType::Base,
+                                        var_type
+                                    ),
+                                    v.line,
+                                    v.column,
+                                );
+                            }
+                        } else if arg_types[0] == VarType::ScalarArray {
+                            if var_type != VarType::Scalar {
+                                self.error.abort(
+                                    &format!(
+                                        "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
+                                        VarType::Scalar,
+                                        var_type
+                                    ),
+                                    v.line,
+                                    v.column,
+                                );
+                            }
+                        } else if var_type != arg_types[idx] {
+                            self.error.abort(
+                                &format!(
+                                    "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
+                                    arg_types[idx], var_type
+                                ),
+                                v.line,
+                                v.column,
+                            );
+                        }
 
-        println!("Loading constants...\n-----");
-        for i in &self.constants {
-            println!("Adding `{}` to stack", i.name);
-            stack.push(&i.name);
-            Analyzer::pause();
-        }
-        println!("Stack:\n{:#?}\n-----", stack);
+                        // Replace Dummy type with correct type.
+                        let mut v_new = v.clone();
+                        v_new.typ = var_type;
+                        rhs.push(Arg::Var(v_new));
+                        continue
+                    }
 
-        println!("Loading witnesses...\n-----");
-        for i in &self.witnesses {
-            println!("Adding `{}` to stack", i.name);
-            stack.push(&i.name);
-            Analyzer::pause();
-        }
-        println!("Stack:\n{:#?}\n-----", stack);
-
-        println!("Loading circuit...");
-        for i in &self.statements {
-            let argnames: Vec<String> = i.args.iter().map(|x| x.name.clone()).collect();
-            println!("Executing: {:?}({:?})", i.opcode, argnames);
-            Analyzer::pause();
-
-            for arg in &i.args {
-                print!("Looking up `{}` on the stack... ", arg.name);
-                if let Some(index) = stack.iter().position(|&r| r == &arg.name) {
-                    println!("Found at stack index {}", index);
-                } else {
                     self.error.abort(
-                        &format!("Could not find `{}` on the stack", arg.name),
-                        arg.line,
-                        arg.column,
+                        &format!("Unknown variable reference `{}`.", v.name),
+                        v.line,
+                        v.column,
                     );
                 }
-                Analyzer::pause();
+            } // <-- statement.rhs.iter().enumerate()
+
+            // We now type-checked and assigned types to the statement rhs,
+            // so now we apply it to the statement.
+            stmt.rhs = rhs;
+
+            // In case this statement is an assignment, we will push its
+            // result on the stack.
+            if statement.typ == StatementType::Assign {
+                let mut var = statement.lhs.clone().unwrap();
+                var.typ = return_types[0];
+                stmt.lhs = Some(var.clone());
+                stack.push(var.clone());
+                self.stack = stack.clone();
             }
 
-            match i.typ {
-                StatementType::Assignment => {
-                    println!("Pushing result as `{}` to stack", &i.variable.as_ref().unwrap().name);
-                    stack.push(&i.variable.as_ref().unwrap().name);
-                    println!("Stack:\n{:#?}\n-----", stack);
-                }
-                StatementType::Call => {
-                    println!("-----");
-                }
-                _ => unreachable!(),
-            }
-        }
+            //println!("{:#?}", stmt);
+            statements.push(stmt);
+        } // <-- for statement in &self.statements
+
+        // Here we replace the self.statements and self.stack with what we
+        // built so far. These can be used later on by the compiler after
+        // this function is finished.
+        self.statements = statements;
+        self.stack = stack;
 
-        // println!("{:#?}", self.constants);
-        // println!("{:#?}", self.witnesses);
-        // println!("{:#?}", self.statements);
+        println!("=================STATEMENTS===============\n{:#?}", self.statements);
+        println!("===================STACK==================\n{:#?}", self.stack);
+        println!("==================LITERALS================\n{:#?}", self.literals);
     }
 
     fn lookup_var(&self, name: &str) -> Option<Var> {
@@ -253,6 +339,7 @@ impl Analyzer {
                 return Some(i.clone())
             }
         }
+
         None
     }
 
@@ -262,6 +349,7 @@ impl Analyzer {
                 return Some(i.clone())
             }
         }
+
         None
     }
 
@@ -271,15 +359,7 @@ impl Analyzer {
                 return Some(i.clone())
             }
         }
-        None
-    }
 
-    fn pause() {
-        let msg = b"[Press Enter to continue]\r";
-        let mut stdout = stdout();
-        let _ = stdout.write(msg).unwrap();
-        stdout.flush().unwrap();
-        let _ = stdin().read(&mut [0]).unwrap();
-        write!(stdout, "{}{}\r", termion::cursor::Up(1), termion::clear::CurrentLine).unwrap();
+        None
     }
 }