| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use std::{
- borrow::Borrow, collections::HashMap, hash::Hash, io::Result, iter::Peekable, str::Chars,
- };
- use super::{
- ast::{Arg, Constant, Literal, Statement, StatementType, Variable, Witness},
- constants::{ALLOWED_FIELDS, MAX_K, MAX_NS_LEN},
- error::ErrorEmitter,
- lexer::{Token, TokenType},
- LitType, Opcode, VarType,
- };
- /// zkas language builtin keywords.
- /// These can not be used anywhere except where they are expected.
- const KEYWORDS: [&str; 5] = ["k", "field", "constant", "witness", "circuit"];
- /// Forbidden namespaces
- const NOPE_NS: [&str; 4] = [".constant", ".literal", ".witness", ".circuit"];
- /// Valid EcFixedPoint constant names supported by the VM.
- const VALID_ECFIXEDPOINT: [&str; 1] = ["VALUE_COMMIT_RANDOM"];
- /// Valid EcFixedPointShort constant names supported by the VM.
- const VALID_ECFIXEDPOINTSHORT: [&str; 1] = ["VALUE_COMMIT_VALUE"];
- /// Valid EcFixedPointBase constant names supported by the VM.
- const VALID_ECFIXEDPOINTBASE: [&str; 1] = ["NULLIFIER_K"];
- #[derive(Clone)]
- struct IndexMap<K, V> {
- pub order: Vec<K>,
- pub map: HashMap<K, V>,
- }
- impl<K, V> IndexMap<K, V> {
- fn new() -> Self {
- Self { order: vec![], map: HashMap::new() }
- }
- }
- impl<K, V> IndexMap<K, V>
- where
- K: Eq + Hash + Send + Sync + Clone + 'static,
- V: Send + Sync + Clone + 'static,
- {
- fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool
- where
- K: Borrow<Q>,
- Q: Hash + Eq,
- {
- self.map.contains_key(k)
- }
- fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
- where
- K: Borrow<Q>,
- Q: Hash + Eq,
- {
- self.map.get(k)
- }
- fn insert(&mut self, k: K, v: V) -> Option<V> {
- self.order.push(k.clone());
- self.map.insert(k, v)
- }
- fn scam_iter(&self) -> Vec<(K, V)> {
- self.order.iter().map(|k| (k.clone(), self.get(k).unwrap().clone())).collect()
- }
- }
- pub struct Parser {
- tokens: Vec<Token>,
- 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
- // vector so we have references to lines.
- let lines: Vec<String> = source.as_str().lines().map(|x| x.to_string()).collect();
- let error = ErrorEmitter::new("Parser", filename, lines);
- Self { tokens, error }
- }
- 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);
- let (mut declaring_witness, mut declared_witness) = (false, false);
- let (mut declaring_circuit, mut declared_circuit) = (false, false);
- // The tokens gathered from each of the sections
- let mut constant_tokens = vec![];
- let mut witness_tokens = vec![];
- let mut circuit_tokens = vec![];
- // Tokens belonging to the current statement
- let mut circuit_stmt = vec![];
- // All completed statements are pushed here
- let mut circuit_stmts = vec![];
- // Contains constant and witness sections
- let mut ast_inner = IndexMap::new();
- let mut ast = IndexMap::new();
- if self.tokens[0].token_type != TokenType::Symbol {
- 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();
- // The first thing that has to be declared in the source
- // 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 {
- return Err(self.error.abort("Source file does not start with k=n;", 0, 0))
- };
- if k.token_type != TokenType::Symbol ||
- equal.token_type != TokenType::Assign ||
- number.token_type != TokenType::Number ||
- semicolon.token_type != TokenType::Semicolon
- {
- return Err(self.error.abort("Source file does not start with k=n;", k.line, k.column))
- }
- if k.token != "k" {
- 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 {
- 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 {
- return Err(self.error.abort(
- "Source file does not declare field after k",
- k.line,
- k.column,
- ))
- };
- if field.token_type != TokenType::Symbol ||
- equal.token_type != TokenType::Assign ||
- field_name.token_type != TokenType::String ||
- semicolon.token_type != TokenType::Semicolon
- {
- return Err(self.error.abort(
- "Source file does not declare field after k",
- field.line,
- field.column,
- ))
- }
- if field.token != "field" {
- 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()) {
- 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() {
- // Sections "constant", "witness", and "circuit" are
- // the sections we must be declaring in our source code.
- // When we find one, we'll take all the tokens found in
- // the section and place them in their respective vec.
- // NOTE: Currently this logic depends on the fact that
- // the sections are closed off with braces. This should
- // be revisited later when we decide to add other lang
- // functionality that also depends on using braces.
- if !declaring_constant && !declaring_witness && !declaring_circuit {
- //
- // We use this macro to avoid code repetition in the following
- // match statement for soaking up the section tokens.
- macro_rules! absorb_inner_tokens {
- ($v:ident) => {
- for inner in iter.by_ref() {
- if KEYWORDS.contains(&inner.token.as_str()) &&
- inner.token_type == TokenType::Symbol
- {
- return Err(self.error.abort(
- &format!("Keyword '{}' used in improper place.", inner.token),
- inner.line,
- inner.column,
- ))
- }
- $v.push(inner.clone());
- if inner.token_type == TokenType::RightBrace {
- break
- }
- }
- };
- }
- match t.token.as_str() {
- "constant" => {
- declaring_constant = true;
- absorb_inner_tokens!(constant_tokens);
- }
- "witness" => {
- declaring_witness = true;
- absorb_inner_tokens!(witness_tokens);
- }
- "circuit" => {
- declaring_circuit = true;
- absorb_inner_tokens!(circuit_tokens);
- }
- x => {
- return Err(self.error.abort(
- &format!("Section `{}` is not a valid section", x),
- t.line,
- t.column,
- ))
- }
- }
- }
- // We use this macro to set or check that the namespace of all sections
- // is the same and no stray strings appeared.
- macro_rules! check_namespace {
- ($t:ident) => {
- if let Some(ns) = namespace.clone() {
- if ns != $t[0].token {
- 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()) {
- 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 {
- return Err(self.error.abort(
- &format!("Namespace too long, max {} bytes", MAX_NS_LEN),
- $t[0].line,
- $t[0].column,
- ))
- }
- }
- };
- }
- // Parse the constant section into the AST.
- if declaring_constant {
- if declared_constant {
- return Err(self.error.abort(
- "Duplicate `constant` section found.",
- t.line,
- t.column,
- ))
- }
- self.check_section_structure("constant", constant_tokens.clone())?;
- check_namespace!(constant_tokens);
- let mut constants_map = IndexMap::new();
- // This is everything between the braces: { ... }
- 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 {
- 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()) {
- 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() {
- return Err(self.error.abort(
- "Internal error, leftovers in 'constant' iterator",
- 0,
- 0,
- ))
- }
- ast_inner.insert("constant".to_string(), constants_map);
- declaring_constant = false;
- declared_constant = true;
- }
- // Parse the witness section into the AST.
- if declaring_witness {
- if declared_witness {
- return Err(self.error.abort(
- "Duplicate `witness` section found.",
- t.line,
- t.column,
- ))
- }
- self.check_section_structure("witness", witness_tokens.clone())?;
- check_namespace!(witness_tokens);
- let mut witnesses_map = IndexMap::new();
- // This is everything between the braces: { ... }
- 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 {
- 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()) {
- 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() {
- return Err(self.error.abort(
- "Internal error, leftovers in 'witness' iterator",
- 0,
- 0,
- ))
- }
- ast_inner.insert("witness".to_string(), witnesses_map);
- declaring_witness = false;
- declared_witness = true;
- }
- // Parse the circuit section into the AST.
- if declaring_circuit {
- if declared_circuit {
- return Err(self.error.abort(
- "Duplicate `circuit` section found.",
- t.line,
- t.column,
- ))
- }
- self.check_section_structure("circuit", circuit_tokens.clone())?;
- check_namespace!(circuit_tokens);
- // Grab tokens for each statement
- for i in circuit_tokens[2..circuit_tokens.len() - 1].iter() {
- if i.token_type == TokenType::Semicolon {
- // Push completed statement to the heap
- circuit_stmts.push(circuit_stmt.clone());
- circuit_stmt = vec![];
- continue
- }
- circuit_stmt.push(i.clone());
- }
- declaring_circuit = false;
- declared_circuit = true;
- }
- }
- // Tokens have been processed and ast is complete
- let ns = namespace.unwrap();
- ast.insert(ns.clone(), ast_inner);
- let constants = {
- let c = match ast.get(&ns).unwrap().get("constant") {
- Some(c) => c,
- None => {
- return Err(self.error.abort("Missing `constant` section in .zk source.", 0, 0))
- }
- };
- self.parse_ast_constants(c)?
- };
- let witnesses = {
- let c = match ast.get(&ns).unwrap().get("witness") {
- Some(c) => c,
- None => {
- return Err(self.error.abort("Missing `witness` section in .zk source.", 0, 0))
- }
- };
- self.parse_ast_witness(c)?
- };
- let statements = self.parse_ast_circuit(circuit_stmts)?;
- if statements.is_empty() {
- return Err(self.error.abort("Circuit section is empty.", 0, 0))
- }
- Ok((ns, declared_k, constants, witnesses, statements))
- }
- /// Routine checks on section structure
- fn check_section_structure(&self, section: &str, tokens: Vec<Token>) -> Result<()> {
- if tokens[0].token_type != TokenType::String {
- 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 {
- 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 {
- return Err(self.error.abort(
- "Section must be closed with a right brace '}'",
- tokens[0].line,
- tokens[0].column,
- ))
- }
- match section {
- "constant" | "witness" => {
- if tokens.len() == 3 {
- self.error.warn(&format!("{} section is empty.", section), 0, 0);
- }
- if tokens[2..tokens.len() - 1].len() % 3 != 0 {
- 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 {
- return Err(self.error.abort("circuit section is empty.", 0, 0))
- }
- if tokens[tokens.len() - 2].token_type != TokenType::Semicolon {
- 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)>) -> Result<Vec<Constant>> {
- let mut ret = vec![];
- // k = name
- // v = (name, type)
- for (k, v) in ast.scam_iter() {
- if v.0.token != k {
- 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 {
- 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 {
- 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
- // in `src/crypto/constants.rs` and `src/crypto/constants/`.
- match v.1.token.as_str() {
- "EcFixedPoint" => {
- if !VALID_ECFIXEDPOINT.contains(&v.0.token.as_str()) {
- return Err(self.error.abort(
- &format!(
- "`{}` is not a valid EcFixedPoint constant. Supported: {:?}",
- v.0.token.as_str(),
- VALID_ECFIXEDPOINT
- ),
- v.0.line,
- v.0.column,
- ))
- }
- ret.push(Constant {
- name: k.to_string(),
- typ: VarType::EcFixedPoint,
- line: v.1.line,
- column: v.1.column,
- });
- }
- "EcFixedPointShort" => {
- if !VALID_ECFIXEDPOINTSHORT.contains(&v.0.token.as_str()) {
- return Err(self.error.abort(
- &format!(
- "`{}` is not a valid EcFixedPointShort constant. Supported: {:?}",
- v.0.token.as_str(),
- VALID_ECFIXEDPOINTSHORT
- ),
- v.0.line,
- v.0.column,
- ))
- }
- ret.push(Constant {
- name: k.to_string(),
- typ: VarType::EcFixedPointShort,
- line: v.1.line,
- column: v.1.column,
- });
- }
- "EcFixedPointBase" => {
- if !VALID_ECFIXEDPOINTBASE.contains(&v.0.token.as_str()) {
- return Err(self.error.abort(
- &format!(
- "`{}` is not a valid EcFixedPointBase constant. Supported: {:?}",
- v.0.token.as_str(),
- VALID_ECFIXEDPOINTBASE
- ),
- v.0.line,
- v.0.column,
- ))
- }
- ret.push(Constant {
- name: k.to_string(),
- typ: VarType::EcFixedPointBase,
- line: v.1.line,
- column: v.1.column,
- });
- }
- x => {
- return Err(self.error.abort(
- &format!("`{}` is an unsupported constant type.", x),
- v.1.line,
- v.1.column,
- ))
- }
- }
- }
- Ok(ret)
- }
- 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 {
- 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 {
- 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 {
- return Err(self.error.abort(
- &format!("Witness type `{}` is not a symbol.", v.1.token),
- v.1.line,
- v.1.column,
- ))
- }
- // Valid witness types
- match v.1.token.as_str() {
- "EcPoint" => {
- ret.push(Witness {
- name: k.to_string(),
- typ: VarType::EcPoint,
- line: v.0.line,
- column: v.0.column,
- });
- }
- "EcNiPoint" => {
- ret.push(Witness {
- name: k.to_string(),
- typ: VarType::EcNiPoint,
- line: v.0.line,
- column: v.0.column,
- });
- }
- "Base" => {
- ret.push(Witness {
- name: k.to_string(),
- typ: VarType::Base,
- line: v.0.line,
- column: v.0.column,
- });
- }
- "Scalar" => {
- ret.push(Witness {
- name: k.to_string(),
- typ: VarType::Scalar,
- line: v.0.line,
- column: v.0.column,
- });
- }
- "MerklePath" => {
- ret.push(Witness {
- name: k.to_string(),
- typ: VarType::MerklePath,
- line: v.0.line,
- column: v.0.column,
- });
- }
- "Uint32" => {
- ret.push(Witness {
- name: k.to_string(),
- typ: VarType::Uint32,
- line: v.0.line,
- column: v.0.column,
- });
- }
- "Uint64" => {
- ret.push(Witness {
- name: k.to_string(),
- typ: VarType::Uint64,
- line: v.0.line,
- column: v.0.column,
- });
- }
- x => {
- return Err(self.error.abort(
- &format!("`{}` is an unsupported witness type.", x),
- v.1.line,
- v.1.column,
- ))
- }
- }
- }
- Ok(ret)
- }
- 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);
- // | | | | |
- // V V V V V
- // variable opcode arg arg
- // assign
- //
- // constrain_instance(C);
- // | |
- // V V
- // opcode arg
- //
- // inner opcode arg
- // |
- // constrain_instance(ec_get_x(foo));
- // | |
- // V V
- // opcode arg as opcode
- //
- // In the latter, we want to support nested function calls, e.g.:
- //
- // constrain_instance(ec_get_x(token_commit));
- //
- // The inner call's result would still get pushed on the heap,
- // but it will not be accessible in any other scope.
- //
- // In certain opcodes, we also support literal types, and the
- // opcodes can return a variable type after running the operation.
- // e.g.
- // one = witness_base(1);
- // zero = witness_base(0);
- //
- // The literal type is used only in the function call's scope, but
- // the result is then accessible on the heap to be used by further
- // computation.
- //
- // Regarding multiple return values from opcodes, this is perhaps
- // not necessary for the current language scope, as this is a low
- // level representation. Note that it could be relatively easy to
- // modify the parsing logic to support that here. For now we'll
- // defer it, and if at some point we decide that the language is
- // too expressive and noisy, we'll consider having multiple return
- // types. It also very much depends on the type of functions/opcodes
- // that we want to support.
- // Vec of statements to return from this entire parsing operation.
- let mut ret = vec![];
- // Here, our statements tokens have been parsed and delimited by
- // semicolons (;) in the source file. This iterator contains each
- // of those statements as an array of tokens we then consume and
- // build the AST further.
- for statement in statements {
- if statement.is_empty() {
- continue
- }
- let (mut left_paren, mut right_paren) = (0, 0);
- for i in &statement {
- match i.token.as_str() {
- "(" => left_paren += 1,
- ")" => right_paren += 1,
- _ => {}
- }
- }
- if left_paren != right_paren || (left_paren == 0 || right_paren == 0) {
- 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
- // without consuming the iterator.
- let mut iter = statement.iter().peekable();
- // Dummy statement that we'll hopefully fill now.
- let mut stmt = Statement::default();
- let mut parsing = false;
- while let Some(token) = iter.next() {
- if !parsing {
- // TODO: MAKE SURE IT'S A SYMBOL
- // This logic must be changed if we want to support
- // multiple return values.
- if let Some(next_token) = iter.peek() {
- if next_token.token_type == TokenType::Assign {
- stmt.line = token.line;
- stmt.typ = StatementType::Assign;
- stmt.rhs = vec![];
- stmt.lhs = Some(Variable {
- name: token.token.clone(),
- typ: VarType::Dummy,
- line: token.line,
- column: token.column,
- });
- // Skip over the `=` token.
- iter.next();
- parsing = true;
- continue
- }
- if next_token.token_type == TokenType::LeftParen {
- stmt.line = token.line;
- stmt.typ = StatementType::Call;
- stmt.rhs = vec![];
- stmt.lhs = None;
- parsing = true;
- }
- if !parsing {
- return Err(self.error.abort(
- &format!("Illegal token `{}`.", next_token.token),
- next_token.line,
- next_token.column,
- ))
- }
- }
- }
- // If parsing == true, we now know if we're making a variable
- // assignment or a function call without a return value.
- // Let's dig deeper to see what the statement's call is, and
- // what it contains as arguments. With this we'll fill `rhs`.
- // The arguments could be literal types, other variables, or
- // even nested function calls.
- // For now, we don't care if the params are valid, as this is
- // the job of the semantic analyzer which comes after the
- // parsing module.
- // The assumption here is that the current token is a function
- // call, so we check if it's legit and start digging.
- let func_name = token.token.as_str();
- // 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)?;
- stmt.opcode = op;
- stmt.rhs = rhs;
- } else {
- return Err(self.error.abort(
- &format!("Unimplemented opcode `{}`.", func_name),
- token.line,
- token.column,
- ))
- }
- // At this stage of parsing, we should have assigned `stmt` a StatementType that is
- // not a Noop. If we have failed to do so, we cannot proceed because Nooops must
- // never be pased to the compiler. This can occur when multiple independent
- // statements are passed on one line, or if a statement is not terminated by a
- // semicolon.
- if stmt.typ == StatementType::Noop {
- return Err(self.error.abort(
- "Statement is a NOOP; not allowed. (Did you miss a semicolon?)",
- token.line,
- token.column,
- ))
- }
- ret.push(stmt);
- stmt = Statement::default();
- }
- }
- Ok(ret)
- }
- fn parse_function_call(
- &self,
- token: &Token,
- iter: &mut Peekable<std::slice::Iter<'_, Token>>,
- ) -> Result<Vec<Arg>> {
- if let Some(next_token) = iter.peek() {
- if next_token.token_type != TokenType::LeftParen {
- 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 {
- return Err(self.error.abort("Premature ending of statement.", token.line, token.column))
- }
- let mut ret = vec![];
- // The next element in the iter now hopefully contains an opcode
- // argument. If it's another opcode, we'll recurse into this
- // function's logic.
- // Otherwise, we look for variable and literal types.
- while let Some(arg) = iter.next() {
- // ============================
- // Parse a nested function call
- // ============================
- if let Some(op_inner) = Opcode::from_name(&arg.token) {
- if let Some(paren) = iter.peek() {
- if paren.token_type != TokenType::LeftParen {
- 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)?;
- // Then we assign a "fake" variable that serves as a heap
- // reference.
- let var = Variable {
- name: format!("_op_inner_{}_{}", arg.line, arg.column),
- typ: VarType::Dummy,
- line: arg.line,
- column: arg.column,
- };
- let arg = Arg::Func(Statement {
- typ: StatementType::Assign,
- opcode: op_inner,
- lhs: Some(var),
- rhs: args,
- line: arg.line,
- });
- ret.push(arg);
- continue
- }
- return Err(self.error.abort(
- "Missing tokens in statement, there's a syntax error here.",
- arg.line,
- arg.column,
- ))
- }
- // ==========================================
- // Parse normal argument, not a function call
- // ==========================================
- if let Some(sep) = iter.next() {
- // See if we have a variable or a literal type.
- match arg.token_type {
- TokenType::Symbol => ret.push(Arg::Var(Variable {
- name: arg.token.clone(),
- typ: VarType::Dummy,
- line: arg.line,
- column: arg.column,
- })),
- TokenType::Number => {
- // Check if we can actually convert this into a number.
- match arg.token.parse::<u64>() {
- Ok(_) => {}
- Err(e) => {
- return Err(self.error.abort(
- &format!("Failed to convert literal into u64: {}", e),
- arg.line,
- arg.column,
- ))
- }
- };
- ret.push(Arg::Lit(Literal {
- name: arg.token.clone(),
- typ: LitType::Uint64,
- line: arg.line,
- column: arg.column,
- }))
- }
- TokenType::RightParen => {
- if let Some(comma) = iter.peek() {
- if comma.token_type == TokenType::Comma {
- iter.next();
- }
- }
- break
- }
- x => unimplemented!("{:#?}", x),
- };
- if sep.token_type == TokenType::RightParen {
- if let Some(comma) = iter.peek() {
- if comma.token_type == TokenType::Comma {
- iter.next();
- }
- }
- // Reached end of args
- break
- }
- if sep.token_type != TokenType::Comma {
- return Err(self.error.abort(
- "Argument separator is not a comma (`,`)",
- sep.line,
- sep.column,
- ))
- }
- }
- }
- Ok(ret)
- }
- }
- trait NextTuple3<I>: Iterator<Item = I> {
- fn next_tuple(&mut self) -> Option<(I, I, I)>;
- }
- impl<I: Iterator<Item = T>, T> NextTuple3<T> for I {
- fn next_tuple(&mut self) -> Option<(T, T, T)> {
- let a = self.next()?;
- let b = self.next()?;
- let c = self.next()?;
- Some((a, b, c))
- }
- }
- trait NextTuple4<I>: Iterator<Item = I> {
- fn next_tuple(&mut self) -> Option<(I, I, I, I)>;
- }
- impl<I: Iterator<Item = T>, T> NextTuple4<T> for I {
- fn next_tuple(&mut self) -> Option<(T, T, T, T)> {
- let a = self.next()?;
- let b = self.next()?;
- let c = self.next()?;
- let d = self.next()?;
- Some((a, b, c, d))
- }
- }
|