@petristation/renewjs
Version:
Parser for .rnw files
118 lines (103 loc) • 2.96 kB
JavaScript
import symbols from './symbols.js'
export function makeSerializer(grammar, metaKeys) {
const kindKey = metaKeys?.kindKey ?? symbols.kindKey
const refKey = metaKeys?.refKey ?? symbols.refKey
const selfKey = metaKeys?.selfKey ?? symbols.selfKey
const refKeyMarker = metaKeys?.refKeyMarker ?? symbols.refKeyMarker
return function serializer(sourceRefMap) {
const output = []
const outputRefMap = []
let indent = 0;
const decimalFormat = new Intl.NumberFormat('en-US', { minimumFractionDigits: 1 })
function tryDeref(refOrObject, sourceRefMap, path = []) {
const selfId = refOrObject ? refOrObject[selfKey] : undefined;
const object = (refOrObject && refOrObject[refKeyMarker]) ? sourceRefMap[refOrObject[refKey]] : selfId!==undefined ? sourceRefMap[selfId] : refOrObject
return path.reduce((o, k) => o ? tryDeref(o[k], sourceRefMap) : null, object)
}
function writeSelfAndSuper(out, object, rule) {
const syn = grammar.rules[rule];
if(!syn) {
throw new Error(`Unknown grammar rule '${rule}'. Can not serialize.`)
}
if(syn.super && !syn.skipSuper) {
writeSelfAndSuper(out, object, syn.super)
}
if(syn.writer) {
syn.writer(object, context)
}
}
const context = {
get version() {
return grammar.version
},
get sourceRefMap() {
return sourceRefMap;
},
get kindKey() {
return kindKey;
},
get refKeyMarker() {
return refKeyMarker;
},
writeStorable(object) {
context.writeIndents()
const actualObject = tryDeref(object, sourceRefMap)
if(actualObject === null) {
context.writeNull()
} else {
const prevRef = outputRefMap.indexOf(actualObject)
if(prevRef > -1) {
output.push(`REF ${prevRef}`)
} else {
outputRefMap.push(actualObject)
output.push(actualObject[kindKey])
indent++
writeSelfAndSuper(output, actualObject, actualObject[kindKey])
indent--
}
}
},
writeImplicitStorable(kind, object) {
const actualObject = tryDeref(object, sourceRefMap)
if(actualObject === null) {
context.writeNull()
} else {
writeSelfAndSuper(output, actualObject, kind)
}
},
writeIndents() {
for(let i=0;i<indent;i++) {
output.push("\t");
}
},
writeLine() {
output.push("\n")
},
writeInt(int) {
output.push(int.toString(10))
},
writeString(str) {
output.push(`"${str.replace(/(\\|")/g,"\\$1").replace(/\n/g,'\\n')}"`)
},
writeDouble(num) {
output.push(decimalFormat.format(num))
},
writeFloat(num) {
output.push(decimalFormat.format(num))
},
writeBoolean(b) {
output.push(b?'true':'false')
},
writeNull(b) {
output.push('NULL')
},
writeVersion() {
output.push(`${grammar.version}`)
}
}
return {
context,
output,
}
}
}