mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
zkas: Propagate io::Result instead of aborting on errors.
This commit is contained in:
@@ -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 };
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user