diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 88a0272b..76c8ae9d 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -298,9 +298,14 @@ add_library(src_parser ${PARSER_SOURCE}) target_include_directories(src_parser PUBLIC $) target_link_libraries(src_parser PUBLIC ton_crypto) +add_library(ton_block ${BLOCK_SOURCE}) +target_include_directories(ton_block PUBLIC $ + $ $) +target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api) + add_executable(func func/func.cpp ${FUNC_LIB_SOURCE}) target_include_directories(func PUBLIC $) -target_link_libraries(func PUBLIC ton_crypto src_parser git) +target_link_libraries(func PUBLIC ton_crypto src_parser git ton_block) if (WINGETOPT_FOUND) target_link_libraries_system(func wingetopt) endif() @@ -324,11 +329,6 @@ if (WINGETOPT_FOUND) target_link_libraries_system(pow-miner wingetopt) endif() -add_library(ton_block ${BLOCK_SOURCE}) -target_include_directories(ton_block PUBLIC $ - $ $) -target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api) - set(TURN_OFF_LSAN cd .) if (TON_USE_ASAN AND NOT WIN32) set(TURN_OFF_LSAN export LSAN_OPTIONS=detect_leaks=0) diff --git a/crypto/func/abscode.cpp b/crypto/func/abscode.cpp index 8d73c805..4c893f35 100644 --- a/crypto/func/abscode.cpp +++ b/crypto/func/abscode.cpp @@ -168,6 +168,11 @@ void VarDescr::set_const(td::RefInt256 value) { } } +void VarDescr::set_const(std::string value) { + str_const = value; + val = _Const; +} + void VarDescr::set_const_nan() { set_const(td::make_refint()); } @@ -342,6 +347,11 @@ void Op::show(std::ostream& os, const std::vector& vars, std::string pfx show_var_list(os, left, vars); os << " := " << int_const << std::endl; break; + case _SliceConst: + os << pfx << dis << "SCONST "; + show_var_list(os, left, vars); + os << " := " << str_const << std::endl; + break; case _Import: os << pfx << dis << "IMPORT "; show_var_list(os, left, vars); diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index 5af435bc..26162521 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -353,6 +353,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { } switch (cl) { case _IntConst: + case _SliceConst: case _GlobVar: case _Call: case _CallInd: @@ -540,6 +541,7 @@ bool prune_unreachable(std::unique_ptr& ops) { bool reach; switch (op.cl) { case Op::_IntConst: + case Op::_SliceConst: case Op::_GlobVar: case Op::_SetGlob: case Op::_Call: @@ -707,6 +709,10 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { values.add_newval(left[0]).set_const(int_const); break; } + case _SliceConst: { + values.add_newval(left[0]).set_const(str_const); + break; + } case _Call: { prepare_args(values); auto func = dynamic_cast(fun_ref->value); @@ -848,6 +854,7 @@ bool Op::mark_noreturn() { // fallthrough case _Import: case _IntConst: + case _SliceConst: case _Let: case _Tuple: case _UnTuple: diff --git a/crypto/func/asmops.cpp b/crypto/func/asmops.cpp index f0df9f3d..ccc409d3 100644 --- a/crypto/func/asmops.cpp +++ b/crypto/func/asmops.cpp @@ -55,10 +55,10 @@ std::ostream& operator<<(std::ostream& os, AsmOp::SReg stack_reg) { } } -AsmOp AsmOp::Const(int arg, std::string push_op) { +AsmOp AsmOp::Const(int arg, std::string push_op, td::RefInt256 origin) { std::ostringstream os; os << arg << ' ' << push_op; - return AsmOp::Const(os.str()); + return AsmOp::Const(os.str(), origin); } AsmOp AsmOp::make_stk2(int a, int b, const char* str, int delta) { @@ -166,27 +166,27 @@ AsmOp AsmOp::UnTuple(int a) { AsmOp AsmOp::IntConst(td::RefInt256 x) { if (x->signed_fits_bits(8)) { - return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT"); + return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT", x); } if (!x->is_valid()) { - return AsmOp::Const("PUSHNAN"); + return AsmOp::Const("PUSHNAN", x); } int k = is_pos_pow2(x); if (k >= 0) { - return AsmOp::Const(k, "PUSHPOW2"); + return AsmOp::Const(k, "PUSHPOW2", x); } k = is_pos_pow2(x + 1); if (k >= 0) { - return AsmOp::Const(k, "PUSHPOW2DEC"); + return AsmOp::Const(k, "PUSHPOW2DEC", x); } k = is_pos_pow2(-x); if (k >= 0) { - return AsmOp::Const(k, "PUSHNEGPOW2"); + return AsmOp::Const(k, "PUSHNEGPOW2", x); } if (!x->mod_pow2_short(23)) { - return AsmOp::Const(dec_string(std::move(x)) + " PUSHINTX"); + return AsmOp::Const(dec_string(std::move(x)) + " PUSHINTX", x); } - return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT"); + return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT", x); } AsmOp AsmOp::BoolConst(bool f) { diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 91020be0..075d8f62 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -27,7 +27,7 @@ using namespace std::literals::string_literals; * */ -int glob_func_cnt, undef_func_cnt, glob_var_cnt; +int glob_func_cnt, undef_func_cnt, glob_var_cnt, const_cnt; std::vector glob_func, glob_vars; SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) { diff --git a/crypto/func/codegen.cpp b/crypto/func/codegen.cpp index 26d8215e..3e58b995 100644 --- a/crypto/func/codegen.cpp +++ b/crypto/func/codegen.cpp @@ -302,6 +302,15 @@ bool Op::generate_code_step(Stack& stack) { } return true; } + case _SliceConst: { + auto p = next_var_info[left[0]]; + if (!p || p->is_unused()) { + return true; + } + stack.o << AsmOp::Const("x{" + str_const + "} PUSHSLICE"); + stack.push_new_var(left[0]); + return true; + } case _GlobVar: if (dynamic_cast(fun_ref->value)) { bool used = false; diff --git a/crypto/func/func.h b/crypto/func/func.h index 22935dfe..751e50a9 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -109,6 +109,7 @@ enum Keyword { _Infix, _Infixl, _Infixr, + _Const, _PragmaHashtag }; @@ -336,6 +337,8 @@ struct VarDescr { static constexpr int FiniteUInt = FiniteInt | _Pos; int val; td::RefInt256 int_const; + std::string str_const; + VarDescr(var_idx_t _idx = -1, int _flags = 0, int _val = 0) : idx(_idx), flags(_flags), val(_val) { } bool operator<(var_idx_t other_idx) const { @@ -406,6 +409,7 @@ struct VarDescr { } void set_const(long long value); void set_const(td::RefInt256 value); + void set_const(std::string value); void set_const_nan(); void operator+=(const VarDescr& y) { flags &= y.flags; @@ -530,7 +534,8 @@ struct Op { _While, _Until, _Repeat, - _Again + _Again, + _SliceConst }; int cl; enum { _Disabled = 1, _Reachable = 2, _NoReturn = 4, _ImpureR = 8, _ImpureW = 16, _Impure = 24 }; @@ -543,6 +548,7 @@ struct Op { std::vector left, right; std::unique_ptr block0, block1; td::RefInt256 int_const; + std::string str_const; Op(const SrcLocation& _where = {}, int _cl = _Undef) : cl(_cl), flags(0), fun_ref(nullptr), where(_where) { } Op(const SrcLocation& _where, int _cl, const std::vector& _left) @@ -554,6 +560,9 @@ struct Op { Op(const SrcLocation& _where, int _cl, const std::vector& _left, td::RefInt256 _const) : cl(_cl), flags(0), fun_ref(nullptr), where(_where), left(_left), int_const(_const) { } + Op(const SrcLocation& _where, int _cl, const std::vector& _left, std::string _const) + : cl(_cl), flags(0), fun_ref(nullptr), where(_where), left(_left), str_const(_const) { + } Op(const SrcLocation& _where, int _cl, const std::vector& _left, const std::vector& _right, SymDef* _fun = nullptr) : cl(_cl), flags(0), fun_ref(_fun), where(_where), left(_left), right(_right) { @@ -784,6 +793,30 @@ struct SymValGlobVar : sym::SymValBase { } }; +struct SymValConst : sym::SymValBase { + td::RefInt256 intval; + std::string strval; + Keyword type; + SymValConst(int idx, td::RefInt256 value) + : sym::SymValBase(_Const, idx), intval(value) { + type = _Int; + } + SymValConst(int idx, std::string value) + : sym::SymValBase(_Const, idx), strval(value) { + type = _Slice; + } + ~SymValConst() override = default; + td::RefInt256 get_int_value() const { + return intval; + } + std::string get_str_value() const { + return strval; + } + Keyword get_type() const { + return type; + } +}; + extern int glob_func_cnt, undef_func_cnt, glob_var_cnt; extern std::vector glob_func, glob_vars; @@ -820,7 +853,8 @@ struct Expr { _LetFirst, _Hole, _Type, - _CondExpr + _CondExpr, + _SliceConst }; int cls; int val{0}; @@ -828,6 +862,7 @@ struct Expr { int flags{0}; SrcLocation here; td::RefInt256 intval; + std::string strval; SymDef* sym{nullptr}; TypeExpr* e_type{nullptr}; std::vector args; @@ -910,6 +945,7 @@ struct AsmOp { int a, b, c; bool gconst{false}; std::string op; + td::RefInt256 origin; struct SReg { int idx; SReg(int _idx) : idx(_idx) { @@ -929,6 +965,9 @@ struct AsmOp { AsmOp(int _t, int _a, int _b, std::string _op) : t(_t), a(_a), b(_b), op(std::move(_op)) { compute_gconst(); } + AsmOp(int _t, int _a, int _b, std::string _op, td::RefInt256 x) : t(_t), a(_a), b(_b), op(std::move(_op)), origin(x) { + compute_gconst(); + } AsmOp(int _t, int _a, int _b, int _c) : t(_t), a(_a), b(_b), c(_c) { } AsmOp(int _t, int _a, int _b, int _c, std::string _op) : t(_t), a(_a), b(_b), c(_c), op(std::move(_op)) { @@ -1047,10 +1086,10 @@ struct AsmOp { static AsmOp make_stk3(int a, int b, int c, const char* str, int delta); static AsmOp IntConst(td::RefInt256 value); static AsmOp BoolConst(bool f); - static AsmOp Const(std::string push_op) { - return AsmOp(a_const, 0, 1, std::move(push_op)); + static AsmOp Const(std::string push_op, td::RefInt256 origin = {}) { + return AsmOp(a_const, 0, 1, std::move(push_op), origin); } - static AsmOp Const(int arg, std::string push_op); + static AsmOp Const(int arg, std::string push_op, td::RefInt256 origin = {}); static AsmOp Comment(std::string comment) { return AsmOp(a_none, std::string{"// "} + comment); } diff --git a/crypto/func/gen-abscode.cpp b/crypto/func/gen-abscode.cpp index c0a8985a..21dce714 100644 --- a/crypto/func/gen-abscode.cpp +++ b/crypto/func/gen-abscode.cpp @@ -362,6 +362,11 @@ std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { code.close_pop_cur(args[2]->here); return rvect; } + case _SliceConst: { + auto rvect = new_tmp_vect(code); + code.emplace_back(here, Op::_SliceConst, rvect, strval); + return rvect; + } default: std::cerr << "expression constructor is " << cls << std::endl; throw src::Fatal{"cannot compile expression with unknown constructor"}; diff --git a/crypto/func/keywords.cpp b/crypto/func/keywords.cpp index 785c7765..b7bba0c2 100644 --- a/crypto/func/keywords.cpp +++ b/crypto/func/keywords.cpp @@ -125,8 +125,8 @@ void define_keywords() { .add_keyword("operator", Kw::_Operator) .add_keyword("infix", Kw::_Infix) .add_keyword("infixl", Kw::_Infixl) - .add_keyword("infixr", Kw::_Infixr); - + .add_keyword("infixr", Kw::_Infixr) + .add_keyword("const", Kw::_Const); sym::symbols.add_keyword("#pragma", Kw::_PragmaHashtag); } diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index d340a78c..929f5b3c 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -19,6 +19,9 @@ #include "func.h" #include "td/utils/crypto.h" #include "common/refint.h" +#include "openssl/digest.hpp" +#include "block/block.h" +#include "block-parse.h" #include namespace sym { @@ -229,6 +232,83 @@ void parse_global_var_decl(Lexer& lex) { lex.next(); } +extern int const_cnt; +Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false); + +void parse_const_decl(Lexer& lex) { + SrcLocation loc = lex.cur().loc; + int wanted_type = Expr::_None; + if (lex.tp() == _Int) { + wanted_type = Expr::_Const; + lex.next(); + } else if (lex.tp() == _Slice) { + wanted_type = Expr::_SliceConst; + lex.next(); + } + if (lex.tp() != _Ident) { + lex.expect(_Ident, "constant name"); + } + loc = lex.cur().loc; + SymDef* sym_def = sym::define_global_symbol(lex.cur().val, false, loc); + if (!sym_def) { + lex.cur().error_at("cannot define global symbol `", "`"); + } + if (sym_def->value) { + lex.cur().error_at("global symbol `", "` already exists"); + } + lex.next(); + if (lex.tp() != '=') { + lex.cur().error_at("expected = instead of ", ""); + } + lex.next(); + CodeBlob code; + // Handles processing and resolution of literals and consts + auto x = parse_expr(lex, code, false); // also does lex.next() ! + if (x->flags != Expr::_IsRvalue) { + lex.cur().error("expression is not strictly Rvalue"); + } + if ((wanted_type == Expr::_Const) && (x->cls == Expr::_Apply)) + wanted_type = Expr::_None; // Apply is additionally checked to result in an integer + if ((wanted_type != Expr::_None) && (x->cls != wanted_type)) { + lex.cur().error("expression type does not match wanted type"); + } + if (x->cls == Expr::_Const) { // Integer constant + sym_def->value = new SymValConst{const_cnt++, x->intval}; + } else if (x->cls == Expr::_SliceConst) { // Slice constant (string) + sym_def->value = new SymValConst{const_cnt++, x->strval}; + } else if (x->cls == Expr::_Apply) { + code.emplace_back(loc, Op::_Import, std::vector()); + auto tmp_vars = x->pre_compile(code); + code.emplace_back(loc, Op::_Return, std::move(tmp_vars)); + code.emplace_back(loc, Op::_Nop); // This is neccessary to prevent SIGSEGV! + // It is REQUIRED to execute "optimizations" as in func.cpp + code.simplify_var_types(); + code.prune_unreachable_code(); + code.split_vars(true); + for (int i = 0; i < 16; i++) { + code.compute_used_code_vars(); + code.fwd_analyze(); + code.prune_unreachable_code(); + } + code.mark_noreturn(); + AsmOpList out_list(0, &code.vars); + code.generate_code(out_list); + if (out_list.list_.size() != 1) { + lex.cur().error("precompiled expression must result in single operation"); + } + auto op = out_list.list_[0]; + if (!op.is_const()) { + lex.cur().error("precompiled expression must result in compilation time constant"); + } + if (op.origin.is_null() || !op.origin->is_valid()) { + lex.cur().error("precompiled expression did not result in a valid integer constant"); + } + sym_def->value = new SymValConst{const_cnt++, op.origin}; + } else { + lex.cur().error("integer or slice literal or constant expected"); + } +} + FormalArgList parse_formal_args(Lexer& lex) { FormalArgList args; lex.expect('(', "formal argument list"); @@ -246,6 +326,18 @@ FormalArgList parse_formal_args(Lexer& lex) { return args; } +void parse_const_decls(Lexer& lex) { + lex.expect(_Const); + while (true) { + parse_const_decl(lex); + if (lex.tp() != ',') { + break; + } + lex.expect(','); + } + lex.expect(';'); +} + TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) { if (arg_list.empty()) { return TypeExpr::new_unit(); @@ -322,8 +414,6 @@ Expr* make_func_apply(Expr* fun, Expr* x) { return res; } -Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false); - // parse ( E { , E } ) | () | [ E { , E } ] | [] | id | num | _ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { if (lex.tp() == '(' || lex.tp() == '[') { @@ -388,6 +478,87 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { lex.next(); return res; } + if (t == Lexem::String) { + std::string str = lex.cur().str; + int str_type = lex.cur().val; + Expr* res; + switch (str_type) { + case 0: + case 's': + case 'a': + { + res = new Expr{Expr::_SliceConst, lex.cur().loc}; + res->e_type = TypeExpr::new_atomic(_Slice); + break; + } + case 'u': + case 'h': + case 'H': + case 'c': + { + res = new Expr{Expr::_Const, lex.cur().loc}; + res->e_type = TypeExpr::new_atomic(_Int); + break; + } + default: + { + res = new Expr{Expr::_Const, lex.cur().loc}; + res->e_type = TypeExpr::new_atomic(_Int); + lex.cur().error("invalid string type `" + std::string(1, static_cast(str_type)) + "`"); + return res; + } + } + res->flags = Expr::_IsRvalue; + switch (str_type) { + case 0: { + res->strval = td::hex_encode(str); + break; + } + case 's': { + res->strval = str; + unsigned char buff[128]; + int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.data(), str.data() + str.size()); + if (bits < 0) { + lex.cur().error_at("Invalid hex bitstring constant `", "`"); + } + break; + } + case 'a': { // MsgAddressInt + block::StdAddress a; + if (a.parse_addr(str)) { + res->strval = block::tlb::MsgAddressInt().pack_std_address(a)->as_bitslice().to_hex(); + } else { + lex.cur().error_at("invalid standard address `", "`"); + } + break; + } + case 'u': { + res->intval = td::hex_string_to_int256(td::hex_encode(str)); + if (!str.size()) { + lex.cur().error("empty integer ascii-constant"); + } + if (res->intval.is_null()) { + lex.cur().error_at("too long integer ascii-constant `", "`"); + } + break; + } + case 'h': + case 'H': + { + unsigned char hash[32]; + digest::hash_str(hash, str.data(), str.size()); + res->intval = td::bits_to_refint(hash, (str_type == 'h') ? 32 : 256, false); + break; + } + case 'c': + { + res->intval = td::make_refint(td::crc32(td::Slice{str})); + break; + } + } + lex.next(); + return res; + } if (t == '_') { Expr* res = new Expr{Expr::_Hole, lex.cur().loc}; res->val = -1; @@ -429,6 +600,25 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { lex.next(); return res; } + if (sym && dynamic_cast(sym->value)) { + auto val = dynamic_cast(sym->value); + Expr* res = new Expr{Expr::_None, lex.cur().loc}; + res->flags = Expr::_IsRvalue; + if (val->type == _Int) { + res->cls = Expr::_Const; + res->intval = val->get_int_value(); + } + else if (val->type == _Slice) { + res->cls = Expr::_SliceConst; + res->strval = val->get_str_value(); + } + else { + lex.cur().error("Invalid symbolic constant type"); + } + res->e_type = TypeExpr::new_atomic(val->type); + lex.next(); + return res; + } bool auto_apply = false; Expr* res = new Expr{Expr::_Var, lex.cur().loc}; if (nv) { @@ -1425,6 +1615,8 @@ bool parse_source(std::istream* is, src::FileDescr* fdescr) { parse_pragma(lex); } else if (lex.tp() == _Global) { parse_global_var_decls(lex); + } else if (lex.tp() == _Const) { + parse_const_decls(lex); } else { parse_func_def(lex); } diff --git a/crypto/func/test/co1.fc b/crypto/func/test/co1.fc new file mode 100644 index 00000000..82071e45 --- /dev/null +++ b/crypto/func/test/co1.fc @@ -0,0 +1,55 @@ +const int1 = 1, int2 = 2; + +const int int101 = 101; +const int int111 = 111; + +const int1r = int1; + +const str1 = "const1", str2 = "aabbcc"s; + +const slice str2r = str2; + +const str1int = 0x636f6e737431; +const str2int = 0xAABBCC; + +const int nibbles = 4; + +int iget1() { return int1; } +int iget2() { return int2; } +int iget3() { return int1 + int2; } + +int iget1r() { return int1r; } + +slice sget1() { return str1; } +slice sget2() { return str2; } +slice sget2r() { return str2r; } + +const int int240 = ((int1 + int2) * 10) << 3; + +int iget240() { return int240; } + +builder newc() asm "NEWC"; +slice endcs(builder b) asm "ENDC" "CTOS"; +int sdeq (slice s1, slice s2) asm "SDEQ"; +builder stslicer(builder b, slice s) asm "STSLICER"; + +_ main() { + int i1 = iget1(); + int i2 = iget2(); + int i3 = iget3(); + + throw_unless(int101, i1 == 1); + throw_unless(102, i2 == 2); + throw_unless(103, i3 == 3); + + slice s1 = sget1(); + slice s2 = sget2(); + slice s3 = newc().stslicer(str1).stslicer(str2r).endcs(); + + throw_unless(int111, sdeq(s1, newc().store_uint(str1int, 12 * nibbles).endcs())); + throw_unless(112, sdeq(s2, newc().store_uint(str2int, 6 * nibbles).endcs())); + throw_unless(113, sdeq(s3, newc().store_uint(0x636f6e737431ABCDEF, 18 * nibbles).endcs())); + + int i4 = iget240(); + throw_unless(104, i4 == 240); +} diff --git a/crypto/func/test/s1.fc b/crypto/func/test/s1.fc new file mode 100644 index 00000000..630d0180 --- /dev/null +++ b/crypto/func/test/s1.fc @@ -0,0 +1,49 @@ +slice ascii_slice() method_id { + return "string"; +} + +slice raw_slice() method_id { + return "abcdef"s; +} + +slice addr_slice() method_id { + return "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF"a; +} + +int string_hex() method_id { + return "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"u; +} + +int string_minihash() method_id { + return "transfer(slice, int)"h; +} + +int string_maxihash() method_id { + return "transfer(slice, int)"H; +} + +int string_crc32() method_id { + return "transfer(slice, int)"c; +} + +builder newc() asm "NEWC"; +slice endcs(builder b) asm "ENDC" "CTOS"; +int sdeq (slice s1, slice s2) asm "SDEQ"; + +_ main() { + slice s_ascii = ascii_slice(); + slice s_raw = raw_slice(); + slice s_addr = addr_slice(); + int i_hex = string_hex(); + int i_mini = string_minihash(); + int i_maxi = string_maxihash(); + int i_crc = string_crc32(); + throw_unless(101, sdeq(s_ascii, newc().store_uint(0x737472696E67, 12 * 4).endcs())); + throw_unless(102, sdeq(s_raw, newc().store_uint(0xABCDEF, 6 * 4).endcs())); + throw_unless(103, sdeq(s_addr, newc().store_uint(4, 3).store_int(-1, 8) + .store_uint(0x3333333333333333333333333333333333333333333333333333333333333333, 256).endcs())); + throw_unless(104, i_hex == 0x4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435); + throw_unless(105, i_mini == 0x7a62e8a8); + throw_unless(106, i_maxi == 0x7a62e8a8ebac41bd6de16c65e7be363bc2d2cbc6a0873778dead4795c13db979); + throw_unless(107, i_crc == 2235694568); +} diff --git a/crypto/parser/lexer.cpp b/crypto/parser/lexer.cpp index 87c63c3f..5c5b77d8 100644 --- a/crypto/parser/lexer.cpp +++ b/crypto/parser/lexer.cpp @@ -247,6 +247,11 @@ const Lexem& Lexer::next() { } lexem.set(std::string{src.get_ptr() + 1, end}, src.here(), qc == '`' ? Lexem::Unknown : Lexem::String); src.set_ptr(end + 1); + c = src.cur_char(); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + lexem.val = c; + src.set_ptr(end + 2); + } // std::cerr << lexem.name_str() << ' ' << lexem.str << std::endl; return lexem; } diff --git a/crypto/parser/symtable.h b/crypto/parser/symtable.h index 81a828a8..9489b2bc 100644 --- a/crypto/parser/symtable.h +++ b/crypto/parser/symtable.h @@ -32,7 +32,7 @@ namespace sym { typedef int var_idx_t; struct SymValBase { - enum { _Param, _Var, _Func, _Typename, _GlobVar }; + enum { _Param, _Var, _Func, _Typename, _GlobVar, _Const }; int type; int idx; SymValBase(int _type, int _idx) : type(_type), idx(_idx) {