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