]> dreyeck.freedombox.rocks Git - idiomatic.git/blob - index.js
first three pages
[idiomatic.git] / index.js
1 let express = require('express')
2 let acorn = require('acorn')
3 let fs = require('fs')
4 // let fs = require('node:fs/promises');
5
6 let visitor = require('./visitor.js')
7 let dir = '../wiki-client/lib'
8 let mods = []
9
10 const app = express()
11
12
13 // P A G E S
14
15 app.get('/index', async (req,res,next) => {
16 console.log(new Date().toLocaleTimeString(), 'index')
17 const reductions = counter()
18 fs.readdir(dir, async (err, files) => {
19 mods = await Promise.all(files.map(load))
20 const doit = branch => {reductions.count(branch.type)}
21 visitor.wander(mods,doit)
22 const result = `
23 <p>${reductions.size()} non-terminals
24 <br>${reductions.total()} reductions
25 <p>${reductions.tally()
26 .map(([k,v]) => `${v} ${link(k)}`)
27 .join("<br>")}`
28 res.send(result);
29 next()
30 })
31 })
32
33 async function load(file) {
34 return new Promise(resolve => {
35 fs.readFile(`${dir}/${file}`, "utf8", (err,text) => {
36 const tree = acorn.parse(text, {ecmaVersion: "latest"})
37 resolve({file,text,tree})
38 })
39 })
40 }
41
42 function link(key) {
43 if(key.match(/^Ident/)) return `<a href="/terminal?type=${key}&field=name">${key}</a>`
44 if(key.match(/^(As|B|L|U).*Ex/)) return `<a href="/terminal?type=${key}&field=operator">${key}</a>`
45 if(key.match(/^Lit/)) return `<a href="/terminal?type=${key}&field=value">${key}</a>`
46 return key
47 }
48
49
50 app.get('/terminal', (req,res) => {
51 const lits = counter()
52 const id = req.query.type
53 const field = req.query.field
54 const doit = branch => {if(branch.type==id) lits.count(branch[field])}
55 visitor.wander(mods,doit)
56 const result = `
57 <p>${lits.size()} uniques
58 <br>${lits.total()} total
59 <p>${lits.tally()
60 .map(([k,v]) => `${v} <a href="/usage?type=${id}&field=${field}&key=${encodeURIComponent(k)}">${escape(k)}</a>`)
61 .join("<br>")}`
62 res.send(result)
63 })
64
65 app.get('/usage', (req,res) => {
66 const type = req.query.type
67 const field = req.query.field
68 const key = req.query.key
69 const list = []
70 const doit = (branch,stack) => {
71 if(branch.type==type && branch[field]==key)
72 list.push(`${stack.at(-1)} ${sxpr(stack[2],3)}`
73 // `${stack.length}
74 // ${stack.at(-1)}-${branch.start}-${branch.end}
75 // (${stack.slice(0,6).map(n => n.end-n.start).join(" ")})`
76 )
77 }
78 visitor.wander(mods,doit)
79 res.send(`<pre>${JSON.stringify(req.query,null,2)}</pre>${list.join("<br>")}`)
80 })
81
82
83 // H E L P E R S
84
85 function counter() {
86 const counts = new Map()
87 return {
88 count(item) {
89 if(counts.has(item))
90 counts.set(item, counts.get(item)+1)
91 else
92 counts.set(item,1)
93 },
94 size() {
95 return counts.size
96 },
97 total() {
98 return [...counts]
99 .reduce((sum,each) => sum + each[1], 0)
100 },
101 tally() {
102 return [...counts]
103 .sort((a,b) => a[1]==b[1] ? (a[0]>b[0] ? 1 : -1) : b[1]-a[1])
104 },
105 }
106 }
107
108 function escape(text) {
109 try {
110 return text
111 .replace(/&/g, '&amp;')
112 .replace(/</g, '&lt;')
113 .replace(/>/g, '&gt;')
114 .replace(/\*(.+?)\*/g, '<i>$1</i>')
115 } catch (e) {
116 return text
117 }
118 }
119
120 function sxpr(obj,deep,key,child) {
121 const hilite = obj===child ? 'class="hi"' : ''
122 const link = word => obj.type == 'Identifier' ? `<a href=/context?id=${word}>${word}</a>` : word
123 if (obj) {
124 if(deep) {
125 const fields = Object.entries(obj)
126 .filter(([k,v]) => !['start','end','raw','computed','optional','kind'].includes(k))
127 .map(([k,v]) =>
128 k=='type' ? abv(v) :
129 (typeof v == 'string') ? link(expand(v)) :
130 Array.isArray(v) ? `[${v.map(o => sxpr(o,deep-1,k,child)).join(" ")}]` :
131 sxpr(v, deep-1, k, child))
132 .join(" ")
133 return key ? `<span ${hilite} title=${key}>(${(fields)})</span>` : `(${(fields)})`
134 } else return elipsis(obj)
135 } else return `<span title=${obj}>.</span>`
136 }
137
138 function abv(type) {
139 return `<span title=${type}>${type.replaceAll(/[a-z]/g,'')}</span>`
140 }
141
142 function omit(k,v) {
143 return k=='type'?v:k=='start'||k=='end'?undefined:v
144 }
145
146 function elipsis(obj) {
147 const bytes = (obj.end||0)-(obj.start||0)
148 const dots = '..' + '.'.repeat(Math.floor(Math.log2(bytes||1)))
149 return `(<span title="${bytes} bytes">${dots}</span>)`
150 }
151
152 function expand(text) {
153 return text
154 .replace(/&/g, '&amp;')
155 .replace(/</g, '&lt;')
156 .replace(/>/g, '&gt;')
157 .replace(/\*(.+?)\*/g, '<i>$1</i>')
158 };
159
160 app.listen(1954)