Parse types in Julia mode

This commit is contained in:
Alex Beregszaszi 2017-04-26 23:58:34 +01:00
parent b0f2a5c162
commit d6396ee85f
11 changed files with 98 additions and 36 deletions

View File

@ -184,7 +184,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
int const stackHeight = m_stackHeight; int const stackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, *_varDecl.value); bool success = boost::apply_visitor(*this, *_varDecl.value);
solAssert(m_stackHeight - stackHeight == 1, "Invalid value size."); solAssert(m_stackHeight - stackHeight == 1, "Invalid value size.");
boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.name)).active = true; boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.variable.name)).active = true;
m_info.stackHeightInfo[&_varDecl] = m_stackHeight; m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
return success; return success;
} }
@ -193,7 +193,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
{ {
Scope& bodyScope = scope(&_funDef.body); Scope& bodyScope = scope(&_funDef.body);
for (auto const& var: _funDef.arguments + _funDef.returns) for (auto const& var: _funDef.arguments + _funDef.returns)
boost::get<Scope::Variable>(bodyScope.identifiers.at(var)).active = true; boost::get<Scope::Variable>(bodyScope.identifiers.at(var.name)).active = true;
int const stackHeight = m_stackHeight; int const stackHeight = m_stackHeight;
m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); m_stackHeight = _funDef.arguments.size() + _funDef.returns.size();
@ -232,8 +232,9 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall)
}, },
[&](Scope::Function const& _fun) [&](Scope::Function const& _fun)
{ {
arguments = _fun.arguments; /// TODO: compare types too
returns = _fun.returns; arguments = _fun.arguments.size();
returns = _fun.returns.size();
} }
))) )))
{ {

View File

@ -204,7 +204,7 @@ public:
int height = m_state.assembly.deposit(); int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *_varDecl.value); boost::apply_visitor(*this, *_varDecl.value);
expectDeposit(1, height); expectDeposit(1, height);
auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.name)); auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.variable.name));
var.stackHeight = height; var.stackHeight = height;
var.active = true; var.active = true;
} }

View File

@ -33,12 +33,17 @@ namespace solidity
namespace assembly namespace assembly
{ {
using Type = std::string;
struct TypedName { SourceLocation location; std::string name; Type type; };
using TypedNameList = std::vector<TypedName>;
/// What follows are the AST nodes for assembly. /// What follows are the AST nodes for assembly.
/// Direct EVM instruction (except PUSHi and JUMPDEST) /// Direct EVM instruction (except PUSHi and JUMPDEST)
struct Instruction { SourceLocation location; solidity::Instruction instruction; }; struct Instruction { SourceLocation location; solidity::Instruction instruction; };
/// Literal number or string (up to 32 bytes) /// 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 /// External / internal identifier or label reference
struct Identifier { SourceLocation location; std::string name; }; struct Identifier { SourceLocation location; std::string name; };
struct FunctionalInstruction; struct FunctionalInstruction;
@ -52,18 +57,18 @@ struct FunctionDefinition;
struct FunctionCall; struct FunctionCall;
struct Block; struct Block;
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>; using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
/// 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. /// side and requires x to occupy exactly one stack slot.
struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; }; struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> 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<Statement> arguments; }; struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; };
struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; };
/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted
struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; }; struct VariableDeclaration { SourceLocation location; TypedName variable; std::shared_ptr<Statement> value; };
/// Block that creates a scope (frees declared stack variables) /// Block that creates a scope (frees declared stack variables)
struct Block { SourceLocation location; std::vector<Statement> statements; }; struct Block { SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }") /// Function definition ("function f(a, b) -> (d, e) { ... }")
struct FunctionDefinition { SourceLocation location; std::string name; std::vector<std::string> arguments; std::vector<std::string> returns; Block body; }; struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; };
struct LocationExtractor: boost::static_visitor<SourceLocation> struct LocationExtractor: boost::static_visitor<SourceLocation>
{ {

View File

@ -201,16 +201,26 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
} }
else else
ret = Identifier{location(), literal}; ret = Identifier{location(), literal};
m_scanner->next();
break; break;
} }
case Token::StringLiteral: case Token::StringLiteral:
case Token::Number: case Token::Number:
{ {
ret = Literal{ Literal literal{
location(), location(),
m_scanner->currentToken() == Token::Number, 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; break;
} }
default: default:
@ -220,7 +230,6 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
"Expected elementary inline assembly operation." "Expected elementary inline assembly operation."
); );
} }
m_scanner->next();
return ret; return ret;
} }
@ -228,7 +237,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
{ {
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
expectToken(Token::Let); expectToken(Token::Let);
varDecl.name = expectAsmIdentifier(); varDecl.variable = parseTypedName();
expectToken(Token::Colon); expectToken(Token::Colon);
expectToken(Token::Assign); expectToken(Token::Assign);
varDecl.value.reset(new Statement(parseExpression())); varDecl.value.reset(new Statement(parseExpression()));
@ -244,7 +253,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::LParen); expectToken(Token::LParen);
while (m_scanner->currentToken() != Token::RParen) while (m_scanner->currentToken() != Token::RParen)
{ {
funDef.arguments.push_back(expectAsmIdentifier()); funDef.arguments.emplace_back(parseTypedName());
if (m_scanner->currentToken() == Token::RParen) if (m_scanner->currentToken() == Token::RParen)
break; break;
expectToken(Token::Comma); expectToken(Token::Comma);
@ -256,7 +265,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::GreaterThan); expectToken(Token::GreaterThan);
while (true) while (true)
{ {
funDef.returns.push_back(expectAsmIdentifier()); funDef.returns.emplace_back(parseTypedName());
if (m_scanner->currentToken() == Token::LBrace) if (m_scanner->currentToken() == Token::LBrace)
break; break;
expectToken(Token::Comma); expectToken(Token::Comma);
@ -335,6 +344,19 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
return {}; return {};
} }
TypedName Parser::parseTypedName()
{
TypedName typedName = createWithLocation<TypedName>();
typedName.name = expectAsmIdentifier();
if (m_julia)
{
expectToken(Token::Colon);
typedName.location.end = endPosition();
typedName.type = expectAsmIdentifier();
}
return typedName;
}
string Parser::expectAsmIdentifier() string Parser::expectAsmIdentifier()
{ {
string name = m_scanner->currentLiteral(); string name = m_scanner->currentLiteral();

View File

@ -69,6 +69,7 @@ protected:
VariableDeclaration parseVariableDeclaration(); VariableDeclaration parseVariableDeclaration();
FunctionDefinition parseFunctionDefinition(); FunctionDefinition parseFunctionDefinition();
Statement parseFunctionalInstruction(Statement&& _instruction); Statement parseFunctionalInstruction(Statement&& _instruction);
TypedName parseTypedName();
std::string expectAsmIdentifier(); std::string expectAsmIdentifier();
private: private:

View File

@ -47,7 +47,7 @@ string AsmPrinter::operator()(assembly::Instruction const& _instruction)
string AsmPrinter::operator()(assembly::Literal const& _literal) string AsmPrinter::operator()(assembly::Literal const& _literal)
{ {
if (_literal.isNumber) if (_literal.isNumber)
return _literal.value; return _literal.value + appendTypeName(_literal.type);
string out; string out;
for (char c: _literal.value) for (char c: _literal.value)
if (c == '\\') if (c == '\\')
@ -74,7 +74,7 @@ string AsmPrinter::operator()(assembly::Literal const& _literal)
} }
else else
out += c; out += c;
return "\"" + out + "\""; return "\"" + out + "\"" + appendTypeName(_literal.type);
} }
string AsmPrinter::operator()(assembly::Identifier const& _identifier) 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) 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 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()) 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); return out + "\n" + (*this)(_functionDefinition.body);
} }
@ -145,3 +153,10 @@ string AsmPrinter::operator()(Block const& _block)
boost::replace_all(body, "\n", "\n "); boost::replace_all(body, "\n", "\n ");
return "{\n " + body + "\n}"; return "{\n " + body + "\n}";
} }
string AsmPrinter::appendTypeName(std::string const& _type)
{
if (m_julia)
return ":" + _type;
return "";
}

View File

@ -60,6 +60,8 @@ public:
std::string operator()(assembly::Block const& _block); std::string operator()(assembly::Block const& _block);
private: private:
std::string appendTypeName(std::string const& _type);
bool m_julia = false; bool m_julia = false;
}; };

View File

@ -32,15 +32,17 @@ bool Scope::registerLabel(string const& _name)
return true; return true;
} }
bool Scope::registerVariable(string const& _name) bool Scope::registerVariable(string const& _name, JuliaType const& _type)
{ {
if (exists(_name)) if (exists(_name))
return false; return false;
identifiers[_name] = Variable(); Variable variable;
variable.type = _type;
identifiers[_name] = variable;
return true; return true;
} }
bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns) bool Scope::registerFunction(string const& _name, std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns)
{ {
if (exists(_name)) if (exists(_name))
return false; return false;

View File

@ -61,6 +61,8 @@ struct GenericVisitor<>: public boost::static_visitor<> {
struct Scope struct Scope
{ {
using JuliaType = std::string;
struct Variable struct Variable
{ {
/// Used during code generation to store the stack height. @todo move there. /// 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. /// Used during analysis to check whether we already passed the declaration inside the block.
/// @todo move there. /// @todo move there.
bool active = false; bool active = false;
JuliaType type;
}; };
struct Label struct Label
@ -78,18 +81,22 @@ struct Scope
struct Function struct Function
{ {
Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {} Function(std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns): arguments(_arguments), returns(_returns) {}
size_t arguments = 0; std::vector<JuliaType> arguments;
size_t returns = 0; std::vector<JuliaType> returns;
}; };
using Identifier = boost::variant<Variable, Label, Function>; using Identifier = boost::variant<Variable, Label, Function>;
using Visitor = GenericVisitor<Variable const, Label const, Function const>; using Visitor = GenericVisitor<Variable const, Label const, Function const>;
using NonconstVisitor = GenericVisitor<Variable, Label, Function>; using NonconstVisitor = GenericVisitor<Variable, Label, Function>;
bool registerVariable(std::string const& _name); bool registerVariable(std::string const& _name, JuliaType const& _type);
bool registerLabel(std::string const& _name); 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<JuliaType> const& _arguments,
std::vector<JuliaType> const& _returns
);
/// Looks up the identifier in this or super scopes and returns a valid pointer if found /// 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 /// or a nullptr if not found. Variable lookups up across function boundaries will fail, as

View File

@ -59,13 +59,19 @@ bool ScopeFiller::operator()(Label const& _item)
bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl) 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 ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
{ {
bool success = true; bool success = true;
if (!m_currentScope->registerFunction(_funDef.name, _funDef.arguments.size(), _funDef.returns.size())) vector<Scope::JuliaType> arguments;
for (auto const& _argument: _funDef.arguments)
arguments.push_back(_argument.type);
vector<Scope::JuliaType> returns;
for (auto const& _return: _funDef.returns)
returns.push_back(_return.type);
if (!m_currentScope->registerFunction(_funDef.name, arguments, returns))
{ {
//@TODO secondary location //@TODO secondary location
m_errors.push_back(make_shared<Error>( m_errors.push_back(make_shared<Error>(
@ -102,14 +108,14 @@ bool ScopeFiller::operator()(Block const& _block)
return success; 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 //@TODO secondary location
m_errors.push_back(make_shared<Error>( m_errors.push_back(make_shared<Error>(
Error::Type::DeclarationError, Error::Type::DeclarationError,
"Variable name " + _name + " already taken in this scope.", "Variable name " + _name.name + " already taken in this scope.",
_location _location
)); ));
return false; return false;

View File

@ -34,6 +34,7 @@ namespace solidity
namespace assembly namespace assembly
{ {
struct TypedName;
struct Literal; struct Literal;
struct Block; struct Block;
struct Label; struct Label;
@ -72,7 +73,7 @@ public:
private: private:
bool registerVariable( bool registerVariable(
std::string const& _name, TypedName const& _name,
SourceLocation const& _location, SourceLocation const& _location,
Scope& _scope Scope& _scope
); );