Przeglądaj źródła

Use IdentifierPath instead of Expression in solang-parser

Signed-off-by: Julian Popescu <hi@julian.dev>
Julian Popescu 3 lat temu
rodzic
commit
c63b552373

+ 32 - 5
solang-parser/src/pt.rs

@@ -83,6 +83,33 @@ pub struct Identifier {
     pub name: String,
 }
 
+impl Display for Identifier {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(&self.name)
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct IdentifierPath {
+    pub loc: Loc,
+    pub identifiers: Vec<Identifier>,
+}
+
+impl Display for IdentifierPath {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if let Some(ident) = self.identifiers.get(0) {
+            ident.fmt(f)?;
+        } else {
+            return Ok(());
+        }
+        for ident in self.identifiers[1..].iter() {
+            f.write_str(".")?;
+            ident.fmt(f)?;
+        }
+        Ok(())
+    }
+}
+
 #[derive(Debug, PartialEq, Clone)]
 pub enum Comment {
     Line(Loc, String),
@@ -210,8 +237,8 @@ pub enum ContractPart {
 
 #[derive(Debug, PartialEq, Clone)]
 pub enum UsingList {
-    Library(Expression),
-    Functions(Vec<Expression>),
+    Library(IdentifierPath),
+    Functions(Vec<IdentifierPath>),
 }
 
 #[derive(Debug, PartialEq, Clone)]
@@ -244,7 +271,7 @@ impl fmt::Display for ContractTy {
 #[derive(Debug, PartialEq, Clone)]
 pub struct Base {
     pub loc: Loc,
-    pub name: Expression,
+    pub name: IdentifierPath,
     pub args: Option<Vec<Expression>>,
 }
 
@@ -635,8 +662,8 @@ pub enum Statement {
     Continue(Loc),
     Break(Loc),
     Return(Loc, Option<Expression>),
-    Revert(Loc, Option<Expression>, Vec<Expression>),
-    RevertNamedArgs(Loc, Option<Expression>, Vec<NamedArgument>),
+    Revert(Loc, Option<IdentifierPath>, Vec<Expression>),
+    RevertNamedArgs(Loc, Option<IdentifierPath>, Vec<NamedArgument>),
     Emit(Loc, Expression),
     Try(
         Loc,

+ 10 - 9
solang-parser/src/solidity.lalrpop

@@ -130,10 +130,11 @@ SolNoErrorIdentifier: Identifier = {
     <l:@L> "default" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "default".to_string()},
 }
 
-SolIdentifierNamespace: Expression = {
-    SolIdentifier => Expression::Variable(<>),
-    <l:@L> <e:SolIdentifierNamespace> "." <id:SolIdentifier> <r:@R> => {
-        Expression::MemberAccess(Loc::File(file_no, l, r), Box::new(e), id)
+SolIdentifierPath: IdentifierPath = {
+    <l:@L> <e:SolIdentifier> <v:("." <SolIdentifier>)*> <r:@R> => {
+        let mut v = v;
+        v.insert(0, e);
+        IdentifierPath { loc: Loc::File(file_no, l, r), identifiers: v }
     }
 }
 
@@ -179,7 +180,7 @@ Bases: Vec<Base> = {
 }
 
 Base: Base = {
-    <l:@L> <name:SolIdentifierNamespace> <args:("(" <Comma<Expression>> ")")?> <r:@R> => Base {
+    <l:@L> <name:SolIdentifierPath> <args:("(" <Comma<Expression>> ")")?> <r:@R> => Base {
         loc: Loc::File(file_no, l, r),
         name,
         args
@@ -638,8 +639,8 @@ Using: Box<Using> = {
 }
 
 UsingList: UsingList = {
-    SolIdentifierNamespace => UsingList::Library(<>),
-    "{" <Comma<SolIdentifierNamespace>> "}" => UsingList::Functions(<>),
+    SolIdentifierPath => UsingList::Library(<>),
+    "{" <Comma<SolIdentifierPath>> "}" => UsingList::Functions(<>),
 }
 
 BlockStatement: Statement = {
@@ -749,10 +750,10 @@ NonIfStatement: Statement = {
     <l:@L> "emit" <ty:FunctionCall> <r:@R> ";" => {
         Statement::Emit(Loc::File(file_no, l, r), ty)
     },
-    <l:@L> "revert" <error:SolIdentifierNamespace?> "(" <v:Comma<Expression>> ")" <r:@R> ";" => {
+    <l:@L> "revert" <error:SolIdentifierPath?> "(" <v:Comma<Expression>> ")" <r:@R> ";" => {
         Statement::Revert(Loc::File(file_no, l, r), error, v)
     },
-    <l:@L> "revert" <error:SolIdentifierNamespace?> "(" "{" <v:Comma<NamedArgument>> "}" ")" <r:@R> ";" => {
+    <l:@L> "revert" <error:SolIdentifierPath?> "(" "{" <v:Comma<NamedArgument>> "}" ")" <r:@R> ";" => {
         Statement::RevertNamedArgs(Loc::File(file_no, l, r), error, v)
     },
 }

+ 6 - 3
solang-parser/src/test.rs

@@ -906,10 +906,13 @@ fn parse_revert_test() {
                         unchecked: false,
                         statements: vec![Statement::Revert(
                             Loc::File(0, 107, 125),
-                            Some(Expression::Variable(Identifier {
+                            Some(IdentifierPath {
                                 loc: Loc::File(0, 114, 123),
-                                name: "BAR_ERROR".to_string(),
-                            })),
+                                identifiers: vec![Identifier {
+                                    loc: Loc::File(0, 114, 123),
+                                    name: "BAR_ERROR".to_string(),
+                                }],
+                            }),
                             vec![],
                         )],
                     }),

+ 5 - 5
src/sema/contracts.rs

@@ -129,7 +129,7 @@ pub fn resolve_base_contracts(
             if let Ok(no) = ns.resolve_contract_with_namespace(file_no, name, &mut diagnostics) {
                 if no == *contract_no {
                     ns.diagnostics.push(ast::Diagnostic::error(
-                        name.loc(),
+                        name.loc,
                         format!("contract '{}' cannot have itself as a base contract", name),
                     ));
                 } else if ns.contracts[*contract_no]
@@ -138,7 +138,7 @@ pub fn resolve_base_contracts(
                     .any(|e| e.contract_no == no)
                 {
                     ns.diagnostics.push(ast::Diagnostic::error(
-                        name.loc(),
+                        name.loc,
                         format!(
                             "contract '{}' duplicate base '{}'",
                             ns.contracts[*contract_no].name, name
@@ -146,7 +146,7 @@ pub fn resolve_base_contracts(
                     ));
                 } else if is_base(*contract_no, no, ns) {
                     ns.diagnostics.push(ast::Diagnostic::error(
-                        name.loc(),
+                        name.loc,
                         format!(
                             "base '{}' from contract '{}' is cyclic",
                             name, ns.contracts[*contract_no].name
@@ -156,7 +156,7 @@ pub fn resolve_base_contracts(
                     && !ns.contracts[no].is_interface()
                 {
                     ns.diagnostics.push(ast::Diagnostic::error(
-                        name.loc(),
+                        name.loc,
                         format!(
                             "interface '{}' cannot have {} '{}' as a base",
                             ns.contracts[*contract_no].name, ns.contracts[no].ty, name
@@ -166,7 +166,7 @@ pub fn resolve_base_contracts(
                     let contract = &ns.contracts[*contract_no];
 
                     ns.diagnostics.push(ast::Diagnostic::error(
-                        name.loc(),
+                        name.loc,
                         format!(
                             "library '{}' cannot be used as base contract for {} '{}'",
                             name, contract.ty, contract.name,

+ 16 - 46
src/sema/namespace.rs

@@ -309,36 +309,21 @@ impl Namespace {
     pub fn resolve_contract_with_namespace(
         &mut self,
         file_no: usize,
-        expr: &pt::Expression,
+        name: &pt::IdentifierPath,
         diagnostics: &mut Vec<Diagnostic>,
     ) -> Result<usize, ()> {
-        let (namespace, id, dimensions) = self.expr_to_type(file_no, None, expr, diagnostics)?;
+        let (id, namespace) = name
+            .identifiers
+            .split_last()
+            .map(|(id, namespace)| (id, namespace.iter().collect()))
+            .unwrap();
 
-        if !dimensions.is_empty() {
-            diagnostics.push(Diagnostic::decl_error(
-                expr.loc(),
-                "array type found where contract type expected".to_string(),
-            ));
-            return Err(());
-        }
-
-        let id = match id {
-            pt::Expression::Variable(id) => id,
-            _ => {
-                diagnostics.push(Diagnostic::decl_error(
-                    expr.loc(),
-                    "expression found where contract type expected".to_string(),
-                ));
-                return Err(());
-            }
-        };
-
-        let s = self.resolve_namespace(namespace, file_no, None, &id, diagnostics)?;
+        let s = self.resolve_namespace(namespace, file_no, None, id, diagnostics)?;
 
         if let Some(Symbol::Contract(_, contract_no)) = s {
             Ok(*contract_no)
         } else {
-            let error = Namespace::wrong_symbol(s, &id);
+            let error = Namespace::wrong_symbol(s, id);
 
             diagnostics.push(error);
 
@@ -350,36 +335,21 @@ impl Namespace {
     pub fn resolve_free_function_with_namespace(
         &mut self,
         file_no: usize,
-        expr: &pt::Expression,
+        name: &pt::IdentifierPath,
         diagnostics: &mut Vec<Diagnostic>,
     ) -> Result<Vec<(pt::Loc, usize)>, ()> {
-        let (namespace, id, dimensions) = self.expr_to_type(file_no, None, expr, diagnostics)?;
+        let (id, namespace) = name
+            .identifiers
+            .split_last()
+            .map(|(id, namespace)| (id, namespace.iter().collect()))
+            .unwrap();
 
-        if !dimensions.is_empty() {
-            diagnostics.push(Diagnostic::decl_error(
-                expr.loc(),
-                "array type found where function expected".to_string(),
-            ));
-            return Err(());
-        }
-
-        let id = match id {
-            pt::Expression::Variable(id) => id,
-            _ => {
-                diagnostics.push(Diagnostic::decl_error(
-                    expr.loc(),
-                    "expression found where function expected".to_string(),
-                ));
-                return Err(());
-            }
-        };
-
-        let s = self.resolve_namespace(namespace, file_no, None, &id, diagnostics)?;
+        let s = self.resolve_namespace(namespace, file_no, None, id, diagnostics)?;
 
         if let Some(Symbol::Function(list)) = s {
             Ok(list.clone())
         } else {
-            let error = Namespace::wrong_symbol(s, &id);
+            let error = Namespace::wrong_symbol(s, id);
 
             diagnostics.push(error);
 

+ 8 - 7
src/sema/statements.rs

@@ -160,7 +160,13 @@ pub fn resolve_function_body(
 
         for attr in &def.attributes {
             if let pt::FunctionAttribute::BaseOrModifier(_, modifier) = attr {
-                if let pt::Expression::Variable(modifier_name) = &modifier.name {
+                if modifier.name.identifiers.len() != 1 {
+                    ns.diagnostics.push(Diagnostic::error(
+                        def.loc,
+                        format!("unknown modifier '{}' on function", modifier.name),
+                    ));
+                } else {
+                    let modifier_name = &modifier.name.identifiers[0];
                     if let Ok(e) = function_call_pos_args(
                         &modifier.loc,
                         modifier_name,
@@ -182,11 +188,6 @@ pub fn resolve_function_body(
                     ) {
                         modifiers.push(e);
                     }
-                } else {
-                    ns.diagnostics.push(Diagnostic::error(
-                        def.loc,
-                        format!("unknown modifier '{}' on function", modifier.name),
-                    ));
                 }
             }
         }
@@ -840,7 +841,7 @@ fn statement(
         pt::Statement::Revert(loc, error, args) => {
             if let Some(error) = error {
                 ns.diagnostics.push(Diagnostic::error(
-                    error.loc(),
+                    error.loc,
                     format!("revert with custom error '{}' not supported yet", error),
                 ));
                 return Err(());

+ 4 - 4
src/sema/using.rs

@@ -52,7 +52,7 @@ pub(crate) fn using_decl(
                     UsingList::Library(library_no)
                 } else {
                     ns.diagnostics.push(Diagnostic::error(
-                        library.loc(),
+                        library.loc,
                         format!(
                             "library expected but {} '{}' found",
                             ns.contracts[library_no].ty, library
@@ -84,7 +84,7 @@ pub(crate) fn using_decl(
                             .collect();
 
                         diagnostics.push(Diagnostic::error_with_notes(
-                            function_name.loc(),
+                            function_name.loc,
                             format!("'{}' is an overloaded function", function_name),
                             notes,
                         ));
@@ -97,7 +97,7 @@ pub(crate) fn using_decl(
 
                     if func.params.is_empty() {
                         diagnostics.push(Diagnostic::error_with_note(
-                            function_name.loc(),
+                            function_name.loc,
                             format!(
                                 "'{}' has no arguments, at least one argument required",
                                 function_name
@@ -111,7 +111,7 @@ pub(crate) fn using_decl(
                     if let Some(ty) = &ty {
                         if *ty != func.params[0].ty {
                             diagnostics.push(Diagnostic::error_with_note(
-                                function_name.loc(),
+                                function_name.loc,
                                 format!("function cannot be used since first argument is '{}' rather than the required '{}'", func.params[0].ty.to_string(ns), ty.to_string(ns)),
                                 loc,
                                 format!("definition of '{}'", function_name),