S-expressions: Difference between revisions

→‎JavaScript :: Functional: Adjusted type of parser, preferred indented JSON to tree diagram, added reserialization.
(→‎JavaScript :: Functional: Adjusted type of parser, preferred indented JSON to tree diagram, added reserialization.)
Line 3,760:
 
===Functional===
Showing the parse tree in and indented JSON format, and writing out a reserialization:
Generating a diagram of the parse tree:
<lang JavaScript>(() => {
"use strict";
 
// ------------------ S-EXPRESSION PARSINGEXPRESSIONS ------------------
const main = () => {
const expr = [
"((data \"quoted data\" 123 4.5)",
" (data (!@# (4.5) \"(more\" \"data)\")))"
].join("\n");
 
const [parse, residue] = parseExpr(
tokenized(expr)
);
 
return 0 < residue.length ? (
`Unparsed tokens: ${JSON.stringify(residue)}`
) : [
JSON.stringify(parse, null, 2),
"Reserialized from parse:",
parse.map(serialized).join(" ")
].join("\n\n");
};
 
// ---------------- EXPRESSION PARSER ----------------
// Expr = (Float | Int | String | Symbol)
 
// parseExpr [String] -> ([Tree Expr], [String])
const parseExpr = tokens => {
// A tuple of (parsed trees, residual tokens)
// derived from a list of tokens.
const pfinished = ([, tkns]) =>
// Tokens depleted or closing bracket reached ?
0 === tkns.length || ")" === tkns[0];
 
const freadToken = ([treesexprs, tkns]) =>
0 < tkns.length ? (() => {
const [t, ...ts] = tkns;
Line 3,783 ⟶ 3,800:
return "(" === t ? (
bimap(
xs => treesexprs.concat([xs])
Node(symbolName("List"))(xs)
)
)(
xs => xs.slice(1)tail
)(
// recursively,parseExpr(ts)
go(ts)
)
 
// and conclude where brackets close.
) : ")" === t ? (
Tuple(treesexprs)(ts)
 
// Other tokens are appended leaves.
) : Tuple(
treesexprs.concat(atom(t))
Node(atom(t))([])
)
)(ts);
})() : Tuple(treesexprs)(tkns);
 
const go = ts =>return until(pfinished)(freadToken)(
Tuple([])(tstokens)
);
 
return go(tokens);
};
 
// ------------------- ATOM PARSER -------------------
 
// atom :: String -> Expr
Line 3,819 ⟶ 3,830:
"\"'".includes(s[0]) ? (
s.slice(1, -1)
) : symbolName(s){
name: s
}
) : parseFloat(s, 10)
) : "";
 
 
// symbolName :: String -> Symbol String
const symbolName = s => ({
"type": "symbol",
"name": s
});
 
 
Line 3,856 ⟶ 3,862:
);
 
// ------------------ SERIALIZATION ------------------
 
// serialized :: Expr -> String
// ---------------------- TEST -----------------------
const mainserialized = ()e => {
const exprt = [typeof e;
"((data \"quoted data\" 123 4.5)",
" (data (!@# (4.5) \"(more\" \"data)\")))"
].join("\n");
 
constreturn [forest, unparsedTokens]"number" === t ? parseExpr(
tokenized(expr)`${e}`
); : "string" === t ? (
`"${e}"`
 
return) 0: <"object" unparsedTokens.length=== t ? (
`Unparsed residue: ${JSONArray.stringifyisArray(unparsedTokense)}` ? (
`(${e.map(serialized).join(" ")})`
) : drawForest(
forest) : e.map(fmapTree(showAtom))name
) : "?";
};
 
const showAtom = x =>
x.type !== "symbol" ? (
JSON.stringify(x)
) : `<${x.name}>`;
 
// --------------------- GENERIC ---------------------
 
// Node :: a -> [Tree a] -> Tree a
const Node = v =>
// Constructor for a Tree node which connects a
// value of some kind to a list of zero or
// more child trees.
xs => ({
type: "Node",
root: v,
nest: xs || []
});
 
 
// Tuple (,) :: a -> b -> (a, b)
Line 3,917 ⟶ 3,905:
// first and second values respectively.
g => ([a, b]) => Tuple(f(a))(g(b));
 
 
// draw :: Tree String -> [String]
const draw = node => {
// shift :: String -> String -> [String] -> [String]
const shifted = (first, other, xs) => (
[first].concat(
Array.from({
length: xs.length - 1
}, () => other)
).map(
(y, i) => y.concat(xs[i])
)
);
// drawSubTrees :: [Tree String] -> [String]
const drawSubTrees = xs => {
const lng = xs.length;
 
return 0 < lng ? (
1 < lng ? (
["│"].concat(
shifted("├─ ", "│ ", draw(xs[0]))
)
).concat(
drawSubTrees(xs.slice(1))
) : ["│"].concat(
shifted("└─ ", " ", draw(xs[0]))
)
) : [];
};
 
return node.root.split("\n").concat(
drawSubTrees(node.nest)
);
};
 
 
// drawForest :: [Tree String] -> String
const drawForest = trees =>
trees.map(drawTree).join("\n");
 
 
// drawTree :: Tree String -> String
const drawTree = tree =>
draw(tree).join("\n");
 
 
Line 3,968 ⟶ 3,911:
// True if 2 is a factor of n.
0 === n % 2;
 
 
// fmapTree :: (a -> b) -> Tree a -> Tree b
const fmapTree = f => {
// A new tree. The result of a
// structure-preserving application of f
// to each root in the existing tree.
const go = t => Node(
f(t.root)
)(
t.nest.map(go)
);
 
return go;
};
 
 
Line 3,998 ⟶ 3,926:
return go;
};
 
 
// tail :: [a] -> [a]
const tail = xs =>
// A new list consisting of all
// items of xs except the first.
0 < xs.length ? (
xs.slice(1)
) : undefined;
 
return main();
})();</lang>
{{Out}}
<pre><List>[
[
[
├─ <List>
{
│ │
├─ < "name": "data>"
},
│ │
├─ "quoted data",
123,
│ │
4.5
│ ├─ 123
],
[
│ └─ 4.5
{
"name": "data"
└─ <List>
},
├─ <data> [
{
"name": "!@#"
└─ <List>
},
├─ <!@#> [
4.5
├─ <List> ],
"(more",
└─ 4.5"data)"
]
]
├─ "(more"
]
]
└─ "data)"</pre>
 
Reserialized from parse:
 
((data "quoted data" 123 4.5) (data (!@# (4.5) "(more" "data)")))</pre>
 
=={{header|Julia}}==
9,655

edits