aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2025-07-17 18:02:39 +0800
committerLi Jin <dragon-fly@qq.com>2025-07-17 18:02:39 +0800
commitc03bf36db11bcd90034b0e67bd1f5c8c0765eb7f (patch)
tree18d18739cb2b99600f0d93fb9d8e7097a8584a7e /src
parent281149d74af1d218cc43fe57b1d68d3759ce6d0c (diff)
downloadyuescript-c03bf36db11bcd90034b0e67bd1f5c8c0765eb7f.tar.gz
yuescript-c03bf36db11bcd90034b0e67bd1f5c8c0765eb7f.tar.bz2
yuescript-c03bf36db11bcd90034b0e67bd1f5c8c0765eb7f.zip
Added YAML multiline string and macro argument checking.
Diffstat (limited to 'src')
-rw-r--r--src/yuescript/yue_ast.cpp24
-rw-r--r--src/yuescript/yue_ast.h28
-rw-r--r--src/yuescript/yue_compiler.cpp142
-rw-r--r--src/yuescript/yue_parser.cpp34
-rw-r--r--src/yuescript/yue_parser.h5
5 files changed, 221 insertions, 12 deletions
diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp
index da99d07..bacdc01 100644
--- a/src/yuescript/yue_ast.cpp
+++ b/src/yuescript/yue_ast.cpp
@@ -962,6 +962,30 @@ std::string DoubleString_t::to_string(void* ud) const {
962 } 962 }
963 return '"' + join(temp) + '"'; 963 return '"' + join(temp) + '"';
964} 964}
965std::string YAMLLineInner_t::to_string(void* ud) const {
966 auto info = reinterpret_cast<YueFormat*>(ud);
967 return info->convert(this);
968}
969std::string YAMLLineContent_t::to_string(void* ud) const {
970 if (content.is<Exp_t>()) {
971 return "#{"s + content->to_string(ud) + '}';
972 }
973 return content->to_string(ud);
974}
975std::string YAMLLine_t::to_string(void* ud) const {
976 str_list temp;
977 for (auto seg : segments.objects()) {
978 temp.emplace_back(seg->to_string(ud));
979 }
980 return join(temp);
981}
982std::string YAMLMultiline_t::to_string(void* ud) const {
983 str_list temp;
984 for (auto seg : lines.objects()) {
985 temp.emplace_back(seg->to_string(ud));
986 }
987 return "|\n" + join(temp, "\n"sv);
988}
965std::string String_t::to_string(void* ud) const { 989std::string String_t::to_string(void* ud) const {
966 return str->to_string(ud); 990 return str->to_string(ud);
967} 991}
diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h
index d396d82..c529f34 100644
--- a/src/yuescript/yue_ast.h
+++ b/src/yuescript/yue_ast.h
@@ -591,8 +591,28 @@ AST_NODE(DoubleString)
591 AST_MEMBER(DoubleString, &sep, &segments) 591 AST_MEMBER(DoubleString, &sep, &segments)
592AST_END(DoubleString) 592AST_END(DoubleString)
593 593
594AST_LEAF(YAMLLineInner)
595AST_END(YAMLLineInner)
596
597AST_NODE(YAMLLineContent)
598 ast_sel<true, YAMLLineInner_t, Exp_t> content;
599 AST_MEMBER(YAMLLineContent, &content)
600AST_END(YAMLLineContent)
601
602AST_NODE(YAMLLine)
603 ast_ptr<true, Seperator_t> sep;
604 ast_list<false, YAMLLineContent_t> segments;
605 AST_MEMBER(YAMLLine, &sep, &segments)
606AST_END(YAMLLine)
607
608AST_NODE(YAMLMultiline)
609 ast_ptr<true, Seperator_t> sep;
610 ast_list<true, YAMLLine_t> lines;
611 AST_MEMBER(YAMLMultiline, &sep, &lines)
612AST_END(YAMLMultiline)
613
594AST_NODE(String) 614AST_NODE(String)
595 ast_sel<true, DoubleString_t, SingleString_t, LuaString_t> str; 615 ast_sel<true, DoubleString_t, SingleString_t, LuaString_t, YAMLMultiline_t> str;
596 AST_MEMBER(String, &str) 616 AST_MEMBER(String, &str)
597AST_END(String) 617AST_END(String)
598 618
@@ -752,15 +772,17 @@ AST_END(Export)
752AST_NODE(FnArgDef) 772AST_NODE(FnArgDef)
753 ast_sel<true, Variable_t, SelfItem_t> name; 773 ast_sel<true, Variable_t, SelfItem_t> name;
754 ast_ptr<false, ExistentialOp_t> op; 774 ast_ptr<false, ExistentialOp_t> op;
775 ast_ptr<false, Name_t> label;
755 ast_ptr<false, Exp_t> defaultValue; 776 ast_ptr<false, Exp_t> defaultValue;
756 AST_MEMBER(FnArgDef, &name, &op, &defaultValue) 777 AST_MEMBER(FnArgDef, &name, &op, &label, &defaultValue)
757AST_END(FnArgDef) 778AST_END(FnArgDef)
758 779
759AST_NODE(FnArgDefList) 780AST_NODE(FnArgDefList)
760 ast_ptr<true, Seperator_t> sep; 781 ast_ptr<true, Seperator_t> sep;
761 ast_list<false, FnArgDef_t> definitions; 782 ast_list<false, FnArgDef_t> definitions;
762 ast_ptr<false, VarArg_t> varArg; 783 ast_ptr<false, VarArg_t> varArg;
763 AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg) 784 ast_ptr<false, Name_t> label;
785 AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg, &label)
764AST_END(FnArgDefList) 786AST_END(FnArgDefList)
765 787
766AST_NODE(OuterVarShadow) 788AST_NODE(OuterVarShadow)
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp
index 9f5a41e..35d99bd 100644
--- a/src/yuescript/yue_compiler.cpp
+++ b/src/yuescript/yue_compiler.cpp
@@ -78,7 +78,7 @@ static std::unordered_set<std::string> Metamethods = {
78 "close"s // Lua 5.4 78 "close"s // Lua 5.4
79}; 79};
80 80
81const std::string_view version = "0.29.0"sv; 81const std::string_view version = "0.29.1"sv;
82const std::string_view extension = "yue"sv; 82const std::string_view extension = "yue"sv;
83 83
84class CompileError : public std::logic_error { 84class CompileError : public std::logic_error {
@@ -5418,18 +5418,29 @@ private:
5418 auto macroLit = macro->decl.to<MacroLit_t>(); 5418 auto macroLit = macro->decl.to<MacroLit_t>();
5419 auto argsDef = macroLit->argsDef.get(); 5419 auto argsDef = macroLit->argsDef.get();
5420 str_list newArgs; 5420 str_list newArgs;
5421 str_list argChecks;
5422 bool hasCheck = false;
5421 if (argsDef) { 5423 if (argsDef) {
5422 for (auto def_ : argsDef->definitions.objects()) { 5424 for (auto def_ : argsDef->definitions.objects()) {
5423 auto def = static_cast<FnArgDef_t*>(def_); 5425 auto def = static_cast<FnArgDef_t*>(def_);
5424 if (def->name.is<SelfItem_t>()) { 5426 if (def->name.is<SelfItem_t>()) {
5425 throw CompileError("self name is not supported for macro function argument"sv, def->name); 5427 throw CompileError("self name is not supported for macro function argument"sv, def->name);
5426 } else { 5428 } 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 }
5427 std::string defVal; 5439 std::string defVal;
5428 if (def->defaultValue) { 5440 if (def->defaultValue) {
5429 defVal = _parser.toString(def->defaultValue); 5441 defVal = _parser.toString(def->defaultValue);
5430 Utils::trim(defVal); 5442 Utils::trim(defVal);
5431 defVal.insert(0, "=[==========["sv); 5443 defVal = '=' + Utils::toLuaString(defVal);
5432 defVal.append("]==========]"sv);
5433 } 5444 }
5434 newArgs.emplace_back(_parser.toString(def->name) + defVal); 5445 newArgs.emplace_back(_parser.toString(def->name) + defVal);
5435 } 5446 }
@@ -5438,6 +5449,14 @@ private:
5438 newArgs.emplace_back(_parser.toString(argsDef->varArg)); 5449 newArgs.emplace_back(_parser.toString(argsDef->varArg));
5439 } 5450 }
5440 } 5451 }
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 }
5441 std::string macroCodes = "_ENV=require('yue').macro_env\n("s + join(newArgs, ","sv) + ")->"s + _parser.toString(macroLit->body); 5460 std::string macroCodes = "_ENV=require('yue').macro_env\n("s + join(newArgs, ","sv) + ")->"s + _parser.toString(macroLit->body);
5442 auto chunkName = "=(macro "s + macroName + ')'; 5461 auto chunkName = "=(macro "s + macroName + ')';
5443 pushCurrentModule(); // cur 5462 pushCurrentModule(); // cur
@@ -5467,6 +5486,24 @@ private:
5467 throw CompileError("failed to generate macro function\n"s + err, macroLit); 5486 throw CompileError("failed to generate macro function\n"s + err, macroLit);
5468 } // cur true macro 5487 } // cur true macro
5469 lua_remove(L, -2); // cur macro 5488 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
5470 if (exporting && _config.exporting && !_config.module.empty()) { 5507 if (exporting && _config.exporting && !_config.module.empty()) {
5471 pushModuleTable(_config.module); // cur macro module 5508 pushModuleTable(_config.module); // cur macro module
5472 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro module name 5509 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro module name
@@ -5624,7 +5661,11 @@ private:
5624 auto def = static_cast<FnArgDef_t*>(_def); 5661 auto def = static_cast<FnArgDef_t*>(_def);
5625 auto& arg = argItems.emplace_back(); 5662 auto& arg = argItems.emplace_back();
5626 switch (def->name->get_id()) { 5663 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 }
5628 case id<SelfItem_t>(): { 5669 case id<SelfItem_t>(): {
5629 assignSelf = true; 5670 assignSelf = true;
5630 if (def->op) { 5671 if (def->op) {
@@ -6748,7 +6789,25 @@ private:
6748 break; 6789 break;
6749 } 6790 }
6750 } 6791 }
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)) {
6752 auto code = expandBuiltinMacro(macroName, x); 6811 auto code = expandBuiltinMacro(macroName, x);
6753 if (!code.empty()) return code; 6812 if (!code.empty()) return code;
6754 if (macroName == "is_ast"sv) { 6813 if (macroName == "is_ast"sv) {
@@ -6796,8 +6855,31 @@ private:
6796 if (!lua_checkstack(L, argStrs.size())) { 6855 if (!lua_checkstack(L, argStrs.size())) {
6797 throw CompileError("too much macro params"s, x); 6856 throw CompileError("too much macro params"s, x);
6798 } 6857 }
6858 auto checkIt = checks.begin();
6859 node_container::const_iterator argIt;
6860 if (args) {
6861 argIt = args->begin();
6862 }
6799 for (const auto& arg : argStrs) { 6863 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 }
6800 lua_pushlstring(L, arg.c_str(), arg.size()); 6881 lua_pushlstring(L, arg.c_str(), arg.size());
6882 ++argIt;
6801 } // cur pcall macroFunc args... 6883 } // cur pcall macroFunc args...
6802 bool success = lua_pcall(L, static_cast<int>(argStrs.size()), 1, 0) == 0; 6884 bool success = lua_pcall(L, static_cast<int>(argStrs.size()), 1, 0) == 0;
6803 if (!success) { // cur err 6885 if (!success) { // cur err
@@ -9096,12 +9178,62 @@ private:
9096 out.push_back(temp.empty() ? "\"\""s : join(temp, " .. "sv)); 9178 out.push_back(temp.empty() ? "\"\""s : join(temp, " .. "sv));
9097 } 9179 }
9098 9180
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
9099 void transformString(String_t* string, str_list& out) { 9230 void transformString(String_t* string, str_list& out) {
9100 auto str = string->str.get(); 9231 auto str = string->str.get();
9101 switch (str->get_id()) { 9232 switch (str->get_id()) {
9102 case id<SingleString_t>(): transformSingleString(static_cast<SingleString_t*>(str), out); break; 9233 case id<SingleString_t>(): transformSingleString(static_cast<SingleString_t*>(str), out); break;
9103 case id<DoubleString_t>(): transformDoubleString(static_cast<DoubleString_t*>(str), out); break; 9234 case id<DoubleString_t>(): transformDoubleString(static_cast<DoubleString_t*>(str), out); break;
9104 case id<LuaString_t>(): transformLuaString(static_cast<LuaString_t*>(str), out); break; 9235 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;
9105 default: YUEE("AST node mismatch", str); break; 9237 default: YUEE("AST node mismatch", str); break;
9106 } 9238 }
9107 } 9239 }
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp
index 1011a49..eebc676 100644
--- a/src/yuescript/yue_parser.cpp
+++ b/src/yuescript/yue_parser.cpp
@@ -639,7 +639,15 @@ YueParser::YueParser() {
639 DoubleStringInner = +(not_("#{") >> double_string_plain); 639 DoubleStringInner = +(not_("#{") >> double_string_plain);
640 DoubleStringContent = DoubleStringInner | interp; 640 DoubleStringContent = DoubleStringInner | interp;
641 DoubleString = '"' >> Seperator >> *DoubleStringContent >> '"'; 641 DoubleString = '"' >> Seperator >> *DoubleStringContent >> '"';
642 String = DoubleString | SingleString | LuaString; 642
643 YAMLLineInner = +('\\' >> set("\"\\#") | not_("#{" | stop) >> any_char);
644 YAMLLineContent = YAMLLineInner | interp;
645 YAMLLine = check_indent_match >> Seperator >> +YAMLLineContent |
646 advance_match >> Seperator >> ensure(+YAMLLineContent, pop_indent) |
647 Seperator >> *set(" \t") >> and_(line_break);
648 YAMLMultiline = '|' >> Seperator >> +space_break >> advance_match >> ensure(YAMLLine >> *(*set(" \t") >> line_break >> YAMLLine), pop_indent);
649
650 String = DoubleString | SingleString | LuaString | YAMLMultiline;
643 651
644 lua_string_open = '[' >> *expr('=') >> '['; 652 lua_string_open = '[' >> *expr('=') >> '[';
645 lua_string_close = ']' >> *expr('=') >> ']'; 653 lua_string_close = ']' >> *expr('=') >> ']';
@@ -883,11 +891,11 @@ YueParser::YueParser() {
883 891
884 fn_arg_def_lit_lines = fn_arg_def_lit_line >> *(-(space >> ',') >> space_break >> fn_arg_def_lit_line); 892 fn_arg_def_lit_lines = fn_arg_def_lit_line >> *(-(space >> ',') >> space_break >> fn_arg_def_lit_line);
885 893
886 FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '=' >> space >> Exp); 894 FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '`' >> space >> Name) >> -(space >> '=' >> space >> Exp);
887 895
888 FnArgDefList = Seperator >> ( 896 FnArgDefList = Seperator >> (
889 fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg) | 897 fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg >> -(space >> '`' >> space >> Name)) |
890 white >> VarArg 898 white >> VarArg >> -(space >> '`' >> space >> Name)
891 ); 899 );
892 900
893 OuterVarShadow = key("using") >> space >> (NameList | key("nil")); 901 OuterVarShadow = key("using") >> space >> (NameList | key("nil"));
@@ -1176,6 +1184,24 @@ void trim(std::string& str) {
1176 str.erase(0, str.find_first_not_of(" \t\r\n")); 1184 str.erase(0, str.find_first_not_of(" \t\r\n"));
1177 str.erase(str.find_last_not_of(" \t\r\n") + 1); 1185 str.erase(str.find_last_not_of(" \t\r\n") + 1);
1178} 1186}
1187
1188std::string toLuaString(const std::string& input) {
1189 std::string luaStr = "\"";
1190 for (char c : input) {
1191 switch (c) {
1192 case '\"': luaStr += "\\\""; break;
1193 case '\\': luaStr += "\\\\"; break;
1194 case '\n': luaStr += "\\n"; break;
1195 case '\r': luaStr += "\\r"; break;
1196 case '\t': luaStr += "\\t"; break;
1197 default:
1198 luaStr += c;
1199 break;
1200 }
1201 }
1202 luaStr += "\"";
1203 return luaStr;
1204}
1179} // namespace Utils 1205} // namespace Utils
1180 1206
1181std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol, int lineOffset) const { 1207std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol, int lineOffset) const {
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h
index 1057626..15f9277 100644
--- a/src/yuescript/yue_parser.h
+++ b/src/yuescript/yue_parser.h
@@ -378,6 +378,10 @@ private:
378 AST_RULE(DoubleStringInner); 378 AST_RULE(DoubleStringInner);
379 AST_RULE(DoubleStringContent); 379 AST_RULE(DoubleStringContent);
380 AST_RULE(DoubleString); 380 AST_RULE(DoubleString);
381 AST_RULE(YAMLLineInner);
382 AST_RULE(YAMLLineContent);
383 AST_RULE(YAMLLine);
384 AST_RULE(YAMLMultiline);
381 AST_RULE(String); 385 AST_RULE(String);
382 AST_RULE(Parens); 386 AST_RULE(Parens);
383 AST_RULE(DotChainItem); 387 AST_RULE(DotChainItem);
@@ -453,6 +457,7 @@ private:
453namespace Utils { 457namespace Utils {
454void replace(std::string& str, std::string_view from, std::string_view to); 458void replace(std::string& str, std::string_view from, std::string_view to);
455void trim(std::string& str); 459void trim(std::string& str);
460std::string toLuaString(const std::string& input);
456} // namespace Utils 461} // namespace Utils
457 462
458} // namespace yue 463} // namespace yue