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"
|
||||
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
|
||||
"-A and -P.\n"
|
||||
"\t-s\tOutput semantic version of FunC and exit\n"
|
||||
"\t-V<version>\tShow func build information\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
@ -182,7 +183,7 @@ std::string output_filename;
|
|||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
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) {
|
||||
case 'A':
|
||||
funC::asm_preamble = true;
|
||||
|
@ -215,8 +216,13 @@ int main(int argc, char* const argv[]) {
|
|||
funC::boc_output_filename = optarg;
|
||||
funC::asm_preamble = funC::program_envelope = true;
|
||||
break;
|
||||
case 's':
|
||||
std::cout << funC::func_version << "\n";
|
||||
std::exit(0);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case 'h':
|
||||
|
|
|
@ -38,6 +38,8 @@ extern bool op_rewrite_comments;
|
|||
|
||||
constexpr int optimize_depth = 20;
|
||||
|
||||
const std::string func_version{"0.1.0"};
|
||||
|
||||
enum Keyword {
|
||||
_Eof = -1,
|
||||
_Ident = 0,
|
||||
|
@ -106,7 +108,8 @@ enum Keyword {
|
|||
_Operator,
|
||||
_Infix,
|
||||
_Infixl,
|
||||
_Infixr
|
||||
_Infixr,
|
||||
_PragmaHashtag
|
||||
};
|
||||
|
||||
void define_keywords();
|
||||
|
|
|
@ -126,6 +126,8 @@ void define_keywords() {
|
|||
.add_keyword("infix", Kw::_Infix)
|
||||
.add_keyword("infixl", Kw::_Infixl)
|
||||
.add_keyword("infixr", Kw::_Infixr);
|
||||
|
||||
sym::symbols.add_keyword("#pragma", Kw::_PragmaHashtag);
|
||||
}
|
||||
|
||||
} // namespace funC
|
||||
|
|
|
@ -1288,13 +1288,142 @@ void parse_func_def(Lexer& 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;
|
||||
|
||||
bool parse_source(std::istream* is, src::FileDescr* fdescr) {
|
||||
src::SourceReader reader{is, fdescr};
|
||||
Lexer lex{reader, true, ";,()[] ~."};
|
||||
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);
|
||||
} else {
|
||||
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