// @flow | |
import defineFunction from "../defineFunction"; | |
import ParseError from "../ParseError"; | |
import {assertNodeType} from "../parseNode"; | |
// \@char is an internal function that takes a grouped decimal argument like | |
// {123} and converts into symbol with code 123. It is used by the *macro* | |
// \char defined in macros.js. | |
defineFunction({ | |
type: "textord", | |
names: ["\\@char"], | |
props: { | |
numArgs: 1, | |
allowedInText: true, | |
}, | |
handler({parser}, args) { | |
const arg = assertNodeType(args[0], "ordgroup"); | |
const group = arg.body; | |
let number = ""; | |
for (let i = 0; i < group.length; i++) { | |
const node = assertNodeType(group[i], "textord"); | |
number += node.text; | |
} | |
let code = parseInt(number); | |
let text; | |
if (isNaN(code)) { | |
throw new ParseError(`\\@char has non-numeric argument ${number}`); | |
// If we drop IE support, the following code could be replaced with | |
// text = String.fromCodePoint(code) | |
} else if (code < 0 || code >= 0x10ffff) { | |
throw new ParseError(`\\@char with invalid code point ${number}`); | |
} else if (code <= 0xffff) { | |
text = String.fromCharCode(code); | |
} else { // Astral code point; split into surrogate halves | |
code -= 0x10000; | |
text = String.fromCharCode((code >> 10) + 0xd800, | |
(code & 0x3ff) + 0xdc00); | |
} | |
return { | |
type: "textord", | |
mode: parser.mode, | |
text: text, | |
}; | |
}, | |
}); | |