1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

Add support for #pragma version and FunC versioning

FunC pragma refined error messages and ^ partials
This commit is contained in:
starlightduck 2022-05-02 02:10:21 +03:00 committed by EmelyanenkoK
parent 0e47c6c8e0
commit 0e955793ed
5 changed files with 197 additions and 4 deletions

View file

@ -173,6 +173,7 @@ void usage(const char* progname) {
"-R\tInclude operation rewrite comments in the output code\n" "-R\tInclude operation rewrite comments in the output code\n"
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables " "-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
"-A and -P.\n" "-A and -P.\n"
"\t-s\tOutput semantic version of FunC and exit\n"
"\t-V<version>\tShow func build information\n"; "\t-V<version>\tShow func build information\n";
std::exit(2); std::exit(2);
} }
@ -182,7 +183,7 @@ std::string output_filename;
int main(int argc, char* const argv[]) { int main(int argc, char* const argv[]) {
int i; int i;
bool interactive = false; bool interactive = false;
while ((i = getopt(argc, argv, "Ahi:Io:O:PRSvW:V")) != -1) { while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) {
switch (i) { switch (i) {
case 'A': case 'A':
funC::asm_preamble = true; funC::asm_preamble = true;
@ -215,8 +216,13 @@ int main(int argc, char* const argv[]) {
funC::boc_output_filename = optarg; funC::boc_output_filename = optarg;
funC::asm_preamble = funC::program_envelope = true; funC::asm_preamble = funC::program_envelope = true;
break; break;
case 's':
std::cout << funC::func_version << "\n";
std::exit(0);
break;
case 'V': case 'V':
std::cout << "Func build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n"; std::cout << "FunC semantic version: v" << funC::func_version << "\n";
std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
std::exit(0); std::exit(0);
break; break;
case 'h': case 'h':

View file

@ -38,6 +38,8 @@ extern bool op_rewrite_comments;
constexpr int optimize_depth = 20; constexpr int optimize_depth = 20;
const std::string func_version{"0.1.0"};
enum Keyword { enum Keyword {
_Eof = -1, _Eof = -1,
_Ident = 0, _Ident = 0,
@ -106,7 +108,8 @@ enum Keyword {
_Operator, _Operator,
_Infix, _Infix,
_Infixl, _Infixl,
_Infixr _Infixr,
_PragmaHashtag
}; };
void define_keywords(); void define_keywords();

View file

@ -126,6 +126,8 @@ void define_keywords() {
.add_keyword("infix", Kw::_Infix) .add_keyword("infix", Kw::_Infix)
.add_keyword("infixl", Kw::_Infixl) .add_keyword("infixl", Kw::_Infixl)
.add_keyword("infixr", Kw::_Infixr); .add_keyword("infixr", Kw::_Infixr);
sym::symbols.add_keyword("#pragma", Kw::_PragmaHashtag);
} }
} // namespace funC } // namespace funC

View file

@ -1288,13 +1288,142 @@ void parse_func_def(Lexer& lex) {
sym::close_scope(lex); sym::close_scope(lex);
} }
std::string func_ver_test = func_version;
void parse_pragma(Lexer& lex) {
auto pragma = lex.cur();
lex.next();
if (lex.tp() != _Ident) {
lex.expect(_Ident, "pragma name expected");
}
auto pragma_name = lex.cur().str;
lex.next();
if (!pragma_name.compare("version") || !pragma_name.compare("not-version")) {
bool negate = !pragma_name.compare("not-version");
char op = '='; bool eq = false;
int sem_ver[3] = {0, 0, 0};
char segs = 1;
if (lex.tp() == _Number) {
sem_ver[0] = std::stoi(lex.cur().str);
} else if (lex.tp() == _Ident) {
auto id1 = lex.cur().str;
char ch1 = id1[0];
if ((ch1 == '>') || (ch1 == '<') || (ch1 == '=') || (ch1 == '^')) {
op = ch1;
} else {
lex.cur().error("unexpected comparator operation");
}
if (id1.length() < 2) {
lex.cur().error("expected number after comparator");
}
if (id1[1] == '=') {
eq = true;
if (id1.length() < 3) {
lex.cur().error("expected number after comparator");
}
sem_ver[0] = std::stoi(id1.substr(2));
} else {
sem_ver[0] = std::stoi(id1.substr(1));
}
} else {
lex.cur().error("expected semver with optional comparator");
}
lex.next();
if (lex.tp() != ';') {
if (lex.tp() != _Ident || lex.cur().str[0] != '.') {
lex.cur().error("invalid semver format");
}
sem_ver[1] = std::stoi(lex.cur().str.substr(1));
segs = 2;
lex.next();
}
if (lex.tp() != ';') {
if (lex.tp() != _Ident || lex.cur().str[0] != '.') {
lex.cur().error("invalid semver format");
}
sem_ver[2] = std::stoi(lex.cur().str.substr(1));
segs = 3;
lex.next();
}
// End reading semver from source code
int func_ver[3] = {0, 0, 0};
std::istringstream iss(func_ver_test);
std::string s;
for (int idx = 0; idx < 3; idx++) {
std::getline(iss, s, '.');
func_ver[idx] = std::stoi(s);
}
// End parsing embedded semver
std::string semver_expr;
if (negate) {
semver_expr += '!';
}
semver_expr += op;
if (eq) {
semver_expr += '=';
}
for (int idx = 0; idx < 3; idx++) {
semver_expr += std::to_string(sem_ver[idx]);
if (idx < 2)
semver_expr += '.';
}
bool match = true;
switch (op) {
case '=':
if ((func_ver[0] != sem_ver[0]) ||
(func_ver[1] != sem_ver[1]) ||
(func_ver[2] != sem_ver[2])) {
match = false;
}
break;
case '>':
if ( ((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] == sem_ver[2]) && !eq) ||
((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] < sem_ver[2])) ||
((func_ver[0] == sem_ver[0]) && (func_ver[1] < sem_ver[1])) ||
((func_ver[0] < sem_ver[0])) ) {
match = false;
}
break;
case '<':
if ( ((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] == sem_ver[2]) && !eq) ||
((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] > sem_ver[2])) ||
((func_ver[0] == sem_ver[0]) && (func_ver[1] > sem_ver[1])) ||
((func_ver[0] > sem_ver[0])) ) {
match = false;
}
break;
case '^':
if ( ((segs == 3) && ((func_ver[0] != sem_ver[0]) || (func_ver[1] != sem_ver[1]) || (func_ver[2] < sem_ver[2])))
|| ((segs == 2) && ((func_ver[0] != sem_ver[0]) || (func_ver[1] < sem_ver[1])))
|| ((segs == 1) && ((func_ver[0] < sem_ver[0]))) ) {
match = false;
}
break;
}
if ((match && negate) || (!match && !negate)) {
pragma.error(std::string("FunC version ") + func_ver_test + " does not satisfy condition " + semver_expr);
}
} else if (!pragma_name.compare("test-version-set")) {
if (lex.tp() != _String) {
lex.cur().error("version string expected");
}
func_ver_test = lex.cur().str;
lex.next();
} else {
lex.cur().error(std::string{"unknown pragma `"} + pragma_name + "`");
}
lex.expect(';');
}
std::vector<const src::FileDescr*> source_fdescr; std::vector<const src::FileDescr*> source_fdescr;
bool parse_source(std::istream* is, src::FileDescr* fdescr) { bool parse_source(std::istream* is, src::FileDescr* fdescr) {
src::SourceReader reader{is, fdescr}; src::SourceReader reader{is, fdescr};
Lexer lex{reader, true, ";,()[] ~."}; Lexer lex{reader, true, ";,()[] ~."};
while (lex.tp() != _Eof) { while (lex.tp() != _Eof) {
if (lex.tp() == _Global) { if (lex.tp() == _PragmaHashtag) {
parse_pragma(lex);
} else if (lex.tp() == _Global) {
parse_global_var_decls(lex); parse_global_var_decls(lex);
} else { } else {
parse_func_def(lex); parse_func_def(lex);

53
crypto/func/test/pv.fc Normal file
View file

@ -0,0 +1,53 @@
#pragma test-version-set "1.2.3";
;; Positive tests
#pragma version ^1.2.0;
#pragma version ^1.2.3;
#pragma version >1.2.0;
#pragma version >0.9.9;
#pragma version <1.3.0;
#pragma version <2.0.0;
#pragma version >=1.2.0;
#pragma version <=1.3.0;
#pragma version >=1.2.3;
#pragma version <=1.2.3;
#pragma version ^1.2.3;
#pragma version 1.2.3;
#pragma version =1.2.3;
;; Negative tests
#pragma not-version ^1.1.0;
#pragma not-version ^1.0.0;
#pragma not-version ^0.2.3;
#pragma not-version ^2.2.3;
#pragma not-version ^1.3.3;
#pragma not-version >1.2.3;
#pragma not-version <1.2.3;
#pragma not-version ^1.2.4;
#pragma not-version >=1.2.4;
#pragma not-version <=1.2.2;
#pragma not-version 3.2.1;
#pragma not-version =3.2.1;
;; Test incomplete (partial) version
#pragma version ^1.2;
#pragma version >1.2;
#pragma version <1.3;
#pragma version <2;
#pragma version >=1.2;
#pragma version <=1.3;
;; Advanced ^ behaviour (partials)
#pragma version ^1.2;
#pragma version ^1.0;
#pragma version ^1;
#pragma version ^0;
#pragma not-version ^1.0.0;
#pragma not-version ^0.0.0;
#pragma not-version ^0.0;
#pragma not-version ^1.3;
#pragma not-version ^2;
(int) main(int a) {
return a;
}