S-expressions: Difference between revisions
Content added Content deleted
m (→{{header|Wren}}: Minor tidy) |
m (→JS Functional: Tidied.) |
||
Line 3,919: | Line 3,919: | ||
"((data \"quoted data\" 123 4.5)", |
"((data \"quoted data\" 123 4.5)", |
||
" (data (!@# (4.5) \"(more\" \"data)\")))" |
" (data (!@# (4.5) \"(more\" \"data)\")))" |
||
] |
] |
||
⚫ | |||
const [parse, residue] = parseExpr( |
const [parse, residue] = parseExpr( |
||
Line 3,925: | Line 3,926: | ||
); |
); |
||
return 0 < residue.length |
return 0 < residue.length |
||
`Unparsed tokens: ${JSON.stringify(residue)}` |
? `Unparsed tokens: ${JSON.stringify(residue)}` |
||
: 0 < parse.length |
|||
? ([ |
|||
JSON.stringify(parse, null, 2), |
|||
"Reserialized from parse:", |
|||
parse.map(serialized).join(" ") |
|||
⚫ | |||
] |
|||
.join("\n\n")) |
|||
: "Could not be parsed"; |
|||
}; |
}; |
||
Line 3,960: | Line 3,964: | ||
// An open bracket introduces recursion over |
// An open bracket introduces recursion over |
||
// a sub-expression to define a sub-list. |
// a sub-expression to define a sub-list. |
||
return "(" === token |
return "(" === token |
||
? (() => { |
|||
const [expr, rest] = parseExpr(ts); |
|||
return [xs.concat([expr]), rest.slice(1)]; |
return [xs.concat([expr]), rest.slice(1)]; |
||
})() |
|||
: ")" === token |
|||
? [xs, token] |
|||
: [xs.concat(atom(token)), ts]; |
|||
}; |
}; |
||
Line 3,973: | Line 3,979: | ||
// atom :: String -> Expr |
// atom :: String -> Expr |
||
const atom = s => |
const atom = s => |
||
0 < s.length |
0 < s.length |
||
isNaN(s) |
? isNaN(s) |
||
? ( |
|||
"\"'".includes(s[0]) |
|||
? s.slice(1, -1) |
|||
name: s |
: {name: s} |
||
) |
|||
: parseFloat(s, 10) |
|||
: ""; |
|||
Line 3,990: | Line 3,996: | ||
// Brackets and quoted or unquoted atomic strings. |
// Brackets and quoted or unquoted atomic strings. |
||
quoteTokens("\"")(s).flatMap( |
quoteTokens("\"")(s).flatMap( |
||
segment => "\"" !== segment[0] |
segment => "\"" !== segment[0] |
||
segment.replace(/([()])/gu, " $1 ") |
? segment.replace(/([()])/gu, " $1 ") |
||
.split(/\s+/u) |
.split(/\s+/u) |
||
.filter(Boolean) |
.filter(Boolean) |
||
: [segment] |
|||
); |
); |
||
Line 4,002: | Line 4,008: | ||
// Alternating unquoted and quoted segments. |
// Alternating unquoted and quoted segments. |
||
s => s.split(q).flatMap( |
s => s.split(q).flatMap( |
||
(k, i) => even(i) |
(k, i) => even(i) |
||
? 0 < k.length |
|||
[k] |
? [k] |
||
: [] |
|||
: [`${q}${k}${q}`] |
|||
); |
); |
||
Line 4,015: | Line 4,021: | ||
const t = typeof e; |
const t = typeof e; |
||
return "number" === t |
return "number" === t |
||
`${e}` |
? `${e}` |
||
: "string" === t |
|||
`"${e}"` |
? `"${e}"` |
||
: "object" === t |
|||
Array.isArray(e) |
? Array.isArray(e) |
||
`(${e.map(serialized).join(" ")})` |
? `(${e.map(serialized).join(" ")})` |
||
: e.name |
|||
: "?"; |
|||
}; |
}; |
||
Line 4,042: | Line 4,048: | ||
f => { |
f => { |
||
const go = x => |
const go = x => |
||
p(x) |
p(x) |
||
? x |
|||
: go(f(x)); |
|||
return go; |
return go; |