@@ -78,7 +78,7 @@ static std::unordered_set<std::string> Metamethods = {
7878 " close" s // Lua 5.4
7979};
8080
81- const std::string_view version = " 0.29.0 " sv;
81+ const std::string_view version = " 0.29.1 " sv;
8282const std::string_view extension = " yue" sv;
8383
8484class CompileError : public std ::logic_error {
@@ -5418,18 +5418,29 @@ class YueCompilerImpl {
54185418 auto macroLit = macro->decl .to <MacroLit_t>();
54195419 auto argsDef = macroLit->argsDef .get ();
54205420 str_list newArgs;
5421+ str_list argChecks;
5422+ bool hasCheck = false ;
54215423 if (argsDef) {
54225424 for (auto def_ : argsDef->definitions .objects ()) {
54235425 auto def = static_cast <FnArgDef_t*>(def_);
54245426 if (def->name .is <SelfItem_t>()) {
54255427 throw CompileError (" self name is not supported for macro function argument" sv, def->name );
54265428 } else {
5429+ if (def->op ) throw CompileError (" invalid existence checking" sv, def->op );
5430+ if (def->label ) {
5431+ hasCheck = true ;
5432+ const auto & astName = argChecks.emplace_back (_parser.toString (def->label ));
5433+ if (!_parser.hasAST (astName)) {
5434+ throw CompileError (" invalid AST name" sv, def->label );
5435+ }
5436+ } else {
5437+ argChecks.emplace_back ();
5438+ }
54275439 std::string defVal;
54285440 if (def->defaultValue ) {
54295441 defVal = _parser.toString (def->defaultValue );
54305442 Utils::trim (defVal);
5431- defVal.insert (0 , " =[==========[" sv);
5432- defVal.append (" ]==========]" sv);
5443+ defVal = ' =' + Utils::toLuaString (defVal);
54335444 }
54345445 newArgs.emplace_back (_parser.toString (def->name ) + defVal);
54355446 }
@@ -5438,6 +5449,14 @@ class YueCompilerImpl {
54385449 newArgs.emplace_back (_parser.toString (argsDef->varArg ));
54395450 }
54405451 }
5452+ if (argsDef->label ) {
5453+ hasCheck = true ;
5454+ const auto & astName = _parser.toString (argsDef->label );
5455+ if (!_parser.hasAST (astName)) {
5456+ throw CompileError (" invalid AST name" sv, argsDef->label );
5457+ }
5458+ argChecks.emplace_back (" ..." s + astName);
5459+ }
54415460 std::string macroCodes = " _ENV=require('yue').macro_env\n (" s + join (newArgs, " ," sv) + " )->" s + _parser.toString (macroLit->body );
54425461 auto chunkName = " =(macro " s + macroName + ' )' ;
54435462 pushCurrentModule (); // cur
@@ -5467,6 +5486,24 @@ class YueCompilerImpl {
54675486 throw CompileError (" failed to generate macro function\n " s + err, macroLit);
54685487 } // cur true macro
54695488 lua_remove (L, -2 ); // cur macro
5489+ if (hasCheck) {
5490+ lua_createtable (L, 0 , 0 ); // cur macro checks
5491+ int i = 1 ;
5492+ for (const auto & check : argChecks) {
5493+ if (check.empty ()) {
5494+ lua_pushboolean (L, 0 );
5495+ lua_rawseti (L, -2 , i);
5496+ } else {
5497+ lua_pushlstring (L, check.c_str (), check.size ());
5498+ lua_rawseti (L, -2 , i);
5499+ }
5500+ i++;
5501+ }
5502+ lua_createtable (L, 2 , 0 ); // cur macro checks macrotab
5503+ lua_insert (L, -3 ); // cur macrotab macro checks
5504+ lua_rawseti (L, -3 , 1 ); // macrotab[1] = checks, cur macrotab macro
5505+ lua_rawseti (L, -2 , 2 ); // macrotab[2] = macro, cur macrotab
5506+ } // cur macro
54705507 if (exporting && _config.exporting && !_config.module .empty ()) {
54715508 pushModuleTable (_config.module ); // cur macro module
54725509 lua_pushlstring (L, macroName.c_str (), macroName.size ()); // cur macro module name
@@ -5624,7 +5661,11 @@ class YueCompilerImpl {
56245661 auto def = static_cast <FnArgDef_t*>(_def);
56255662 auto & arg = argItems.emplace_back ();
56265663 switch (def->name ->get_id ()) {
5627- case id<Variable_t>(): arg.name = variableToString (static_cast <Variable_t*>(def->name .get ())); break ;
5664+ case id<Variable_t>(): {
5665+ if (def->op ) throw CompileError (" invalid existence checking" sv, def->op );
5666+ arg.name = variableToString (static_cast <Variable_t*>(def->name .get ()));
5667+ break ;
5668+ }
56285669 case id<SelfItem_t>(): {
56295670 assignSelf = true ;
56305671 if (def->op ) {
@@ -6748,7 +6789,25 @@ class YueCompilerImpl {
67486789 break ;
67496790 }
67506791 }
6751- if (!lua_isfunction (L, -1 )) {
6792+ str_list checks;
6793+ if (lua_istable (L, -1 )) {
6794+ lua_rawgeti (L, -1 , 1 ); // cur macrotab checks
6795+ int len = lua_objlen (L, -1 );
6796+ for (int i = 1 ; i <= len; i++) {
6797+ lua_rawgeti (L, -1 , i);
6798+ if (lua_toboolean (L, -1 ) == 0 ) {
6799+ checks.emplace_back ();
6800+ } else {
6801+ size_t str_len = 0 ;
6802+ auto str = lua_tolstring (L, -1 , &str_len);
6803+ checks.emplace_back (std::string{str, str_len});
6804+ }
6805+ lua_pop (L, 1 );
6806+ }
6807+ lua_pop (L, 1 );
6808+ lua_rawgeti (L, -1 , 2 ); // cur macrotab macroFunc
6809+ lua_remove (L, -2 ); // cur macroFunc
6810+ } else if (!lua_isfunction (L, -1 )) {
67526811 auto code = expandBuiltinMacro (macroName, x);
67536812 if (!code.empty ()) return code;
67546813 if (macroName == " is_ast" sv) {
@@ -6796,8 +6855,31 @@ class YueCompilerImpl {
67966855 if (!lua_checkstack (L, argStrs.size ())) {
67976856 throw CompileError (" too much macro params" s, x);
67986857 }
6858+ auto checkIt = checks.begin ();
6859+ node_container::const_iterator argIt;
6860+ if (args) {
6861+ argIt = args->begin ();
6862+ }
67996863 for (const auto & arg : argStrs) {
6864+ if (checkIt != checks.end ()) {
6865+ if (checkIt->empty ()) {
6866+ ++checkIt;
6867+ } else {
6868+ if ((*checkIt)[0 ] == ' .' ) {
6869+ auto astName = checkIt->substr (3 );
6870+ if (!_parser.match (astName, arg)) {
6871+ throw CompileError (" expecting \" " s + astName + " \" , AST mismatch" s, *argIt);
6872+ }
6873+ } else {
6874+ if (!_parser.match (*checkIt, arg)) {
6875+ throw CompileError (" expecting \" " s + *checkIt + " \" , AST mismatch" s, *argIt);
6876+ }
6877+ ++checkIt;
6878+ }
6879+ }
6880+ }
68006881 lua_pushlstring (L, arg.c_str (), arg.size ());
6882+ ++argIt;
68016883 } // cur pcall macroFunc args...
68026884 bool success = lua_pcall (L, static_cast <int >(argStrs.size ()), 1 , 0 ) == 0 ;
68036885 if (!success) { // cur err
@@ -9096,12 +9178,62 @@ class YueCompilerImpl {
90969178 out.push_back (temp.empty () ? " \"\" " s : join (temp, " .. " sv));
90979179 }
90989180
9181+ void transformYAMLMultiline (YAMLMultiline_t* multiline, str_list& out) {
9182+ std::optional<std::string_view> indent;
9183+ str_list temp;
9184+ for (auto line_ : multiline->lines .objects ()) {
9185+ auto line = static_cast <YAMLLine_t*>(line_);
9186+ if (!line->segments .empty ()) {
9187+ str_list segs;
9188+ for (auto seg_ : line->segments .objects ()) {
9189+ auto content = static_cast <YAMLLineContent_t*>(seg_)->content .get ();
9190+ switch (content->get_id ()) {
9191+ case id<YAMLLineInner_t>(): {
9192+ auto str = _parser.toString (content);
9193+ Utils::replace (str, " \r\n " sv, " \n " sv);
9194+ Utils::replace (str, " \n " sv, " \\ n" sv);
9195+ Utils::replace (str, " \\ #" sv, " #" sv);
9196+ segs.push_back (' \" ' + str + ' \" ' );
9197+ break ;
9198+ }
9199+ case id<Exp_t>(): {
9200+ transformExp (static_cast <Exp_t*>(content), segs, ExpUsage::Closure);
9201+ segs.back () = globalVar (" tostring" sv, content, AccessType::Read) + ' (' + segs.back () + ' )' ;
9202+ break ;
9203+ }
9204+ default : YUEE (" AST node mismatch" , content); break ;
9205+ }
9206+ }
9207+ auto lineStr = join (segs, " .. " sv);
9208+ if (!indent) {
9209+ auto pos = lineStr.find_first_not_of (" \t " sv, 1 );
9210+ if (pos == std::string::npos) {
9211+ throw CompileError (" expecting first line indent" sv, line);
9212+ }
9213+ indent = std::string_view{lineStr.c_str (), pos};
9214+ } else {
9215+ if (std::string_view{lineStr}.substr (0 , indent.value ().size ()) != indent.value ()) {
9216+ throw CompileError (" inconsistent indent" sv, line);
9217+ }
9218+ }
9219+ lineStr = ' "' + lineStr.substr (indent.value ().size ());
9220+ temp.push_back (lineStr);
9221+ }
9222+ }
9223+ auto str = join (temp, " .. '\\ n' .. " sv);
9224+ Utils::replace (str, " \" .. '\\ n' .. \" " sv, " \\ n" sv);
9225+ Utils::replace (str, " \" .. '\\ n'" sv, " \\ n\" " sv);
9226+ Utils::replace (str, " '\\ n' .. \" " sv, " \"\\ n" sv);
9227+ out.push_back (str);
9228+ }
9229+
90999230 void transformString (String_t* string, str_list& out) {
91009231 auto str = string->str .get ();
91019232 switch (str->get_id ()) {
91029233 case id<SingleString_t>(): transformSingleString (static_cast <SingleString_t*>(str), out); break ;
91039234 case id<DoubleString_t>(): transformDoubleString (static_cast <DoubleString_t*>(str), out); break ;
91049235 case id<LuaString_t>(): transformLuaString (static_cast <LuaString_t*>(str), out); break ;
9236+ case id<YAMLMultiline_t>(): transformYAMLMultiline (static_cast <YAMLMultiline_t*>(str), out); break ;
91059237 default : YUEE (" AST node mismatch" , str); break ;
91069238 }
91079239 }
0 commit comments