Răsfoiți Sursa

zkas: Propagate io::Result instead of aborting on errors.

parazyd 3 ani în urmă
părinte
comite
576a20e057
7 a modificat fișierele cu 241 adăugiri și 181 ștergeri
  1. 17 6
      bin/zkas/src/main.rs
  2. 4 4
      src/sdk/python/src/zkas.rs
  3. 39 35
      src/zkas/analyzer.rs
  4. 8 8
      src/zkas/compiler.rs
  5. 3 3
      src/zkas/error.rs
  6. 19 10
      src/zkas/lexer.rs
  7. 151 115
      src/zkas/parser.rs

+ 17 - 6
bin/zkas/src/main.rs

@@ -94,22 +94,30 @@ fn main() -> ExitCode {
     // The lexer goes over the input file and separates its content into
     // tokens that get fed into a parser.
     let lexer = Lexer::new(filename, source.chars());
-    let tokens = lexer.lex();
+    let tokens = match lexer.lex() {
+        Ok(v) => v,
+        Err(_) => return ExitCode::FAILURE,
+    };
 
     // The parser goes over the tokens provided by the lexer and builds
     // the initial AST, not caring much about the semantics, just enforcing
     // syntax and general structure.
     let parser = Parser::new(filename, source.chars(), tokens);
-    let (namespace, k, constants, witnesses, statements) = parser.parse();
+    let (namespace, k, constants, witnesses, statements) = match parser.parse() {
+        Ok(v) => v,
+        Err(_) => return ExitCode::FAILURE,
+    };
 
     // The analyzer goes through the initial AST provided by the parser and
     // converts return and variable types to their correct forms, and also
     // checks that the semantics of the ZK script are correct.
     let mut analyzer = Analyzer::new(filename, source.chars(), constants, witnesses, statements);
-    analyzer.analyze_types();
+    if analyzer.analyze_types().is_err() {
+        return ExitCode::FAILURE
+    }
 
-    if iflag {
-        analyzer.analyze_semantic();
+    if iflag && analyzer.analyze_semantic().is_err() {
+        return ExitCode::FAILURE
     }
 
     if pflag {
@@ -132,7 +140,10 @@ fn main() -> ExitCode {
         !sflag,
     );
 
-    let bincode = compiler.compile();
+    let bincode = match compiler.compile() {
+        Ok(v) => v,
+        Err(_) => return ExitCode::FAILURE,
+    };
     // ANCHOR_END: zkas
 
     let output = if output.is_empty() { format!("{}.bin", filename) } else { output };

+ 4 - 4
src/sdk/python/src/zkas.rs

@@ -48,12 +48,12 @@ impl ZkBinary {
     fn new(filename: String, source_code: String) -> Self {
         let source = source_code.replace('\t', "    ").replace("\r\n", "\n");
         let lexer = zkas::Lexer::new(&filename, source.chars());
-        let tokens = lexer.lex();
+        let tokens = lexer.lex().unwrap();
         let parser = zkas::Parser::new(&filename, source.chars(), tokens);
-        let (namespace, k, constants, witnesses, statements) = parser.parse();
+        let (namespace, k, constants, witnesses, statements) = parser.parse().unwrap();
         let mut analyzer =
             zkas::Analyzer::new(&filename, source.chars(), constants, witnesses, statements);
-        analyzer.analyze_types();
+        analyzer.analyze_types().unwrap();
 
         let compiler = zkas::Compiler::new(
             &filename,
@@ -67,7 +67,7 @@ impl ZkBinary {
             true,
         );
 
-        let bincode = compiler.compile();
+        let bincode = compiler.compile().unwrap();
 
         Self::decode(bincode)
     }

+ 39 - 35
src/zkas/analyzer.rs

@@ -17,7 +17,7 @@
  */
 
 use std::{
-    io::{stdin, stdout, Read, Write},
+    io::{stdin, stdout, Read, Result, Write},
     str::Chars,
 };
 
@@ -52,7 +52,7 @@ impl Analyzer {
         Self { constants, witnesses, statements, literals: vec![], heap: vec![], error }
     }
 
-    pub fn analyze_types(&mut self) {
+    pub fn analyze_types(&mut self) -> Result<()> {
         // To work around the pedantic safety, we'll make new vectors and then
         // replace the `statements` and `heap` vectors from the `Analyzer`
         // object when we are done.
@@ -70,7 +70,7 @@ impl Analyzer {
             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(
+                    return Err(self.error.abort(
                         &format!(
                             "Incorrect number of arguments for statement. Expected {}, got {}.",
                             arg_types.len(),
@@ -78,16 +78,16 @@ impl Analyzer {
                         ),
                         statement.line,
                         1,
-                    );
+                    ))
                 }
             } else {
                 // In case of arrays, check there's at least one element.
                 if statement.rhs.is_empty() {
-                    self.error.abort(
+                    return Err(self.error.abort(
                         "Expected at least one element for statement using arrays.",
                         statement.line,
                         1,
-                    );
+                    ))
                 }
             }
 
@@ -97,18 +97,18 @@ impl Analyzer {
                 Opcode::RangeCheck => {
                     if let Arg::Lit(arg0) = &statement.rhs[0] {
                         if &arg0.name != "64" && &arg0.name != "253" {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 "Supported range checks are only 64 and 253 bits.",
                                 arg0.line,
                                 arg0.column,
-                            );
+                            ))
                         }
                     } else {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             "Invalid argument for range_check opcode.",
                             statement.line,
                             0,
-                        );
+                        ))
                     }
                 }
 
@@ -127,14 +127,14 @@ impl Analyzer {
                 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(
+                        return Err(self.error.abort(
                             &format!(
                                 "Used a function argument which doesn't have a return value: {:?}",
                                 func.opcode
                             ),
                             statement.line,
                             1,
-                        );
+                        ))
                     }
 
                     let v = Variable {
@@ -146,7 +146,7 @@ impl Analyzer {
 
                     if arg_types[0] == VarType::BaseArray {
                         if f_return_types[0] != VarType::Base {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!(
                                     "Function passed as argument returns wrong type. Expected `{:?}`, got `{:?}`.",
                                     VarType::Base,
@@ -154,11 +154,11 @@ impl Analyzer {
                                 ),
                                 v.line,
                                 v.column,
-                            );
+                            ))
                         }
                     } else if arg_types[0] == VarType::ScalarArray {
                         if f_return_types[0] != VarType::Scalar {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!(
                                     "Function passed as argument returns wrong type. Expected `{:?}`, got `{:?}`.",
                                     VarType::Scalar,
@@ -166,10 +166,10 @@ impl Analyzer {
                                 ),
                                 v.line,
                                 v.column,
-                            );
+                            ));
                         }
                     } else if f_return_types[0] != arg_types[idx] {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!(
                                 "Function passed as argument returns wrong type. Expected `{:?}`, got `{:?}`.",
                                 arg_types[idx],
@@ -177,7 +177,7 @@ impl Analyzer {
                             ),
                             v.line,
                             v.column,
-                        );
+                        ))
                     }
 
                     // Replace the statement function call with the variable from
@@ -195,14 +195,14 @@ impl Analyzer {
                                 };
 
                                 if var_type != f_arg_types[inner_idx] {
-                                    self.error.abort(
+                                    return Err(self.error.abort(
                                         &format!(
                                             "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
                                             f_arg_types[inner_idx], var_type
                                         ),
                                         ln,
                                         col,
-                                    );
+                                    ))
                                 }
 
                                 // Apply the proper type.
@@ -213,11 +213,11 @@ impl Analyzer {
                                 continue
                             }
 
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!("Unknown variable reference `{}`.", v.name),
                                 v.line,
                                 v.column,
-                            );
+                            ))
                         } else {
                             unimplemented!()
                         }
@@ -255,14 +255,14 @@ impl Analyzer {
                     // type checking.
                     let var_type = v.typ.to_vartype();
                     if var_type != arg_types[idx] {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!(
                                 "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
                                 arg_types[idx], var_type
                             ),
                             v.line,
                             v.column,
-                        );
+                        ))
                     }
 
                     self.literals.push(v.clone());
@@ -281,7 +281,7 @@ impl Analyzer {
 
                         if arg_types[0] == VarType::BaseArray {
                             if var_type != VarType::Base {
-                                self.error.abort(
+                                return Err(self.error.abort(
                                     &format!(
                                         "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
                                         VarType::Base,
@@ -289,11 +289,11 @@ impl Analyzer {
                                     ),
                                     v.line,
                                     v.column,
-                                );
+                                ))
                             }
                         } else if arg_types[0] == VarType::ScalarArray {
                             if var_type != VarType::Scalar {
-                                self.error.abort(
+                                return Err(self.error.abort(
                                     &format!(
                                         "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
                                         VarType::Scalar,
@@ -301,17 +301,17 @@ impl Analyzer {
                                     ),
                                     v.line,
                                     v.column,
-                                );
+                                ))
                             }
                         } else if var_type != arg_types[idx] && arg_types[idx] != VarType::Any {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!(
                                     "Incorrect argument type. Expected `{:?}`, got `{:?}`.",
                                     arg_types[idx], var_type
                                 ),
                                 v.line,
                                 v.column,
-                            );
+                            ))
                         }
 
                         // Replace Dummy type with correct type.
@@ -321,11 +321,11 @@ impl Analyzer {
                         continue
                     }
 
-                    self.error.abort(
+                    return Err(self.error.abort(
                         &format!("Unknown variable reference `{}`.", v.name),
                         v.line,
                         v.column,
-                    );
+                    ))
                 }
             } // <-- statement.rhs.iter().enumerate()
 
@@ -356,6 +356,8 @@ impl Analyzer {
         //println!("=================STATEMENTS===============\n{:#?}", self.statements);
         //println!("====================HEAP==================\n{:#?}", self.heap);
         //println!("==================LITERALS================\n{:#?}", self.literals);
+
+        Ok(())
     }
 
     fn lookup_var(&self, name: &str) -> Option<Var> {
@@ -404,7 +406,7 @@ impl Analyzer {
         None
     }
 
-    pub fn analyze_semantic(&mut self) {
+    pub fn analyze_semantic(&mut self) -> Result<()> {
         let mut heap = vec![];
 
         println!("Loading constants...\n-----");
@@ -443,11 +445,11 @@ impl Analyzer {
                     if let Some(index) = heap.iter().position(|&r| r == &arg.name) {
                         println!("Found at heap index {}", index);
                     } else {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!("Could not find `{}` on the heap", arg.name),
                             arg.line,
                             arg.column,
-                        );
+                        ))
                     }
                 } else if let Arg::Lit(lit) = arg {
                     println!("Using literal `{}`", lit.name);
@@ -470,6 +472,8 @@ impl Analyzer {
                 _ => unreachable!(),
             }
         }
+
+        Ok(())
     }
 
     fn pause() {

+ 8 - 8
src/zkas/compiler.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::str::Chars;
+use std::{io::Result, str::Chars};
 
 use darkfi_serial::{serialize, VarInt};
 
@@ -63,7 +63,7 @@ impl Compiler {
         Self { namespace, k, constants, witnesses, statements, literals, debug_info, error }
     }
 
-    pub fn compile(&self) -> Vec<u8> {
+    pub fn compile(&self) -> Result<Vec<u8>> {
         let mut bincode = vec![];
 
         // Write the magic bytes and version
@@ -127,11 +127,11 @@ impl Compiler {
                             continue
                         }
 
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!("Failed finding a heap reference for `{}`", arg.name),
                             arg.line,
                             arg.column,
-                        );
+                        ))
                     }
                     Arg::Lit(lit) => {
                         if let Some(found) = Compiler::lookup_literal(&self.literals, &lit.name) {
@@ -140,11 +140,11 @@ impl Compiler {
                             continue
                         }
 
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!("Failed finding literal `{}`", lit.name),
                             lit.line,
                             lit.column,
-                        );
+                        ))
                     }
                     _ => unreachable!(),
                 };
@@ -153,12 +153,12 @@ impl Compiler {
 
         // If we're not doing debug info, we're done here and can return.
         if !self.debug_info {
-            return bincode
+            return Ok(bincode)
         }
 
         // TODO: Otherwise, we proceed appending debug info.
 
-        bincode
+        Ok(bincode)
     }
 
     fn lookup_heap(heap: &[&str], name: &str) -> Option<usize> {

+ 3 - 3
src/zkas/error.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{io, io::Write, process};
+use std::io::{self, Error, ErrorKind, Write};
 
 pub(super) struct ErrorEmitter {
     namespace: String,
@@ -43,10 +43,10 @@ impl ErrorEmitter {
         format!("{}\n{}\n{}\n", err_msg, dbg_msg, caret)
     }
 
-    pub fn abort(&self, msg: &str, ln: usize, col: usize) {
+    pub fn abort(&self, msg: &str, ln: usize, col: usize) -> Error {
         let m = self.fmt(msg.to_string(), ln, col);
         self.emit("error", &m);
-        process::exit(1);
+        Error::new(ErrorKind::Other, m)
     }
 
     pub fn warn(&self, msg: &str, ln: usize, col: usize) {

+ 19 - 10
src/zkas/lexer.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::str::Chars;
+use std::{io::Result, str::Chars};
 
 use super::error::ErrorEmitter;
 
@@ -73,7 +73,7 @@ impl<'a> Lexer<'a> {
         Self { source, error }
     }
 
-    pub fn lex(&self) -> Vec<Token> {
+    pub fn lex(&self) -> Result<Vec<Token>> {
         let mut tokens = vec![];
         let mut lineno = 1;
         let mut column = 0;
@@ -120,11 +120,11 @@ impl<'a> Lexer<'a> {
                 }
 
                 if in_string {
-                    self.error.abort("Strings can't contain newlines", lineno, column);
+                    return Err(self.error.abort("Strings can't contain newlines", lineno, column))
                 }
 
                 if in_number {
-                    self.error.abort("Numbers can't contain newlines", lineno, column);
+                    return Err(self.error.abort("Numbers can't contain newlines", lineno, column))
                 }
 
                 in_comment = false;
@@ -162,7 +162,11 @@ impl<'a> Lexer<'a> {
 
                 if in_string {
                     // For now we forbid whitespace in strings.
-                    self.error.abort("Strings/Namespaces can't contain whitespace", lineno, column);
+                    return Err(self.error.abort(
+                        "Strings/Namespaces can't contain whitespace",
+                        lineno,
+                        column,
+                    ))
                 }
 
                 continue
@@ -204,7 +208,7 @@ impl<'a> Lexer<'a> {
             if in_string && c == '"' {
                 // " I need to fix my vis lexer
                 if buf.is_empty() {
-                    self.error.abort("String cannot be empty", lineno, column);
+                    return Err(self.error.abort("String cannot be empty", lineno, column))
                 }
                 new_string!();
                 continue
@@ -252,14 +256,19 @@ impl<'a> Lexer<'a> {
                         tokens.push(Token::new("=", TokenType::Assign, lineno, column));
                         continue
                     }
-                    _ => self.error.abort(&format!("Invalid token `{}`", c), lineno, column - 1),
+                    _ => {
+                        return Err(self.error.abort(
+                            &format!("Invalid token `{}`", c),
+                            lineno,
+                            column - 1,
+                        ))
+                    }
                 }
-                continue
             }
 
-            self.error.abort(&format!("Invalid token `{}`", c), lineno, column - 1);
+            return Err(self.error.abort(&format!("Invalid token `{}`", c), lineno, column - 1))
         }
 
-        tokens
+        Ok(tokens)
     }
 }

+ 151 - 115
src/zkas/parser.rs

@@ -16,7 +16,9 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{borrow::Borrow, collections::HashMap, hash::Hash, iter::Peekable, str::Chars};
+use std::{
+    borrow::Borrow, collections::HashMap, hash::Hash, io::Result, iter::Peekable, str::Chars,
+};
 
 use super::{
     ast::{Arg, Constant, Literal, Statement, StatementType, Variable, Witness},
@@ -90,6 +92,8 @@ pub struct Parser {
     error: ErrorEmitter,
 }
 
+type Parsed = (String, u32, Vec<Constant>, Vec<Witness>, Vec<Statement>);
+
 impl Parser {
     pub fn new(filename: &str, source: Chars, tokens: Vec<Token>) -> Self {
         // For nice error reporting, we'll load everything into a string
@@ -100,7 +104,7 @@ impl Parser {
         Self { tokens, error }
     }
 
-    pub fn parse(&self) -> (String, u32, Vec<Constant>, Vec<Witness>, Vec<Statement>) {
+    pub fn parse(&self) -> Result<Parsed> {
         // We use these to keep state while parsing.
         let mut namespace = None;
         let (mut declaring_constant, mut declared_constant) = (false, false);
@@ -121,11 +125,11 @@ impl Parser {
         let mut ast = IndexMap::new();
 
         if self.tokens[0].token_type != TokenType::Symbol {
-            self.error.abort(
+            return Err(self.error.abort(
                 "Source file does not start with a section. Expected `constant/witness/circuit`.",
                 0,
                 0,
-            );
+            ))
         }
 
         let mut iter = self.tokens.iter();
@@ -134,8 +138,7 @@ impl Parser {
         // code is the constant "k" which defines 2^k rows that
         // the circuit needs to successfully execute.
         let Some((k, equal, number, semicolon)) = NextTuple4::next_tuple(&mut iter) else {
-            self.error.abort("Source file does not start with k=n;", 0, 0);
-            unreachable!();
+            return Err(self.error.abort("Source file does not start with k=n;", 0, 0))
         };
 
         if k.token_type != TokenType::Symbol ||
@@ -143,26 +146,29 @@ impl Parser {
             number.token_type != TokenType::Number ||
             semicolon.token_type != TokenType::Semicolon
         {
-            self.error.abort("Source file does not start with k=n;", k.line, k.column);
+            return Err(self.error.abort("Source file does not start with k=n;", k.line, k.column))
         }
 
         if k.token != "k" {
-            self.error.abort("Source file does not start with k=n;", k.line, k.column);
+            return Err(self.error.abort("Source file does not start with k=n;", k.line, k.column))
         }
 
         let declared_k = number.token.parse().unwrap();
         if declared_k > MAX_K {
-            self.error.abort(
+            return Err(self.error.abort(
                 &format!("k param is too high, max allowed is {}", MAX_K),
                 number.line,
                 number.column,
-            );
+            ))
         }
 
         // Then we declare the field we're working in.
         let Some((field, equal, field_name, semicolon)) = NextTuple4::next_tuple(&mut iter) else {
-            self.error.abort("Source file does not declare field after k", k.line, k.column);
-            unreachable!();
+            return Err(self.error.abort(
+                "Source file does not declare field after k",
+                k.line,
+                k.column,
+            ))
         };
 
         if field.token_type != TokenType::Symbol ||
@@ -170,30 +176,30 @@ impl Parser {
             field_name.token_type != TokenType::String ||
             semicolon.token_type != TokenType::Semicolon
         {
-            self.error.abort(
+            return Err(self.error.abort(
                 "Source file does not declare field after k",
                 field.line,
                 field.column,
-            );
+            ))
         }
 
         if field.token != "field" {
-            self.error.abort(
+            return Err(self.error.abort(
                 "Source file does not declare field after k",
                 field.line,
                 field.column,
-            );
+            ))
         }
 
         if !ALLOWED_FIELDS.contains(&field_name.token.as_str()) {
-            self.error.abort(
+            return Err(self.error.abort(
                 &format!(
                     "Declared field \"{}\" is not supported. Use any of: {:?}",
                     field_name.token, ALLOWED_FIELDS
                 ),
                 field_name.line,
                 field_name.column,
-            );
+            ))
         }
 
         while let Some(t) = iter.next() {
@@ -215,11 +221,11 @@ impl Parser {
                             if KEYWORDS.contains(&inner.token.as_str()) &&
                                 inner.token_type == TokenType::Symbol
                             {
-                                self.error.abort(
+                                return Err(self.error.abort(
                                     &format!("Keyword '{}' used in improper place.", inner.token),
                                     inner.line,
                                     inner.column,
-                                );
+                                ))
                             }
 
                             $v.push(inner.clone());
@@ -244,11 +250,13 @@ impl Parser {
                         absorb_inner_tokens!(circuit_tokens);
                     }
 
-                    x => self.error.abort(
-                        &format!("Section `{}` is not a valid section", x),
-                        t.line,
-                        t.column,
-                    ),
+                    x => {
+                        return Err(self.error.abort(
+                            &format!("Section `{}` is not a valid section", x),
+                            t.line,
+                            t.column,
+                        ))
+                    }
                 }
             }
 
@@ -258,28 +266,28 @@ impl Parser {
                 ($t:ident) => {
                     if let Some(ns) = namespace.clone() {
                         if ns != $t[0].token {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!("Found '{}' namespace, expected '{}'.", $t[0].token, ns),
                                 $t[0].line,
                                 $t[0].column,
-                            );
+                            ))
                         }
                     } else {
                         if NOPE_NS.contains(&$t[0].token.as_str()) {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!("'{}' cannot be a namespace.", $t[0].token),
                                 $t[0].line,
                                 $t[0].column,
-                            );
+                            ))
                         }
 
                         namespace = Some($t[0].token.clone());
                         if namespace.as_ref().unwrap().as_bytes().len() > MAX_NS_LEN {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!("Namespace too long, max {} bytes", MAX_NS_LEN),
                                 $t[0].line,
                                 $t[0].column,
-                            );
+                            ))
                         }
                     }
                 };
@@ -288,10 +296,14 @@ impl Parser {
             // Parse the constant section into the AST.
             if declaring_constant {
                 if declared_constant {
-                    self.error.abort("Duplicate `constant` section found.", t.line, t.column);
+                    return Err(self.error.abort(
+                        "Duplicate `constant` section found.",
+                        t.line,
+                        t.column,
+                    ))
                 }
 
-                self.check_section_structure("constant", constant_tokens.clone());
+                self.check_section_structure("constant", constant_tokens.clone())?;
                 check_namespace!(constant_tokens);
 
                 let mut constants_map = IndexMap::new();
@@ -299,26 +311,34 @@ impl Parser {
                 let mut constant_inner = constant_tokens[2..constant_tokens.len() - 1].iter();
                 while let Some((typ, name, comma)) = NextTuple3::next_tuple(&mut constant_inner) {
                     if comma.token_type != TokenType::Comma {
-                        self.error.abort("Separator is not a comma.", comma.line, comma.column);
+                        return Err(self.error.abort(
+                            "Separator is not a comma.",
+                            comma.line,
+                            comma.column,
+                        ))
                     }
 
                     // No variable shadowing
                     if constants_map.contains_key(name.token.as_str()) {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!(
                                 "Section `constant` already contains the token `{}`.",
                                 &name.token
                             ),
                             name.line,
                             name.column,
-                        );
+                        ))
                     }
 
                     constants_map.insert(name.token.clone(), (name.clone(), typ.clone()));
                 }
 
                 if constant_inner.next().is_some() {
-                    self.error.abort("Internal error, leftovers in 'constant' iterator", 0, 0);
+                    return Err(self.error.abort(
+                        "Internal error, leftovers in 'constant' iterator",
+                        0,
+                        0,
+                    ))
                 }
 
                 ast_inner.insert("constant".to_string(), constants_map);
@@ -329,10 +349,14 @@ impl Parser {
             // Parse the witness section into the AST.
             if declaring_witness {
                 if declared_witness {
-                    self.error.abort("Duplicate `witness` section found.", t.line, t.column);
+                    return Err(self.error.abort(
+                        "Duplicate `witness` section found.",
+                        t.line,
+                        t.column,
+                    ))
                 }
 
-                self.check_section_structure("witness", witness_tokens.clone());
+                self.check_section_structure("witness", witness_tokens.clone())?;
                 check_namespace!(witness_tokens);
 
                 let mut witnesses_map = IndexMap::new();
@@ -340,26 +364,34 @@ impl Parser {
                 let mut witness_inner = witness_tokens[2..witness_tokens.len() - 1].iter();
                 while let Some((typ, name, comma)) = NextTuple3::next_tuple(&mut witness_inner) {
                     if comma.token_type != TokenType::Comma {
-                        self.error.abort("Separator is not a comma.", comma.line, comma.column);
+                        return Err(self.error.abort(
+                            "Separator is not a comma.",
+                            comma.line,
+                            comma.column,
+                        ))
                     }
 
                     // No variable shadowing
                     if witnesses_map.contains_key(name.token.as_str()) {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!(
                                 "Section `witness` already contains the token `{}`.",
                                 &name.token
                             ),
                             name.line,
                             name.column,
-                        );
+                        ))
                     }
 
                     witnesses_map.insert(name.token.clone(), (name.clone(), typ.clone()));
                 }
 
                 if witness_inner.next().is_some() {
-                    self.error.abort("Internal error, leftovers in 'witness' iterator", 0, 0);
+                    return Err(self.error.abort(
+                        "Internal error, leftovers in 'witness' iterator",
+                        0,
+                        0,
+                    ))
                 }
 
                 ast_inner.insert("witness".to_string(), witnesses_map);
@@ -370,10 +402,14 @@ impl Parser {
             // Parse the circuit section into the AST.
             if declaring_circuit {
                 if declared_circuit {
-                    self.error.abort("Duplicate `circuit` section found.", t.line, t.column);
+                    return Err(self.error.abort(
+                        "Duplicate `circuit` section found.",
+                        t.line,
+                        t.column,
+                    ))
                 }
 
-                self.check_section_structure("circuit", circuit_tokens.clone());
+                self.check_section_structure("circuit", circuit_tokens.clone())?;
                 check_namespace!(circuit_tokens);
 
                 // Grab tokens for each statement
@@ -401,56 +437,54 @@ impl Parser {
             let c = match ast.get(&ns).unwrap().get("constant") {
                 Some(c) => c,
                 None => {
-                    self.error.abort("Missing `constant` section in .zk source.", 0, 0);
-                    unreachable!();
+                    return Err(self.error.abort("Missing `constant` section in .zk source.", 0, 0))
                 }
             };
-            self.parse_ast_constants(c)
+            self.parse_ast_constants(c)?
         };
 
         let witnesses = {
             let c = match ast.get(&ns).unwrap().get("witness") {
                 Some(c) => c,
                 None => {
-                    self.error.abort("Missing `witness` section in .zk source.", 0, 0);
-                    unreachable!();
+                    return Err(self.error.abort("Missing `witness` section in .zk source.", 0, 0))
                 }
             };
-            self.parse_ast_witness(c)
+            self.parse_ast_witness(c)?
         };
 
-        let statements = self.parse_ast_circuit(circuit_stmts);
+        let statements = self.parse_ast_circuit(circuit_stmts)?;
         if statements.is_empty() {
-            self.error.abort("Circuit section is empty.", 0, 0);
+            return Err(self.error.abort("Circuit section is empty.", 0, 0))
         }
 
-        (ns, declared_k, constants, witnesses, statements)
+        Ok((ns, declared_k, constants, witnesses, statements))
     }
 
     /// Routine checks on section structure
-    fn check_section_structure(&self, section: &str, tokens: Vec<Token>) {
+    fn check_section_structure(&self, section: &str, tokens: Vec<Token>) -> Result<()> {
         if tokens[0].token_type != TokenType::String {
-            self.error.abort(
+            return Err(self.error.abort(
                 "Section declaration must start with a naming string.",
                 tokens[0].line,
                 tokens[0].column,
-            );
+            ))
         }
 
         if tokens[1].token_type != TokenType::LeftBrace {
-            self.error.abort(
+            return Err(self.error.abort(
                 "Section must be opened with a left brace '{'",
                 tokens[0].line,
                 tokens[0].column,
-            );
+            ))
         }
 
         if tokens.last().unwrap().token_type != TokenType::RightBrace {
-            self.error.abort(
+            return Err(self.error.abort(
                 "Section must be closed with a right brace '}'",
                 tokens[0].line,
                 tokens[0].column,
-            );
+            ))
         }
 
         match section {
@@ -460,58 +494,60 @@ impl Parser {
                 }
 
                 if tokens[2..tokens.len() - 1].len() % 3 != 0 {
-                    self.error.abort(
+                    return Err(self.error.abort(
                         &format!("Invalid number of elements in '{}' section. Must be pairs of '<Type> <name>' separated with a comma ','.", section),
                         tokens[0].line,
                         tokens[0].column
-                    );
+                    ))
                 }
             }
             "circuit" => {
                 if tokens.len() == 3 {
-                    self.error.abort("circuit section is empty.", 0, 0);
+                    return Err(self.error.abort("circuit section is empty.", 0, 0))
                 }
 
                 if tokens[tokens.len() - 2].token_type != TokenType::Semicolon {
-                    self.error.abort(
+                    return Err(self.error.abort(
                         "Circuit section does not end with a semicolon. Would never finish parsing.",
                         tokens[tokens.len()-2].line,
                         tokens[tokens.len()-2].column,
-                    );
+                    ))
                 }
             }
             _ => unreachable!(),
         };
+
+        Ok(())
     }
 
-    fn parse_ast_constants(&self, ast: &IndexMap<String, (Token, Token)>) -> Vec<Constant> {
+    fn parse_ast_constants(&self, ast: &IndexMap<String, (Token, Token)>) -> Result<Vec<Constant>> {
         let mut ret = vec![];
 
         // k = name
         // v = (name, type)
         for (k, v) in ast.scam_iter() {
             if v.0.token != k {
-                self.error.abort(
+                return Err(self.error.abort(
                     &format!("Constant name `{}` doesn't match token `{}`.", v.0.token, k),
                     v.0.line,
                     v.0.column,
-                );
+                ))
             }
 
             if v.0.token_type != TokenType::Symbol {
-                self.error.abort(
+                return Err(self.error.abort(
                     &format!("Constant name `{}` is not a symbol.", v.0.token),
                     v.0.line,
                     v.0.column,
-                );
+                ))
             }
 
             if v.1.token_type != TokenType::Symbol {
-                self.error.abort(
+                return Err(self.error.abort(
                     &format!("Constant type `{}` is not a symbol.", v.1.token),
                     v.1.line,
                     v.1.column,
-                );
+                ))
             }
 
             // Valid constant types, these are the constants/generators supported
@@ -519,7 +555,7 @@ impl Parser {
             match v.1.token.as_str() {
                 "EcFixedPoint" => {
                     if !VALID_ECFIXEDPOINT.contains(&v.0.token.as_str()) {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!(
                                 "`{}` is not a valid EcFixedPoint constant. Supported: {:?}",
                                 v.0.token.as_str(),
@@ -527,7 +563,7 @@ impl Parser {
                             ),
                             v.0.line,
                             v.0.column,
-                        );
+                        ))
                     }
 
                     ret.push(Constant {
@@ -540,7 +576,7 @@ impl Parser {
 
                 "EcFixedPointShort" => {
                     if !VALID_ECFIXEDPOINTSHORT.contains(&v.0.token.as_str()) {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!(
                                 "`{}` is not a valid EcFixedPointShort constant. Supported: {:?}",
                                 v.0.token.as_str(),
@@ -548,7 +584,7 @@ impl Parser {
                             ),
                             v.0.line,
                             v.0.column,
-                        );
+                        ))
                     }
 
                     ret.push(Constant {
@@ -561,7 +597,7 @@ impl Parser {
 
                 "EcFixedPointBase" => {
                     if !VALID_ECFIXEDPOINTBASE.contains(&v.0.token.as_str()) {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             &format!(
                                 "`{}` is not a valid EcFixedPointBase constant. Supported: {:?}",
                                 v.0.token.as_str(),
@@ -569,7 +605,7 @@ impl Parser {
                             ),
                             v.0.line,
                             v.0.column,
-                        );
+                        ))
                     }
 
                     ret.push(Constant {
@@ -581,46 +617,46 @@ impl Parser {
                 }
 
                 x => {
-                    self.error.abort(
+                    return Err(self.error.abort(
                         &format!("`{}` is an unsupported constant type.", x),
                         v.1.line,
                         v.1.column,
-                    );
+                    ))
                 }
             }
         }
 
-        ret
+        Ok(ret)
     }
 
-    fn parse_ast_witness(&self, ast: &IndexMap<String, (Token, Token)>) -> Vec<Witness> {
+    fn parse_ast_witness(&self, ast: &IndexMap<String, (Token, Token)>) -> Result<Vec<Witness>> {
         let mut ret = vec![];
 
         // k = name
         // v = (name, type)
         for (k, v) in ast.scam_iter() {
             if v.0.token != k {
-                self.error.abort(
+                return Err(self.error.abort(
                     &format!("Witness name `{}` doesn't match token `{}`.", v.0.token, k),
                     v.0.line,
                     v.0.column,
-                );
+                ))
             }
 
             if v.0.token_type != TokenType::Symbol {
-                self.error.abort(
+                return Err(self.error.abort(
                     &format!("Witness name `{}` is not a symbol.", v.0.token),
                     v.0.line,
                     v.0.column,
-                );
+                ))
             }
 
             if v.1.token_type != TokenType::Symbol {
-                self.error.abort(
+                return Err(self.error.abort(
                     &format!("Witness type `{}` is not a symbol.", v.1.token),
                     v.1.line,
                     v.1.column,
-                );
+                ))
             }
 
             // Valid witness types
@@ -689,19 +725,19 @@ impl Parser {
                 }
 
                 x => {
-                    self.error.abort(
+                    return Err(self.error.abort(
                         &format!("`{}` is an unsupported witness type.", x),
                         v.1.line,
                         v.1.column,
-                    );
+                    ))
                 }
             }
         }
 
-        ret
+        Ok(ret)
     }
 
-    fn parse_ast_circuit(&self, statements: Vec<Vec<Token>>) -> Vec<Statement> {
+    fn parse_ast_circuit(&self, statements: Vec<Vec<Token>>) -> Result<Vec<Statement>> {
         // The statement layouts/syntax in the language are as follows:
         //
         // C = poseidon_hash(pub_x, pub_y, value, token, serial);
@@ -770,11 +806,11 @@ impl Parser {
             }
 
             if left_paren != right_paren || (left_paren == 0 || right_paren == 0) {
-                self.error.abort(
+                return Err(self.error.abort(
                     "Incorrect number of left and right parenthesis for statement.",
                     statement[0].line,
                     statement[0].column,
-                );
+                ))
             }
 
             // Peekable iterator so we can see tokens in advance
@@ -818,11 +854,11 @@ impl Parser {
                         }
 
                         if !parsing {
-                            self.error.abort(
+                            return Err(self.error.abort(
                                 &format!("Illegal token `{}`.", next_token.token),
                                 next_token.line,
                                 next_token.column,
-                            );
+                            ))
                         }
                     }
                 }
@@ -843,15 +879,15 @@ impl Parser {
 
                 // TODO: MAKE SURE IT'S A SYMBOL
                 if let Some(op) = Opcode::from_name(func_name) {
-                    let rhs = self.parse_function_call(token, &mut iter);
+                    let rhs = self.parse_function_call(token, &mut iter)?;
                     stmt.opcode = op;
                     stmt.rhs = rhs;
                 } else {
-                    self.error.abort(
+                    return Err(self.error.abort(
                         &format!("Unimplemented opcode `{}`.", func_name),
                         token.line,
                         token.column,
-                    );
+                    ))
                 }
 
                 ret.push(stmt);
@@ -859,26 +895,26 @@ impl Parser {
             }
         }
 
-        ret
+        Ok(ret)
     }
 
     fn parse_function_call(
         &self,
         token: &Token,
         iter: &mut Peekable<std::slice::Iter<'_, Token>>,
-    ) -> Vec<Arg> {
+    ) -> Result<Vec<Arg>> {
         if let Some(next_token) = iter.peek() {
             if next_token.token_type != TokenType::LeftParen {
-                self.error.abort(
+                return Err(self.error.abort(
                     "Invalid function call opening. Must start with a '('.",
                     next_token.line,
                     next_token.column,
-                );
+                ))
             }
             // Skip the opening parenthesis
             iter.next();
         } else {
-            self.error.abort("Premature ending of statement.", token.line, token.column);
+            return Err(self.error.abort("Premature ending of statement.", token.line, token.column))
         }
 
         let mut ret = vec![];
@@ -894,15 +930,15 @@ impl Parser {
             if let Some(op_inner) = Opcode::from_name(&arg.token) {
                 if let Some(paren) = iter.peek() {
                     if paren.token_type != TokenType::LeftParen {
-                        self.error.abort(
+                        return Err(self.error.abort(
                             "Invalid function call opening. Must start with a '('.",
                             paren.line,
                             paren.column,
-                        );
+                        ))
                     }
 
                     // Recurse this function to get the params of the nested one.
-                    let args = self.parse_function_call(arg, iter);
+                    let args = self.parse_function_call(arg, iter)?;
 
                     // Then we assign a "fake" variable that serves as a heap
                     // reference.
@@ -925,11 +961,11 @@ impl Parser {
                     continue
                 }
 
-                self.error.abort(
+                return Err(self.error.abort(
                     "Missing tokens in statement, there's a syntax error here.",
                     arg.line,
                     arg.column,
-                );
+                ))
             }
 
             // ==========================================
@@ -950,11 +986,11 @@ impl Parser {
                         match arg.token.parse::<u64>() {
                             Ok(_) => {}
                             Err(e) => {
-                                self.error.abort(
+                                return Err(self.error.abort(
                                     &format!("Failed to convert literal into u64: {}", e),
                                     arg.line,
                                     arg.column,
-                                );
+                                ))
                             }
                         };
 
@@ -989,16 +1025,16 @@ impl Parser {
                 }
 
                 if sep.token_type != TokenType::Comma {
-                    self.error.abort(
+                    return Err(self.error.abort(
                         "Argument separator is not a comma (`,`)",
                         sep.line,
                         sep.column,
-                    );
+                    ))
                 }
             }
         }
 
-        ret
+        Ok(ret)
     }
 }