diff --git a/crypto/func/func.h b/crypto/func/func.h index 2ea1015e..c86f7e6b 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -1631,6 +1631,7 @@ inline compile_func_t make_ext_compile(AsmOp op) { struct SymValAsmFunc : SymValFunc { simple_compile_func_t simple_compile; compile_func_t ext_compile; + td::uint64 crc; ~SymValAsmFunc() override = default; SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool impure = false) : SymValFunc(-1, ft, impure), simple_compile(make_simple_compile(_macro)) { diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index 6118a433..92dfe47b 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -1270,19 +1270,48 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal lex.expect(')'); } while (lex.tp() == _String) { - asm_ops.push_back(AsmOp::Parse(lex.cur().str, cnt, width)); - lex.next(); - if (asm_ops.back().is_custom()) { - cnt = width; + std::string ops = lex.cur().str; // \n\n... + std::string op; + for (const char& c : ops) { + if (c == '\n') { + if (!op.empty()) { + asm_ops.push_back(AsmOp::Parse(op, cnt, width)); + if (asm_ops.back().is_custom()) { + cnt = width; + } + op.clear(); + } + } else { + op.push_back(c); + } } + if (!op.empty()) { + asm_ops.push_back(AsmOp::Parse(op, cnt, width)); + if (asm_ops.back().is_custom()) { + cnt = width; + } + } + lex.next(); } if (asm_ops.empty()) { throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"}; } lex.expect(';'); + std::string crc_s; + for (const AsmOp& asm_op : asm_ops) { + crc_s += asm_op.op; + } + crc_s.push_back(impure); + for (const int& x : arg_order) { + crc_s += std::string((const char*) (&x), (const char*) (&x + 1)); + } + for (const int& x : ret_order) { + crc_s += std::string((const char*) (&x), (const char*) (&x + 1)); + } auto res = new SymValAsmFunc{func_type, asm_ops, impure}; res->arg_order = std::move(arg_order); res->ret_order = std::move(ret_order); + res->crc = td::crc64(crc_s); return res; } @@ -1448,16 +1477,22 @@ void parse_func_def(Lexer& lex) { // code->print(std::cerr); // !!!DEBUG!!! func_sym_code->code = code; } else { + Lexem asm_lexem = lex.cur(); + SymValAsmFunc* asm_func = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure); if (func_sym_val) { if (dynamic_cast(func_sym_val)) { - lex.cur().error("function `"s + func_name.str + "` was already declared as an ordinary function"); + asm_lexem.error("function `"s + func_name.str + "` was already declared as an ordinary function"); } - if (dynamic_cast(func_sym_val)) { - lex.cur().error("redefinition of built-in assembler function `"s + func_name.str + "`"); + SymValAsmFunc* asm_func_old = dynamic_cast(func_sym_val); + if (asm_func_old) { + if (asm_func->crc != asm_func_old->crc) { + asm_lexem.error("redefinition of built-in assembler function `"s + func_name.str + "`"); + } + } else { + asm_lexem.error("redefinition of previously (somehow) defined function `"s + func_name.str + "`"); } - lex.cur().error("redefinition of previously (somehow) defined function `"s + func_name.str + "`"); } - func_sym->value = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure); + func_sym->value = asm_func; } if (method_id.not_null()) { auto val = dynamic_cast(func_sym->value); diff --git a/crypto/func/test/s2.fc b/crypto/func/test/s2.fc new file mode 100644 index 00000000..c6df49d5 --- /dev/null +++ b/crypto/func/test/s2.fc @@ -0,0 +1,26 @@ +slice test1() asm """ + "Test" $>s + PUSHSLICE +"""; + +slice test2() asm """ + "Hello" + " " + "World" + $+ $+ $>s + PUSHSLICE +"""; + +int sdeq (slice s1, slice s2) asm """SDEQ"""; +int sdeq (slice s1, slice s2) asm "SDEQ" ""; +int sdeq (slice s1, slice s2) asm "" """ +SDEQ +"""; + +() main() { + slice s = test1(); + throw_unless(101, sdeq(s, "Test")); + + slice s = test2(); + throw_unless(102, sdeq(s, "Hello World")); +} diff --git a/crypto/parser/lexer.cpp b/crypto/parser/lexer.cpp index 5c5b77d8..624d8dd2 100644 --- a/crypto/parser/lexer.cpp +++ b/crypto/parser/lexer.cpp @@ -125,8 +125,9 @@ int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) { } Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts, - std::string close_cmts, std::string quote_chars) - : src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined) { + std::string close_cmts, std::string quote_chars, std::string multiline_quote) + : src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined), + multiline_quote(std::move(multiline_quote)) { std::memset(char_class, 0, sizeof(char_class)); unsigned char activity = cc::active; for (char c : active_chars) { @@ -171,6 +172,19 @@ void Lexer::set_spec(std::array& arr, std::string setup) { } } +bool Lexer::is_multiline_quote(const char* begin, const char* end) { + if (multiline_quote.empty()) { + return false; + } + for (const char& c : multiline_quote) { + if (begin == end || *begin != c) { + return false; + } + ++begin; + } + return true; +} + void Lexer::expect(int exp_tp, const char* msg) { if (tp() != exp_tp) { throw ParseError{lexem.loc, (msg ? std::string{msg} : Lexem::lexem_name_str(exp_tp)) + " expected instead of " + @@ -234,6 +248,37 @@ const Lexem& Lexer::next() { } return lexem.clear(src.here(), Lexem::Eof); } + if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) { + src.advance(multiline_quote.size()); + const char* begin = src.get_ptr(); + const char* end = nullptr; + SrcLocation here = src.here(); + std::string body; + while (!src.is_eof()) { + if (src.is_eoln()) { + body.push_back('\n'); + src.load_line(); + continue; + } + if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) { + end = src.get_ptr(); + src.advance(multiline_quote.size()); + break; + } + body.push_back(src.cur_char()); + src.advance(1); + } + if (!end) { + src.error("string extends past end of file"); + } + lexem.set(body, here, Lexem::String); + int c = src.cur_char(); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + lexem.val = c; + src.advance(1); + } + return lexem; + } int c = src.cur_char(); const char* end = src.get_ptr(); if (is_quote_char(c) || c == '`') { diff --git a/crypto/parser/lexer.h b/crypto/parser/lexer.h index 12556e46..686d8eac 100644 --- a/crypto/parser/lexer.h +++ b/crypto/parser/lexer.h @@ -71,6 +71,7 @@ class Lexer { Lexem lexem, peek_lexem; unsigned char char_class[128]; std::array eol_cmt, cmt_op, cmt_cl; + std::string multiline_quote; enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 }; public: @@ -78,7 +79,8 @@ class Lexer { return eof; } Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;", - std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\""); + std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"", + std::string multiline_quote = "\"\"\""); const Lexem& next(); const Lexem& cur() const { return lexem; @@ -109,6 +111,7 @@ class Lexer { private: void set_spec(std::array& arr, std::string setup); + bool is_multiline_quote(const char* begin, const char* end); }; } // namespace src diff --git a/crypto/tl/tlbc.cpp b/crypto/tl/tlbc.cpp index 127eacb0..01b8a31a 100644 --- a/crypto/tl/tlbc.cpp +++ b/crypto/tl/tlbc.cpp @@ -2423,7 +2423,7 @@ std::vector source_fdescr; bool parse_source(std::istream* is, src::FileDescr* fdescr) { src::SourceReader reader{is, fdescr}; - src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/"}; + src::Lexer lex{reader, true, "(){}:;? #$. ^~ #", "//", "/*", "*/", ""}; while (lex.tp() != src::_Eof) { parse_constructor_def(lex); // std::cerr << lex.cur().str << '\t' << lex.cur().name_str() << std::endl;