Skip to content

Commit c03bf36

Browse files
committed
Added YAML multiline string and macro argument checking.
1 parent 281149d commit c03bf36

5 files changed

Lines changed: 221 additions & 12 deletions

File tree

src/yuescript/yue_ast.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,30 @@ std::string DoubleString_t::to_string(void* ud) const {
962962
}
963963
return '"' + join(temp) + '"';
964964
}
965+
std::string YAMLLineInner_t::to_string(void* ud) const {
966+
auto info = reinterpret_cast<YueFormat*>(ud);
967+
return info->convert(this);
968+
}
969+
std::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+
}
975+
std::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+
}
982+
std::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+
}
965989
std::string String_t::to_string(void* ud) const {
966990
return str->to_string(ud);
967991
}

src/yuescript/yue_ast.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,8 +591,28 @@ AST_NODE(DoubleString)
591591
AST_MEMBER(DoubleString, &sep, &segments)
592592
AST_END(DoubleString)
593593

594+
AST_LEAF(YAMLLineInner)
595+
AST_END(YAMLLineInner)
596+
597+
AST_NODE(YAMLLineContent)
598+
ast_sel<true, YAMLLineInner_t, Exp_t> content;
599+
AST_MEMBER(YAMLLineContent, &content)
600+
AST_END(YAMLLineContent)
601+
602+
AST_NODE(YAMLLine)
603+
ast_ptr<true, Seperator_t> sep;
604+
ast_list<false, YAMLLineContent_t> segments;
605+
AST_MEMBER(YAMLLine, &sep, &segments)
606+
AST_END(YAMLLine)
607+
608+
AST_NODE(YAMLMultiline)
609+
ast_ptr<true, Seperator_t> sep;
610+
ast_list<true, YAMLLine_t> lines;
611+
AST_MEMBER(YAMLMultiline, &sep, &lines)
612+
AST_END(YAMLMultiline)
613+
594614
AST_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;
596616
AST_MEMBER(String, &str)
597617
AST_END(String)
598618

@@ -752,15 +772,17 @@ AST_END(Export)
752772
AST_NODE(FnArgDef)
753773
ast_sel<true, Variable_t, SelfItem_t> name;
754774
ast_ptr<false, ExistentialOp_t> op;
775+
ast_ptr<false, Name_t> label;
755776
ast_ptr<false, Exp_t> defaultValue;
756-
AST_MEMBER(FnArgDef, &name, &op, &defaultValue)
777+
AST_MEMBER(FnArgDef, &name, &op, &label, &defaultValue)
757778
AST_END(FnArgDef)
758779

759780
AST_NODE(FnArgDefList)
760781
ast_ptr<true, Seperator_t> sep;
761782
ast_list<false, FnArgDef_t> definitions;
762783
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)
764786
AST_END(FnArgDefList)
765787

766788
AST_NODE(OuterVarShadow)

src/yuescript/yue_compiler.cpp

Lines changed: 137 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
8282
const std::string_view extension = "yue"sv;
8383

8484
class 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
}

src/yuescript/yue_parser.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,15 @@ YueParser::YueParser() {
639639
DoubleStringInner = +(not_("#{") >> double_string_plain);
640640
DoubleStringContent = DoubleStringInner | interp;
641641
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;
643651

644652
lua_string_open = '[' >> *expr('=') >> '[';
645653
lua_string_close = ']' >> *expr('=') >> ']';
@@ -883,11 +891,11 @@ YueParser::YueParser() {
883891

884892
fn_arg_def_lit_lines = fn_arg_def_lit_line >> *(-(space >> ',') >> space_break >> fn_arg_def_lit_line);
885893

886-
FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '=' >> space >> Exp);
894+
FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '`' >> space >> Name) >> -(space >> '=' >> space >> Exp);
887895

888896
FnArgDefList = Seperator >> (
889-
fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg) |
890-
white >> VarArg
897+
fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg >> -(space >> '`' >> space >> Name)) |
898+
white >> VarArg >> -(space >> '`' >> space >> Name)
891899
);
892900

893901
OuterVarShadow = key("using") >> space >> (NameList | key("nil"));
@@ -1176,6 +1184,24 @@ void trim(std::string& str) {
11761184
str.erase(0, str.find_first_not_of(" \t\r\n"));
11771185
str.erase(str.find_last_not_of(" \t\r\n") + 1);
11781186
}
1187+
1188+
std::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+
}
11791205
} // namespace Utils
11801206

11811207
std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol, int lineOffset) const {

src/yuescript/yue_parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,10 @@ class YueParser {
378378
AST_RULE(DoubleStringInner);
379379
AST_RULE(DoubleStringContent);
380380
AST_RULE(DoubleString);
381+
AST_RULE(YAMLLineInner);
382+
AST_RULE(YAMLLineContent);
383+
AST_RULE(YAMLLine);
384+
AST_RULE(YAMLMultiline);
381385
AST_RULE(String);
382386
AST_RULE(Parens);
383387
AST_RULE(DotChainItem);
@@ -453,6 +457,7 @@ class YueParser {
453457
namespace Utils {
454458
void replace(std::string& str, std::string_view from, std::string_view to);
455459
void trim(std::string& str);
460+
std::string toLuaString(const std::string& input);
456461
} // namespace Utils
457462

458463
} // namespace yue

0 commit comments

Comments
 (0)