From d6396ee85fd21424568074c41a131e8c52961983 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 26 Apr 2017 23:58:34 +0100 Subject: Parse types in Julia mode --- libsolidity/inlineasm/AsmAnalysis.cpp | 9 +++++---- libsolidity/inlineasm/AsmCodeGen.cpp | 2 +- libsolidity/inlineasm/AsmData.h | 17 ++++++++++------ libsolidity/inlineasm/AsmParser.cpp | 34 ++++++++++++++++++++++++++------ libsolidity/inlineasm/AsmParser.h | 1 + libsolidity/inlineasm/AsmPrinter.cpp | 25 ++++++++++++++++++----- libsolidity/inlineasm/AsmPrinter.h | 2 ++ libsolidity/inlineasm/AsmScope.cpp | 8 +++++--- libsolidity/inlineasm/AsmScope.h | 17 +++++++++++----- libsolidity/inlineasm/AsmScopeFiller.cpp | 16 ++++++++++----- libsolidity/inlineasm/AsmScopeFiller.h | 3 ++- 11 files changed, 98 insertions(+), 36 deletions(-) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index dad05a78..590f9ad6 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -184,7 +184,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) int const stackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, *_varDecl.value); solAssert(m_stackHeight - stackHeight == 1, "Invalid value size."); - boost::get(m_currentScope->identifiers.at(_varDecl.name)).active = true; + boost::get(m_currentScope->identifiers.at(_varDecl.variable.name)).active = true; m_info.stackHeightInfo[&_varDecl] = m_stackHeight; return success; } @@ -193,7 +193,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) { Scope& bodyScope = scope(&_funDef.body); for (auto const& var: _funDef.arguments + _funDef.returns) - boost::get(bodyScope.identifiers.at(var)).active = true; + boost::get(bodyScope.identifiers.at(var.name)).active = true; int const stackHeight = m_stackHeight; m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); @@ -232,8 +232,9 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) }, [&](Scope::Function const& _fun) { - arguments = _fun.arguments; - returns = _fun.returns; + /// TODO: compare types too + arguments = _fun.arguments.size(); + returns = _fun.returns.size(); } ))) { diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index c19667b4..137a70f5 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -204,7 +204,7 @@ public: int height = m_state.assembly.deposit(); boost::apply_visitor(*this, *_varDecl.value); expectDeposit(1, height); - auto& var = boost::get(m_scope.identifiers.at(_varDecl.name)); + auto& var = boost::get(m_scope.identifiers.at(_varDecl.variable.name)); var.stackHeight = height; var.active = true; } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index d61b5803..363873ab 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -33,12 +33,17 @@ namespace solidity namespace assembly { +using Type = std::string; + +struct TypedName { SourceLocation location; std::string name; Type type; }; +using TypedNameList = std::vector; + /// What follows are the AST nodes for assembly. /// Direct EVM instruction (except PUSHi and JUMPDEST) struct Instruction { SourceLocation location; solidity::Instruction instruction; }; /// Literal number or string (up to 32 bytes) -struct Literal { SourceLocation location; bool isNumber; std::string value; }; +struct Literal { SourceLocation location; bool isNumber; std::string value; Type type; }; /// External / internal identifier or label reference struct Identifier { SourceLocation location; std::string name; }; struct FunctionalInstruction; @@ -52,18 +57,18 @@ struct FunctionDefinition; struct FunctionCall; struct Block; using Statement = boost::variant; -/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand +/// Functional assignment ("x := mload(20:u256)", expects push-1-expression on the right hand /// side and requires x to occupy exactly one stack slot. struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr value; }; -/// Functional instruction, e.g. "mul(mload(20), add(2, x))" +/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; -/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted -struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr value; }; +/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted +struct VariableDeclaration { SourceLocation location; TypedName variable; std::shared_ptr value; }; /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { SourceLocation location; std::string name; std::vector arguments; std::vector returns; Block body; }; +struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; }; struct LocationExtractor: boost::static_visitor { diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index d9b0b3e0..7ecad5ea 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -201,16 +201,26 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) } else ret = Identifier{location(), literal}; + m_scanner->next(); break; } case Token::StringLiteral: case Token::Number: { - ret = Literal{ + Literal literal{ location(), m_scanner->currentToken() == Token::Number, - m_scanner->currentLiteral() + m_scanner->currentLiteral(), + "" }; + m_scanner->next(); + if (m_julia) + { + expectToken(Token::Colon); + literal.location.end = endPosition(); + literal.type = expectAsmIdentifier(); + } + ret = std::move(literal); break; } default: @@ -220,7 +230,6 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) "Expected elementary inline assembly operation." ); } - m_scanner->next(); return ret; } @@ -228,7 +237,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { VariableDeclaration varDecl = createWithLocation(); expectToken(Token::Let); - varDecl.name = expectAsmIdentifier(); + varDecl.variable = parseTypedName(); expectToken(Token::Colon); expectToken(Token::Assign); varDecl.value.reset(new Statement(parseExpression())); @@ -244,7 +253,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::LParen); while (m_scanner->currentToken() != Token::RParen) { - funDef.arguments.push_back(expectAsmIdentifier()); + funDef.arguments.emplace_back(parseTypedName()); if (m_scanner->currentToken() == Token::RParen) break; expectToken(Token::Comma); @@ -256,7 +265,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::GreaterThan); while (true) { - funDef.returns.push_back(expectAsmIdentifier()); + funDef.returns.emplace_back(parseTypedName()); if (m_scanner->currentToken() == Token::LBrace) break; expectToken(Token::Comma); @@ -335,6 +344,19 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in return {}; } +TypedName Parser::parseTypedName() +{ + TypedName typedName = createWithLocation(); + typedName.name = expectAsmIdentifier(); + if (m_julia) + { + expectToken(Token::Colon); + typedName.location.end = endPosition(); + typedName.type = expectAsmIdentifier(); + } + return typedName; +} + string Parser::expectAsmIdentifier() { string name = m_scanner->currentLiteral(); diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index c55fd2ac..addc1725 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -69,6 +69,7 @@ protected: VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); Statement parseFunctionalInstruction(Statement&& _instruction); + TypedName parseTypedName(); std::string expectAsmIdentifier(); private: diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 4a6f975d..94829371 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -47,7 +47,7 @@ string AsmPrinter::operator()(assembly::Instruction const& _instruction) string AsmPrinter::operator()(assembly::Literal const& _literal) { if (_literal.isNumber) - return _literal.value; + return _literal.value + appendTypeName(_literal.type); string out; for (char c: _literal.value) if (c == '\\') @@ -74,7 +74,7 @@ string AsmPrinter::operator()(assembly::Literal const& _literal) } else out += c; - return "\"" + out + "\""; + return "\"" + out + "\"" + appendTypeName(_literal.type); } string AsmPrinter::operator()(assembly::Identifier const& _identifier) @@ -113,14 +113,22 @@ string AsmPrinter::operator()(assembly::FunctionalAssignment const& _functionalA string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration) { - return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value); + return "let " + _variableDeclaration.variable.name + appendTypeName(_variableDeclaration.variable.type) + " := " + boost::apply_visitor(*this, *_variableDeclaration.value); } string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) { - string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")"; + string out = "function " + _functionDefinition.name + "("; + for (auto const& argument: _functionDefinition.arguments) + out += argument.name + appendTypeName(argument.type) + ","; + out += ")"; if (!_functionDefinition.returns.empty()) - out += " -> " + boost::algorithm::join(_functionDefinition.returns, ", "); + { + out += " -> "; + for (auto const& argument: _functionDefinition.returns) + out += argument.name + appendTypeName(argument.type) + ","; + } + return out + "\n" + (*this)(_functionDefinition.body); } @@ -145,3 +153,10 @@ string AsmPrinter::operator()(Block const& _block) boost::replace_all(body, "\n", "\n "); return "{\n " + body + "\n}"; } + +string AsmPrinter::appendTypeName(std::string const& _type) +{ + if (m_julia) + return ":" + _type; + return ""; +} diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index 038c6d85..282fd7e3 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -60,6 +60,8 @@ public: std::string operator()(assembly::Block const& _block); private: + std::string appendTypeName(std::string const& _type); + bool m_julia = false; }; diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp index 609dca16..e3f4615a 100644 --- a/libsolidity/inlineasm/AsmScope.cpp +++ b/libsolidity/inlineasm/AsmScope.cpp @@ -32,15 +32,17 @@ bool Scope::registerLabel(string const& _name) return true; } -bool Scope::registerVariable(string const& _name) +bool Scope::registerVariable(string const& _name, JuliaType const& _type) { if (exists(_name)) return false; - identifiers[_name] = Variable(); + Variable variable; + variable.type = _type; + identifiers[_name] = variable; return true; } -bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns) +bool Scope::registerFunction(string const& _name, std::vector const& _arguments, std::vector const& _returns) { if (exists(_name)) return false; diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index b70bee67..dd45613d 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -61,6 +61,8 @@ struct GenericVisitor<>: public boost::static_visitor<> { struct Scope { + using JuliaType = std::string; + struct Variable { /// Used during code generation to store the stack height. @todo move there. @@ -68,6 +70,7 @@ struct Scope /// Used during analysis to check whether we already passed the declaration inside the block. /// @todo move there. bool active = false; + JuliaType type; }; struct Label @@ -78,18 +81,22 @@ struct Scope struct Function { - Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {} - size_t arguments = 0; - size_t returns = 0; + Function(std::vector const& _arguments, std::vector const& _returns): arguments(_arguments), returns(_returns) {} + std::vector arguments; + std::vector returns; }; using Identifier = boost::variant; using Visitor = GenericVisitor; using NonconstVisitor = GenericVisitor; - bool registerVariable(std::string const& _name); + bool registerVariable(std::string const& _name, JuliaType const& _type); bool registerLabel(std::string const& _name); - bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns); + bool registerFunction( + std::string const& _name, + std::vector const& _arguments, + std::vector const& _returns + ); /// Looks up the identifier in this or super scopes and returns a valid pointer if found /// or a nullptr if not found. Variable lookups up across function boundaries will fail, as diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 4a651388..eb10dbb3 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -59,13 +59,19 @@ bool ScopeFiller::operator()(Label const& _item) bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl) { - return registerVariable(_varDecl.name, _varDecl.location, *m_currentScope); + return registerVariable(_varDecl.variable, _varDecl.location, *m_currentScope); } bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) { bool success = true; - if (!m_currentScope->registerFunction(_funDef.name, _funDef.arguments.size(), _funDef.returns.size())) + vector arguments; + for (auto const& _argument: _funDef.arguments) + arguments.push_back(_argument.type); + vector returns; + for (auto const& _return: _funDef.returns) + returns.push_back(_return.type); + if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { //@TODO secondary location m_errors.push_back(make_shared( @@ -102,14 +108,14 @@ bool ScopeFiller::operator()(Block const& _block) return success; } -bool ScopeFiller::registerVariable(string const& _name, SourceLocation const& _location, Scope& _scope) +bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& _location, Scope& _scope) { - if (!_scope.registerVariable(_name)) + if (!_scope.registerVariable(_name.name, _name.type)) { //@TODO secondary location m_errors.push_back(make_shared( Error::Type::DeclarationError, - "Variable name " + _name + " already taken in this scope.", + "Variable name " + _name.name + " already taken in this scope.", _location )); return false; diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h index bb62948b..61428eea 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libsolidity/inlineasm/AsmScopeFiller.h @@ -34,6 +34,7 @@ namespace solidity namespace assembly { +struct TypedName; struct Literal; struct Block; struct Label; @@ -72,7 +73,7 @@ public: private: bool registerVariable( - std::string const& _name, + TypedName const& _name, SourceLocation const& _location, Scope& _scope ); -- cgit From 8688b63fa624fbf14982c3c4340ab83c13144f66 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 3 May 2017 19:32:38 +0100 Subject: Use boost range adaptors in AsmPrinter --- libsolidity/inlineasm/AsmPrinter.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 94829371..f3b66aee 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -119,14 +119,22 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) { string out = "function " + _functionDefinition.name + "("; - for (auto const& argument: _functionDefinition.arguments) - out += argument.name + appendTypeName(argument.type) + ","; + out += boost::algorithm::join( + _functionDefinition.arguments | boost::adaptors::transformed( + [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + ), + ", " + ); out += ")"; if (!_functionDefinition.returns.empty()) { out += " -> "; - for (auto const& argument: _functionDefinition.returns) - out += argument.name + appendTypeName(argument.type) + ","; + out += boost::algorithm::join( + _functionDefinition.returns | boost::adaptors::transformed( + [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + ), + ", " + ); } return out + "\n" + (*this)(_functionDefinition.body); -- cgit From 6706932d7c7cd0a4d1b99e806b8f80cf8fe6cb91 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 5 May 2017 14:51:36 +0100 Subject: Add tests for types in Julia --- test/libjulia/Parser.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 0dfd3033..a582b2ae 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -122,12 +122,12 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(vardecl) { - BOOST_CHECK(successParse("{ let x := 7 }")); + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 }")); } BOOST_AUTO_TEST_CASE(assignment) { - BOOST_CHECK(successParse("{ let x := 2 let y := x }")); + BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); } BOOST_AUTO_TEST_CASE(function_call) @@ -137,27 +137,27 @@ BOOST_AUTO_TEST_CASE(function_call) BOOST_AUTO_TEST_CASE(vardecl_complex) { - BOOST_CHECK(successParse("{ let y := 2 let x := add(7, mul(6, y)) }")); + BOOST_CHECK(successParse("{ let y:u256 := 2:u256 let x:u256 := add(7:u256, mul(6:u256, y)) }")); } BOOST_AUTO_TEST_CASE(blocks) { - BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 { let y:u256 := 3:u256 } { let z:u256 := 2:u256 } }")); } BOOST_AUTO_TEST_CASE(function_definitions) { - BOOST_CHECK(successParse("{ function f() { } function g(a) -> x { } }")); + BOOST_CHECK(successParse("{ function f() { } function g(a:u256) -> x:u256 { } }")); } BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) { - BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> x, y { } }")); + BOOST_CHECK(successParse("{ function f(a:u256, d:u256) { } function g(a:u256, d:u256) -> x:u256, y:u256 { } }")); } BOOST_AUTO_TEST_CASE(function_calls) { - BOOST_CHECK(successParse("{ function f(a) -> b {} function g(a, b, c) {} function x() { g(1, 2, f(mul(2, 3))) x() } }")); + BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(mul(2:u256, 3:u256))) x() } }")); } BOOST_AUTO_TEST_CASE(label) @@ -172,12 +172,12 @@ BOOST_AUTO_TEST_CASE(instructions) BOOST_AUTO_TEST_CASE(push) { - CHECK_ERROR("{ 0x42 }", ParserError, "Call or assignment expected."); + CHECK_ERROR("{ 0x42:u256 }", ParserError, "Call or assignment expected."); } BOOST_AUTO_TEST_CASE(assign_from_stack) { - CHECK_ERROR("{ =: x }", ParserError, "Literal or identifier expected."); + CHECK_ERROR("{ =: x:u256 }", ParserError, "Literal or identifier expected."); } BOOST_AUTO_TEST_CASE(empty_call) @@ -185,6 +185,14 @@ BOOST_AUTO_TEST_CASE(empty_call) CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); } +BOOST_AUTO_TEST_CASE(lacking_types) +{ + CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected token Identifier got 'Assign'"); + CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected token Colon got 'RBrace'"); + CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected token Colon got 'RParen'"); + CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected token Colon got 'LBrace'"); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit