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:
parent
0e47c6c8e0
commit
0e955793ed
5 changed files with 197 additions and 4 deletions
|
@ -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':
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
53
crypto/func/test/pv.fc
Normal 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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue