From f03c46071a0a043bccb45a9c0ab5ade9086bb968 Mon Sep 17 00:00:00 2001 From: Kamil Chmielewski <45183584+ChmielewskiKamil@users.noreply.github.com> Date: Sat, 7 Sep 2024 22:15:21 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20variable=20declaration=20p?= =?UTF-8?q?arsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ast/ast.go | 51 ++++++++++++++++---------- parser/exprparsing.go | 2 +- parser/parser.go | 83 ++++++++++++++++++++++++------------------- 3 files changed, 80 insertions(+), 56 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 3c2eaf5..9b205fc 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -86,6 +86,14 @@ type ( Function Expression // Function being called Args []Expression // Comma-separated list of arguments } + + ElementaryTypeExpression struct { + Pos token.Pos // position of the type keyword e.g. `a` in "address" + Kind token.Token // type of the literal e.g. token.ADDRESS, token.UINT_256, token.BOOL + // Used in expressions e.g. `return uint256(a + b)`. Then it + // contains the expression a + b + Value Expression + } ) // Start() and End() implementations for Expression type Nodes @@ -120,17 +128,25 @@ func (x *CallExpression) End() token.Pos { } return x.Pos + 2 // length of "()" } +func (x *ElementaryTypeExpression) Start() token.Pos { return x.Pos } +func (x *ElementaryTypeExpression) End() token.Pos { + if x.Value != nil { + return x.Value.End() + } + return x.Pos + token.Pos(len(x.Kind.Literal)) +} // expressionNode() implementations to ensure that only expressions can be // assigned to an Expression. This is useful if by mistake we try to use // a Statement in a place where an Expression should be used instead. -func (*Identifier) expressionNode() {} -func (*NumberLiteral) expressionNode() {} -func (*BooleanLiteral) expressionNode() {} -func (*PrefixExpression) expressionNode() {} -func (*InfixExpression) expressionNode() {} -func (*CallExpression) expressionNode() {} +func (*Identifier) expressionNode() {} +func (*NumberLiteral) expressionNode() {} +func (*BooleanLiteral) expressionNode() {} +func (*PrefixExpression) expressionNode() {} +func (*InfixExpression) expressionNode() {} +func (*CallExpression) expressionNode() {} +func (*ElementaryTypeExpression) expressionNode() {} // String() implementations for Expressions @@ -174,6 +190,16 @@ func (x *CallExpression) String() string { out.WriteString(")") return out.String() } +func (x *ElementaryTypeExpression) String() string { + var out bytes.Buffer + out.WriteString(x.Kind.Literal) + if x.Value != nil { + out.WriteString("(") + out.WriteString(x.Value.String()) + out.WriteString(")") + } + return out.String() +} /*~*~*~*~*~*~*~*~*~*~*~*~*~* Types ~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/ // Type nodes are constrains on expressions. They define the kinds of values @@ -189,17 +215,8 @@ func (x *CallExpression) String() string { type ElementaryType struct { Pos token.Pos // position of the type keyword e.g. `a` in "address" Kind token.Token // type of the literal e.g. token.ADDRESS, token.UINT_256, token.BOOL - // nil when used as declaration e.g. `address a;` - // BUT also used in expressions e.g. `return uint256(a + b)`. Then it - // contains the expression a + b - Value Expression } -// WARNING ElementaryType implements both Type and Expression interfaces. -// It can be used as a type e.g. in variable declaration `uint256 x;` OR -// as an expression in return statement `return uint256(a + b);` -func (x *ElementaryType) expressionNode() {} - // FunctionType represents a Solidity's function type. NOT TO BE CONFUSED WITH // FUNCTION DECLARATION. FunctionType is a weird thing that no one uses (lol) e.g. // ```solidity @@ -263,10 +280,6 @@ func (*ElementaryType) typeNode() {} func (x *ElementaryType) String() string { var out bytes.Buffer out.WriteString(x.Kind.Literal) - if x.Value != nil { - out.WriteString(" ") - out.WriteString(x.Value.String()) - } return out.String() } diff --git a/parser/exprparsing.go b/parser/exprparsing.go index 22d58ca..d9cb23f 100644 --- a/parser/exprparsing.go +++ b/parser/exprparsing.go @@ -281,7 +281,7 @@ func (p *Parser) parseElementaryTypeExpression() ast.Expression { defer un(trace("parseElementaryType")) } - et := &ast.ElementaryType{ + et := &ast.ElementaryTypeExpression{ Pos: p.currTkn.Pos, Kind: token.Token{ Type: p.currTkn.Type, diff --git a/parser/parser.go b/parser/parser.go index 181ca19..f8f6a5e 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -182,7 +182,6 @@ func (p *Parser) parseFunctionDeclaration() *ast.FunctionDeclaration { // - function types // - arrays of other types // - mappings (?) - if !token.IsElementaryType(p.peekTkn.Type) { p.errors.Add(p.peekTkn.Pos, "Fn param: expected elementary type, got: "+p.peekTkn.Literal) return nil @@ -190,9 +189,12 @@ func (p *Parser) parseFunctionDeclaration() *ast.FunctionDeclaration { p.nextToken() // Move to the type name. prm.Type = &ast.ElementaryType{ - Pos: p.currTkn.Pos, - Kind: p.currTkn, - Value: nil, // Here it's a type, not an expression. + Pos: p.currTkn.Pos, + Kind: token.Token{ + Type: p.currTkn.Type, + Literal: p.currTkn.Literal, + Pos: p.currTkn.Pos, + }, } if token.IsDataLocation(p.peekTkn.Type) { @@ -259,9 +261,12 @@ func (p *Parser) parseStateVariableDeclaration() *ast.StateVariableDeclaration { // We are sitting on the variable type e.g. address or uint256 decl.Type = &ast.ElementaryType{ - Pos: p.currTkn.Pos, - Kind: p.currTkn, - Value: nil, + Pos: p.currTkn.Pos, + Kind: token.Token{ + Type: p.currTkn.Type, + Literal: p.currTkn.Literal, + Pos: p.currTkn.Pos, + }, } p.nextToken() @@ -418,40 +423,46 @@ func (p *Parser) parseVariableDeclarationStatement() *ast.VariableDeclarationSta Literal: p.currTkn.Literal, Pos: p.currTkn.Pos, }, - Value: nil, } - p.nextToken() + if !p.expectPeek(token.IDENTIFIER) { + return nil + } - for { - switch tkType := p.currTkn.Type; { - default: - p.errors.Add(p.currTkn.Pos, "Unexpected token: "+p.currTkn.Literal) - return nil - case tkType == token.IDENTIFIER: - vdStmt.Name = &ast.Identifier{ - Pos: p.currTkn.Pos, - Value: p.currTkn.Literal, - } - p.nextToken() - case token.IsDataLocation(tkType): - switch tkType { - case token.STORAGE: - vdStmt.DataLocation = ast.Storage - case token.MEMORY: - vdStmt.DataLocation = ast.Memory - case token.CALLDATA: - vdStmt.DataLocation = ast.Calldata - } - p.nextToken() - case tkType == token.ASSIGN: - p.nextToken() - vdStmt.Value = p.parseExpression(LOWEST) - p.nextToken() - case tkType == token.SEMICOLON: - return vdStmt + vdStmt.Name = &ast.Identifier{ + Pos: p.currTkn.Pos, + Value: p.currTkn.Literal, + } + + if token.IsDataLocation(p.peekTkn.Type) { + p.nextToken() + switch p.currTkn.Type { + case token.STORAGE: + vdStmt.DataLocation = ast.Storage + case token.MEMORY: + vdStmt.DataLocation = ast.Memory + case token.CALLDATA: + vdStmt.DataLocation = ast.Calldata } } + + if p.peekTknIs(token.SEMICOLON) { + p.nextToken() + return vdStmt + } + + if !p.expectPeek(token.ASSIGN) { + return nil + } + + p.nextToken() // Move past the ASSIGN token + vdStmt.Value = p.parseExpression(LOWEST) + + if p.peekTknIs(token.SEMICOLON) { + p.nextToken() + } + + return vdStmt } func (p *Parser) parseReturnStatement() *ast.ReturnStatement {