Explorar el Código

zkas/AST: Proper parsing of constant/contract.

The design we are moving towards is a composition of functions that
consume ASTs and produce modified ASTs.
parazyd hace 4 años
padre
commit
bda7ad6c06
Se han modificado 3 ficheros con 79 adiciones y 13 borrados
  1. 12 0
      zkas/src/error.rs
  2. 2 2
      zkas/src/lexer.rs
  3. 65 11
      zkas/src/parser.rs

+ 12 - 0
zkas/src/error.rs

@@ -65,6 +65,18 @@ impl ParserError {
         ParserError::parser_error(&msg);
     }
 
+    pub fn declaration_already_contains_token(&self, s: &str, t: &str, ln: usize, col: usize) {
+        let err_msg = format!(
+            "`{}` section declaration already contains token `{}` on line {} (column {})",
+            s, t, ln, col
+        );
+        let dbg_msg = format!("{}:{}:{}: {}", self.file, ln, col, self.lines[ln - 1]);
+        let pad = dbg_msg.split(": ").next().unwrap().len() + col + 2;
+        let caret = format!("{:width$}^", "", width = pad);
+        let msg = format!("{}\n{}\n{}", err_msg, dbg_msg, caret);
+        ParserError::parser_error(&msg);
+    }
+
     pub fn separator_not_a_comma(&self, ln: usize, col: usize) {
         let err_msg = format!("Invalid separator on line {} (column {})", ln, col);
         let err_msg = format!("{}\nShould be a comma `,`", err_msg);

+ 2 - 2
zkas/src/lexer.rs

@@ -2,7 +2,7 @@ use std::str::Chars;
 
 use crate::error::LexerError;
 
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Hash, Eq, PartialEq, Clone, Debug)]
 pub enum TokenType {
     Symbol,
     String,
@@ -18,7 +18,7 @@ pub enum TokenType {
 
 const SPECIAL_CHARS: [char; 7] = ['{', '}', '(', ')', ',', ';', '='];
 
-#[derive(Clone, Debug)]
+#[derive(Hash, Eq, PartialEq, Clone, Debug)]
 pub struct Token {
     pub token: String,
     pub token_type: TokenType,

+ 65 - 11
zkas/src/parser.rs

@@ -1,4 +1,7 @@
-use std::str::Chars;
+use std::{
+    collections::{hash_map, HashMap},
+    str::Chars,
+};
 
 use itertools::Itertools;
 
@@ -22,6 +25,9 @@ pub fn parse(filename: &str, source: Chars, tokens: Vec<Token>) {
     let mut contract_tokens = vec![];
     let mut circuit_tokens = vec![];
 
+    let mut ast: HashMap<String, Vec<HashMap<String, HashMap<String, (Token, Token)>>>> =
+        HashMap::new();
+
     let mut iter = tokens.iter();
     while let Some(t) = iter.next() {
         // Start by declaring a section
@@ -37,7 +43,7 @@ pub fn parse(filename: &str, source: Chars, tokens: Vec<Token>) {
                 "constant" => {
                     declaring_constant = true;
                     for inner in iter.by_ref() {
-                        constant_tokens.push(inner);
+                        constant_tokens.push(inner.clone());
                         if inner.token_type == TokenType::RightBrace {
                             break
                         }
@@ -47,7 +53,7 @@ pub fn parse(filename: &str, source: Chars, tokens: Vec<Token>) {
                 "contract" => {
                     declaring_contract = true;
                     for inner in iter.by_ref() {
-                        contract_tokens.push(inner);
+                        contract_tokens.push(inner.clone());
                         if inner.token_type == TokenType::RightBrace {
                             break
                         }
@@ -57,7 +63,7 @@ pub fn parse(filename: &str, source: Chars, tokens: Vec<Token>) {
                 "circuit" => {
                     declaring_circuit = true;
                     for inner in iter.by_ref() {
-                        circuit_tokens.push(inner);
+                        circuit_tokens.push(inner.clone());
                         if inner.token_type == TokenType::RightBrace {
                             break
                         }
@@ -90,14 +96,37 @@ pub fn parse(filename: &str, source: Chars, tokens: Vec<Token>) {
                 );
             }
 
-            let mut constants = vec![];
+            let namespace = constant_tokens[0].token.clone();
+            let mut constants_map = HashMap::new();
 
-            let mut constants_inner = constant_tokens[2..constant_tokens.len() - 1].iter();
+            let constants_cloned = constant_tokens.clone();
+            let mut constants_inner = constants_cloned[2..constant_tokens.len() - 1].iter();
             while let Some((typ, name, comma)) = constants_inner.next_tuple() {
                 if comma.token_type != TokenType::Comma {
                     parser_error.separator_not_a_comma(comma.line, comma.column);
                 }
-                constants.push((typ, name));
+
+                if constants_map.contains_key(name.token.as_str()) {
+                    parser_error.declaration_already_contains_token(
+                        "constant",
+                        &name.token,
+                        name.line,
+                        name.column,
+                    );
+                }
+
+                constants_map.insert(name.token.clone(), (name.clone(), typ.clone()));
+            }
+
+            let mut c_map = HashMap::new();
+            c_map.insert("constant".to_string(), constants_map);
+
+            if let hash_map::Entry::Vacant(e) = ast.entry(namespace.clone()) {
+                let v = vec![c_map];
+                e.insert(v);
+            } else {
+                let v = ast.get_mut(&namespace).unwrap();
+                v.push(c_map);
             }
 
             declaring_constant = false;
@@ -113,14 +142,37 @@ pub fn parse(filename: &str, source: Chars, tokens: Vec<Token>) {
                 );
             }
 
-            let mut contract = vec![];
+            let namespace = contract_tokens[0].token.clone();
+            let mut contract_map = HashMap::new();
 
-            let mut contract_inner = contract_tokens[2..contract_tokens.len() - 1].iter();
+            let contract_cloned = contract_tokens.clone();
+            let mut contract_inner = contract_cloned[2..contract_tokens.len() - 1].iter();
             while let Some((typ, name, comma)) = contract_inner.next_tuple() {
                 if comma.token_type != TokenType::Comma {
                     parser_error.separator_not_a_comma(comma.line, comma.column);
                 }
-                contract.push((typ, name));
+
+                if contract_map.contains_key(&name.token) {
+                    parser_error.declaration_already_contains_token(
+                        "contract",
+                        &name.token,
+                        name.line,
+                        name.column,
+                    );
+                }
+
+                contract_map.insert(name.token.clone(), (name.clone(), typ.clone()));
+            }
+
+            let mut c_map = HashMap::new();
+            c_map.insert("contract".to_string(), contract_map);
+
+            if let hash_map::Entry::Vacant(e) = ast.entry(namespace.clone()) {
+                let v = vec![c_map];
+                e.insert(v);
+            } else {
+                let v = ast.get_mut(&namespace).unwrap();
+                v.push(c_map);
             }
 
             declaring_contract = false;
@@ -129,10 +181,12 @@ pub fn parse(filename: &str, source: Chars, tokens: Vec<Token>) {
         if declaring_circuit {
             declaring_circuit = false;
         }
+
+        println!("{:#?}", ast);
     }
 }
 
-fn check_section_structure(tokens: Vec<&Token>) -> Option<&str> {
+fn check_section_structure(tokens: Vec<Token>) -> Option<&'static str> {
     if tokens[0].token_type != TokenType::String {
         return Some("Section declaration must start with a naming string.")
     }