UNPKG

ki

Version:

lisp + mori, sweet.js

964 lines (875 loc) 35.3 kB
let quoteSyntax = macro { function(stx) { var name_stx = stx[0]; if (!(stx[1] && stx[1].token && stx[1].token.inner)) { throwSyntaxError("macro", "Macro `quoteSyntax` could not be matched" , stx[1]); } var res = [ makeIdent("#quoteSyntax", null), stx[1].expose() ]; return { result: res, rest: stx.slice(2) }; } } export quoteSyntax let syntax = macro { function(stx) { var name_stx = stx[0]; var here = quoteSyntax{here}; var takeLineContext = patternModule.takeLineContext; var takeLine = patternModule.takeLine; var mod = makeIdent("patternModule", here); if (!(stx[1] && stx[1].token && stx[1].token.inner)) { throwSyntaxError("macro", "Macro `syntax` could not be matched", stx[1]); } var res = [mod, makePunc(".", here), makeIdent("transcribe", here), makeDelim("()", [ makeIdent("#quoteSyntax", here), stx[1].expose(), makePunc(",", here), // breaking hygiene to capture `name_stx`, `match`, and // `patternEnv` inside the syntaxCase macro makeIdent("name_stx", name_stx), makePunc(",", here), makeIdent("match", name_stx), makePunc(".", here), makeIdent("patternEnv", name_stx) ], here)]; return { result: res, rest: stx.slice(2) }; } } export syntax macro # { function (stx) { return { // breaking hygiene to capture inside syntaxCase result: [makeIdent("syntax", stx[0]), stx[1]], rest: stx.slice(2) } } } export # let syntaxCase = macro { function(stx, context) { var name_stx = stx[0]; var here = quoteSyntax{here}; if (!(stx[1] && stx[1].token && stx[1].token.inner) || !(stx[2] && stx[2].token && stx[2].token.inner)) { throwSyntaxError("macro", "Macro `syntaxCase` could not be matched" , stx[1]); } var arg_stx = stx[1].expose().token.inner; var cases_stx = stx[2].expose().token.inner; var Token = parser.Token; var assert = parser.assert; var loadPattern = patternModule.loadPattern; var takeLine = patternModule.takeLine; var matchPatterns = matchPatterns; function makeFunc(params, body) { return [ makeKeyword("function", here), makeDelim("()", params, here), makeDelim("{}", body, here) ]; } function makeVarDef(id, expr) { return [ makeKeyword("var", here), makeIdent(id, name_stx), makePunc("=", here) ].concat(expr, makePunc(";", here)); } function makeAssign(id, expr) { return [ makeIdent(id, name_stx), makePunc("=", here) ].concat(expr, makePunc(";", here)); } function cloneSyntax(stx) { var clone = _.extend({}, stx, { token: _.clone(stx.token) }); if (clone.token.inner) { clone.token.inner = clone.token.inner.map(cloneSyntax); } return clone; } if (cases_stx.length == 0) { throw new Error("Must have at least one case") } var cases = []; for (var i = 0; i < cases_stx.length; i += 4) { var caseKwd = cases_stx[i]; var isInfix = cases_stx[i + 1].token.value === "infix"; if (isInfix) { i += 1; } var casePattern = cases_stx[i + 1]; var caseArrow = cases_stx[i + 2]; var caseBody = cases_stx[i + 3]; if (!(caseKwd && caseKwd.token && caseKwd.token.value === "case")) { throw new Error("expecting case keyword in syntax case"); } if (!(casePattern && casePattern.token && casePattern.token.value === "{}")) { throw new Error("expecting a pattern surrounded by {} in syntax case"); } if (!(caseArrow && caseArrow.token && caseArrow.token.value === "=>")) { throw new Error("expecting an arrow separating pattern from body in syntax case"); } if (!(caseBody && caseBody.token && caseBody.token.value === "{}")) { throw new Error("expecting a body surrounded by {} in syntax case"); } // If infix, loop through the pattern separating the lhs and rhs. if (isInfix) { var pattern = cloneSyntax(casePattern).expose().token.inner; var lhs = []; var rhs = []; var separator = null; for (var j = 0; j < pattern.length; j++) { if (separator) { rhs.push(pattern[j]); } else { if (pattern[j].token.type === parser.Token.Punctuator && pattern[j].token.value === '|') { separator = pattern[j]; } else { lhs.push(pattern[j]); } } } if (!separator) { throwSyntaxError("syntaxCase", "Infix macros require a `|` separator", casePattern); } cases.push({ lookbehind: loadPattern(lhs, true), pattern: loadPattern(rhs), body: caseBody.expose().token.inner }); } else { cases.push({ lookbehind: [], pattern: loadPattern(cloneSyntax(casePattern).expose().token.inner), body: caseBody.expose().token.inner }); } } function patternsToObject(pats) { if (!pats.length) { return makeDelim("[]", [], here); } var freshId = __fresh(); context.patternMap.set(freshId, pats); return [ makeIdent("getPattern", here), makeDelim("()", [ makeValue(freshId, here) ], here) ]; } function makeMatch(caseObj) { var lhs = makeAssign("lhs", patternsToObject(caseObj.lookbehind)); var rhs = makeAssign("rhs", patternsToObject(caseObj.pattern)); var lhsMatch = makeAssign("lhsMatch", [ makeIdent("patternModule", here), makePunc(".", here), makeIdent("matchLookbehind", here), makeDelim("()", [ makeIdent("lhs", name_stx), makePunc(",", here), makeIdent("prevStx", name_stx), makePunc(",", here), makeIdent("prevTerms", name_stx), makePunc(",", here), makeIdent("context", name_stx) ], here) ]); var rhsMatch = makeAssign("rhsMatch", [ makeIdent("patternModule", here), makePunc(".", here), makeIdent("matchPatterns", here), makeDelim("()", [ makeIdent("rhs", name_stx), makePunc(",", here), makeIdent("arg", name_stx), makePunc(",", here), makeIdent("context", name_stx), makePunc(",", here), makeValue(true, here) ], here) ]); var mergeMatch = makeAssign("match", [ makeIdent("mergeMatches", here), makeDelim("()", [ makeIdent("rhsMatch", name_stx), makePunc(",", here), ].concat( makeIdent("mergeMatches", here), makeDelim("()", [ makeIdent("lhsMatch", name_stx), makePunc(",", here), makeIdent("parentMatch", name_stx) ], here) ), here) ]); return lhs.concat(lhsMatch, [ makeKeyword("if", here), makeDelim("()", [ makeIdent("lhsMatch", name_stx), makePunc(".", here), makeIdent("success", here) ], here), makeDelim("{}", rhs.concat(rhsMatch, [ makeKeyword("if", here), makeDelim("()", [ makeIdent("rhsMatch", name_stx), makePunc(".", here), makeIdent("success", here) ], here), makeDelim("{}", mergeMatch.concat(makeTranscribe(caseObj)), here) ]), here) ]); } function makeTranscribe(caseObj) { // applyMarkToPatternEnv (context.mark, match.patternEnv); var applyPreMark = [ makeIdent("applyMarkToPatternEnv", here), makeDelim("()", [ makeIdent("context", name_stx), makePunc(".", here), makeIdent("mark", name_stx), makePunc(",", here), makeIdent("match", name_stx), makePunc(".", here), makeIdent("patternEnv", name_stx) ], here), makePunc(";", here) ]; // var res = (function() { <caseObj.body> })(); var runBody = makeVarDef("res", [ makeDelim("()", makeFunc([], caseObj.body), here), makeDelim("()", [], here) ]); // if (!Array.isArray(res)) { throwSyntaxError("macro", "Macro must return a syntax array", stx); } var errHandling = [ makeKeyword("if", here), makeDelim("()", [ makePunc("!", here), makeIdent("Array", here), makePunc(".", here), makeIdent("isArray", here), makeDelim("()", [ makeIdent("res", name_stx) ], here) ], here), makeDelim("{}", [ makeIdent("throwSyntaxError", here), makeDelim("()", [ makeValue("macro", here), makePunc(",", here), makeValue("Macro must return a syntax array", here), makePunc(",", here), makeIdent("stx", name_stx) ], here) ], here) ]; // res = res.map(function(stx) { return stx.mark(context.mark); }) var applyPostMark = [ makeIdent("res", name_stx), makePunc("=", here), makeIdent("res", name_stx), makePunc(".", here), makeIdent("map", here), makeDelim("()", makeFunc([makeIdent("stx", here)], [ makeKeyword("return", here), makeIdent("stx", here), makePunc(".", here), makeIdent("mark", here), makeDelim("()", [ makeIdent("context", name_stx), makePunc(".", here), makeIdent("mark", here) ], here) ]), here), makePunc(";", here) ]; // return { result: res, rest: match.rest }; var retResult = [ makeKeyword("return", here), makeDelim("{}", [ makeIdent("result", here), makePunc(":", here), makeIdent("res", name_stx), makePunc(",", here), makeIdent("rest", here), makePunc(":", here), makeIdent("match", name_stx), makePunc(".", here), makeIdent("rest", here), makePunc(",", here), makeIdent("prevStx", here), makePunc(":", here), makeIdent("lhsMatch", name_stx), makePunc(".", here), makeIdent("prevStx", here), makePunc(",", here), makeIdent("prevTerms", here), makePunc(":", here), makeIdent("lhsMatch", name_stx), makePunc(".", here), makeIdent("prevTerms", here) ], here) ]; return applyPreMark.concat(runBody, errHandling, applyPostMark, retResult); } var arg_def = makeVarDef("arg", [makeIdent("stx", name_stx)]); var name_def = makeVarDef("name_stx", [ makeIdent("arg", name_stx), makeDelim("[]", [makeValue(0, here)], here) ]); var match_defs = [ makeKeyword('var', here), makeIdent('lhs', name_stx), makePunc(',', here), makeIdent('lhsMatch', name_stx), makePunc(',', here), makeIdent('rhs', name_stx), makePunc(',', here), makeIdent('rhsMatch', name_stx), makePunc(',', here), makeIdent('match', name_stx), makePunc(',', here), makeIdent('res', name_stx), makePunc(';', here), ]; var body = arg_def.concat(name_def, match_defs); for (var i = 0; i < cases.length; i++) { body = body.concat(makeMatch(cases[i])); } body = body.concat(quoteSyntax { throwSyntaxCaseError("Could not match any cases"); }); var res = makeFunc([ makeIdent("stx", name_stx), makePunc(",", here), makeIdent("context", name_stx), makePunc(",", here), makeIdent("prevStx", name_stx), makePunc(",", here), makeIdent("prevTerms", name_stx), makePunc(",", here), makeIdent("parentMatch", name_stx) ], body).concat([ makeDelim("()", arg_stx.concat([ makePunc(",", here), makeKeyword("typeof", here), makeIdent("match", name_stx), makePunc("!==", here), makeValue("undefined", here), makePunc("?", here), makeIdent("match", name_stx), makePunc(":", here), makeDelim("{}", [], here) ]), here) ]); return { result: res, rest: stx.slice(3) } } } export syntaxCase let macro = macro { function(stx) { var name_stx = stx[0]; var here = quoteSyntax{here}; var mac_name_stx; var body_inner_stx; var body_stx; var takeLine = patternModule.takeLine; var makeIdentityRule = patternModule.makeIdentityRule; var rest; if (stx[1] && stx[1].token.type === parser.Token.Delimiter && stx[1].token.value === "{}") { mac_name_stx = null; body_stx = stx[1]; body_inner_stx = stx[1].expose().token.inner; rest = stx.slice(2); } else { mac_name_stx = []; mac_name_stx.push(stx[1]); body_stx = stx[2]; body_inner_stx = stx[2].expose().token.inner; rest = stx.slice(3); } function makeFunc(params, body) { return [ makeKeyword("function", here), makeDelim("()", params, here), makeDelim("{}", body, here) ]; } function translateRule(pattern, def, isInfix) { var translatedPatt; // When infix, we need to loop through the body and make sure there // is a separator to distinguish the lhs and rhs. if (isInfix) { translatedPatt = []; for (var i = 0, len = pattern.length; i < len; i++) { translatedPatt.push(pattern[i]); if (pattern[i].token.type === parser.Token.Punctuator && pattern[i].token.value === '|') { translatedPatt.push(makeIdent("_", here)); translatedPatt = translatedPatt.concat([makeIdent("$", here), makeDelim("()", pattern.slice(i + 1), here)]); break; } } } else { translatedPatt = [makeIdent("_", here), // wrapping the patterns in a group to disambiguate // `_ (foo) ...` // since the `(foo)` would be interpreted as a separator makeIdent("$", here), makeDelim("()", pattern, here)]; } var translatedDef = [ makeKeyword("return", here), takeLine(here[0], makeIdent("syntax", name_stx)), makeDelim("{}", def, here) ]; return [makeIdent("case", here)].concat( isInfix ? makeIdent("infix", here) : [], makeDelim("{}", translatedPatt, here), makePunc("=>", here), makeDelim("{}", translatedDef, here) ); } if (body_inner_stx[0] && body_inner_stx[0].token.value === "function") { if (mac_name_stx) { var res = [makeIdent("macro", here)].concat(mac_name_stx).concat(body_stx) return { result: res, rest: rest }; } else { var res = [ makeIdent("macro", here), body_stx ]; return { result: res, rest: rest }; } } var rules = []; if (body_inner_stx[0] && body_inner_stx[0].token.value === "rule") { for (var i = 0; i < body_inner_stx.length; i += 4) { var isInfix = body_inner_stx[i + 1].token.value === 'infix'; if (isInfix) { i += 1; } var rule_pattern = body_inner_stx[i + 1]; var rule_arrow = body_inner_stx[i + 2]; var rule_def = body_inner_stx[i + 3]; if (rule_pattern && rule_arrow && rule_arrow.token.value === "=>" && rule_def) { rules = rules.concat(translateRule(rule_pattern.expose().token.inner, rule_def.expose().token.inner, isInfix)); } else if (rule_pattern) { var idRule = makeIdentityRule(rule_pattern.token.inner, isInfix, rule_pattern); rules = rules.concat(translateRule(idRule.pattern, idRule.body, isInfix)); i -= 2; } else { throwSyntaxError("macro", "Macro `macro` could not be matched" , rule_arrow); } } rules = makeDelim("{}", rules, here); } else { rules = body_stx; } var stxSyntaxCase = takeLine(here[0], makeIdent("syntaxCase", name_stx)); var res = mac_name_stx ? [makeIdent("macro", here)].concat(mac_name_stx) : [makeIdent("macro", here)]; res = res.concat(makeDelim("{}", makeFunc([makeIdent("stx", name_stx), makePunc(",", here), makeIdent("context", name_stx), makePunc(",", here), makeIdent("prevStx", name_stx), makePunc(",", here), makeIdent("prevTerms", name_stx)], [makeKeyword("return", here), stxSyntaxCase, makeDelim("()", [makeIdent("stx", name_stx), makePunc(",", here), makeIdent("context", name_stx), makePunc(",", here), makeIdent("prevStx", name_stx), makePunc(",", here), makeIdent("prevTerms", name_stx)], here), rules]), here)); return { result: res, rest: rest } } } export macro; macro withSyntax_done { case { _ $ctx ($vars ...) {$rest ...} } => { var ctx = #{ $ctx }; var here = #{ here }; var vars = #{ $vars ... }; var rest = #{ $rest ... }; var res = []; for (var i = 0; i < vars.length; i += 3) { var name = vars[i]; var repeat = !!vars[i + 1].token.inner.length; var rhs = vars[i + 2]; if (repeat) { res.push( makeIdent('match', ctx), makePunc('.', here), makeIdent('patternEnv', here), makeDelim('[]', [makeValue(name.token.value, here)], here), makePunc('=', here), makeDelim('{}', [ makeIdent('level', here), makePunc(':', here), makeValue(1, here), makePunc(',', here), makeIdent('match', here), makePunc(':', here), makeDelim('()', #{ (function(exp) { return exp.length ? exp.map(function(t) { return { level: 0, match: [t] } }) : [{ level: 0, match: [] }]; }) }, here), makeDelim('()', [rhs], here) ], here), makePunc(';', here) ); } else { res.push( makeIdent('match', ctx), makePunc('.', here), makeIdent('patternEnv', here), makeDelim('[]', [makeValue(name.token.value, here)], here), makePunc('=', here), makeDelim('{}', [ makeIdent('level', here), makePunc(':', here), makeValue(0, here), makePunc(',', here), makeIdent('match', here), makePunc(':', here), rhs ], here), makePunc(';', here) ); } } res = res.concat(rest); res = [ makeDelim("()", [ makeKeyword("function", here), makeDelim("()", [makeIdent("match", ctx)], here), makeDelim("{}", res, here) ], here), makeDelim("()", [ makeIdent("patternModule", here), makePunc(".", here), makeIdent("cloneMatch", here), makeDelim("()", [makeIdent("match", ctx)], here) ], here) ]; return res; } } macro withSyntax_bind { rule { $name:ident $[...] = $rhs:expr } => { $name (true) $rhs } rule { $name:ident = $rhs:expr } => { $name () $rhs } } let withSyntax = macro { case { $name ($binders:withSyntax_bind (,) ...) { $body ... } } => { return #{ withSyntax_done $name ($binders ...) { $body ... } } } case { $name ($binders:withSyntax_bind (,) ...) $quote:[#] { $body ... } } => { return #{ withSyntax_done $name ($binders ...) { return $quote { $body ... } } } } } export withSyntax; macro letstx_bind { rule { $name:ident = $rhs:expr , $more:letstx_bind } => { $name () $rhs $more } rule { $name:ident = $rhs:expr ;... letstx $more:letstx_bind } => { $name () $rhs $more } rule { $name:ident = $rhs:expr ;... } => { $name () $rhs } rule { $name:ident $[...] = $rhs:expr , $more:letstx_bind } => { $name (true) $rhs $more } rule { $name:ident $[...] = $rhs:expr ;... letstx $more:letstx_bind } => { $name (true) $rhs $more } rule { $name:ident $[...] = $rhs:expr ;... } => { $name (true) $rhs } } let letstx = macro { case { $name $binders:letstx_bind $rest ... } => { return #{ return withSyntax_done $name ($binders) { $rest ... } } } } export letstx; macro macroclass { rule { $name:ident { $decls:macroclass_decl ... } } => { macro $name { function (stx, context, prevStx, prevTerms) { var name_stx = stx[0]; var match; macroclass_create $name stx context match ($decls ...) } } } } macro macroclass_decl { rule { $kw:[name] = $name:lit ;... } => { ($kw $name) } rule { $kw:[pattern] { $mods:macroclass_modifier ... } ;... } => { ($kw $mods ...) } rule { rule { $rule ... } ;... } => { (pattern (rule ($rule ...))) } } macro macroclass_modifier { rule { $kw:[name] = $name:lit ;... } => { ($kw $name) } rule { $kw:[rule] { $rule ... } ;... } => { ($kw ($rule ...)) } rule { $kw:[with] $($lhs:macroclass_with_lhs = $rhs:macroclass_with_rhs) (,) ... } => { $(($kw ($lhs) ($rhs))) ... } rule { ; ;... } => { } } macro macroclass_with_lhs { rule { $name:ident $[...] } rule { $name:ident } } macro macroclass_with_rhs { rule { #{ $stx ... } } rule { $code:expr } } macro macroclass_create { function(stx, context, prevStx, prevTerms) { var here = quoteSyntax { here }; var macName = stx[0]; var nameStx = stx[1]; var stxName = stx[2]; var ctxName = stx[3]; var matchName = stx[4]; var decls = stx[5].expose().token.inner; var mclass = decls.reduce(function(m, decl) { var tag = unwrapSyntax(decl.token.inner[0]); if (tag === 'name') { if (m.name) { throwSyntaxError('macroclass', 'Duplicate name declaration', decl.token.inner[0]) } m.name = unwrapSyntax(decl.token.inner[1]); } else if (tag === 'pattern') { var patternStx = decl.expose().token.inner.slice(1); var pattern = patternStx.reduce(function(p, mod) { var tag = unwrapSyntax(mod.token.inner[0]); if (tag === 'name') { if (p.name) { throwSyntaxError('macroclass', 'Duplicate name declaration', mod.token.inner[0]) } p.name = unwrapSyntax(mod.token.inner[1]); } else if (tag === 'rule') { if (p.rule) { throwSyntaxError('macroclass', 'Duplicate rule declaration', mod.token.inner[0]) } p.rule = mod.expose().token.inner[1].expose().token.inner; } else if (tag === 'with') { mod.expose(); p.withs.push({ lhs: mod.token.inner[1].expose().token.inner, rhs: mod.token.inner[2].expose().token.inner.map(function mapper(s) { // We need to transplant syntax quotes so that it looks // like they are within the macro body code and not // the original code, otherwise it won't expand. if (unwrapSyntax(s) === '#') { s.context = macName.context; } else if (s.token.type === parser.Token.Delimiter) { s.expose(); s.token.inner = s.token.inner.map(mapper); } return s; }) }); } return p; }, { withs: [] }); m.patterns.push(pattern); } return m; }, { patterns: [] }); var body = mclass.patterns.reduce(function(stx, pattern) { var ruleStx = [makeIdent('_', here)].concat(pattern.rule); var ruleId = __fresh(); var rule = patternModule.loadPattern(ruleStx); context.patternMap.set(ruleId, rule); var withBindings = pattern.withs.reduce(function(acc, w) { return acc.concat(w.lhs.concat(makePunc('=', here), w.rhs, makePunc(',', here))); }, []); var ret = [ makeKeyword('return', here), makeDelim('{}', [ makeIdent('result', here), makePunc(':', here), makeDelim('[]', [], here), makePunc(',', here), makeIdent('rest', here), makePunc(':', here), matchName, makePunc('.', here), makeIdent('rest', here), makePunc(',', here), makeIdent('patterns', here), makePunc(':', here), matchName, makePunc('.', here), makeIdent('patternEnv', here), ], here) ]; var inner = ret; if (withBindings.length) { inner = [ makeKeyword('return', macName), makeIdent('withSyntax', macName), makeDelim('()', withBindings, here), makeDelim('{}', ret, here) ]; } var res = [ matchName, makePunc('=', here), makeIdent('patternModule', here), makePunc('.', here), makeIdent('matchPatterns', here), makeDelim('()', [ makeIdent('getPattern', here), makeDelim('()', [ makeValue(ruleId, here) ], here), makePunc(',', here), stxName, makePunc(',', here), ctxName, makePunc(',', here), makeValue(true, here) ], here), makePunc(';', here), makeKeyword('if', here), makeDelim('()', [ matchName, makePunc('.', here), makeIdent('success', here) ], here), makeDelim('{}', inner, here) ]; return stx.concat(res); }, []); var res = body.concat( makeIdent('throwSyntaxCaseError', here), makeDelim('()', [ makeValue(mclass.name || unwrapSyntax(nameStx), here), makePunc(',', here), makeValue('No match', here) ], here) ); return { result: res, rest: stx.slice(6) }; } } export macroclass; macro safemacro { rule { $name:ident { rule $body ... } } => { let $name = macro { rule { : } => { $name : } rule infix { . | } => { . $name } rule $body ... } } rule { $name:ident { case $body ... } } => { let $name = macro { case { _ : } => { return #{ $name : } } case infix { . | _ } => { return #{ . $name } } case $body ... } } } macro op_assoc { rule { left } rule { right } } macro op_name { rule { ($name ...) } rule { $name } => { ($name) } } safemacro operator { rule { $name:op_name $prec:lit $assoc:op_assoc { $left:ident, $right:ident } => #{ $body ... } } => { binaryop $name $prec $assoc { macro { rule { ($left:expr) ($right:expr) } => { $body ... } } } } rule { $name:op_name $prec:lit { $op:ident } => #{ $body ... } } => { unaryop $name $prec { macro { rule { $op:expr } => { $body ... } } } } } export operator; // macro __log { // case { _ defctx $stx } => { // var context = #{ $stx }[0].context; // console.log("defctx context for " + unwrapSyntax(#{$stx}) + "]"); // while (context) { // if (context.defctx) { // console.log(context.defctx.map(function(d) { // return d.id.token.value // })); // } // context = context.context; // } // return []; // } // case {_ rename $stx } => { // var context = #{ $stx }[0].context; // console.log("rename context for " + unwrapSyntax(#{$stx}) + ":"); // while (context) { // if (context.name) { // console.log("[name: " + context.name + ", id: " + context.id.token.value + "]"); // } // context = context.context; // } // return []; // } // case {_ all $stx } => { // var context = #{ $stx }[0].context; // console.log("context for " + unwrapSyntax(#{$stx}) + ":"); // while (context) { // if (context.name) { // console.log("rename@[name: " + context.name + ", id: " + context.id.token.value + "]"); // } // if (context.mark) { // console.log("mark@[mark: " + context.mark + "]"); // } // if (context.defctx) { // console.log("defctx@[" + context.defctx.map(function(d) { // return d.id.token.value // }) + "]"); // } // context = context.context; // } // return []; // } // } // export __log;