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

This commit is contained in:
parazyd
2023-08-23 23:42:57 +02:00
parent 00e4456787
commit 576a20e057
7 changed files with 241 additions and 181 deletions

View File

@@ -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 };

View File

@@ -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)
}

View File

@@ -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() {

View File

@@ -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> {

View File

@@ -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) {

View File

@@ -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)
}
}

View File

@@ -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)
}
}