"use strict"; // https://www.w3.org/TR/REC-xml/#NT-Name // http://www.bottlecaps.de/rr/ui Object.defineProperty(exports, "__esModule", { value: true }); // Grammar ::= Production* // Production ::= NCName '::=' Choice // NCName ::= [http://www.w3.org/TR/xml-names/#NT-NCName] // Choice ::= SequenceOrDifference ( '|' SequenceOrDifference )* // SequenceOrDifference ::= (Item ( '-' Item | Item* ))? // Item ::= Primary ( '?' | '*' | '+' )? // Primary ::= NCName | StringLiteral | CharCode | CharClass | '(' Choice ')' // StringLiteral ::= '"' [^"]* '"' | "'" [^']* "'" // CharCode ::= '#x' [0-9a-fA-F]+ // CharClass ::= '[' '^'? ( RULE_Char | CharCode | CharRange | CharCodeRange )+ ']' // RULE_Char ::= [http://www.w3.org/TR/xml#NT-RULE_Char] // CharRange ::= RULE_Char '-' ( RULE_Char - ']' ) // CharCodeRange ::= CharCode '-' CharCode // RULE_WHITESPACE ::= RULE_S | Comment // RULE_S ::= #x9 | #xA | #xD | #x20 // Comment ::= '/*' ( [^*] | '*'+ [^*/] )* '*'* '*/' const TokenError_1 = require("../TokenError"); const Parser_1 = require("../Parser"); var BNF; (function (BNF) { BNF.RULES = [ { name: 'Grammar', bnf: [['RULE_S*', 'Attributes?', 'RULE_S*', '%Atomic*', 'EOF']] }, { name: '%Atomic', bnf: [['Production', 'RULE_S*']], fragment: true }, { name: 'Production', bnf: [ [ 'NCName', 'RULE_S*', '"::="', 'RULE_WHITESPACE*', '%Choice', 'RULE_WHITESPACE*', 'Attributes?', 'RULE_EOL+', 'RULE_S*' ] ] }, { name: 'NCName', bnf: [[/[a-zA-Z][a-zA-Z_0-9]*/]] }, { name: 'Attributes', bnf: [['"{"', 'Attribute', '%Attributes*', 'RULE_S*', '"}"']] }, { name: '%Attributes', bnf: [['RULE_S*', '","', 'Attribute']], fragment: true }, { name: 'Attribute', bnf: [['RULE_S*', 'NCName', 'RULE_WHITESPACE*', '"="', 'RULE_WHITESPACE*', 'AttributeValue']] }, { name: 'AttributeValue', bnf: [['NCName'], [/[1-9][0-9]*/]] }, { name: '%Choice', bnf: [['SequenceOrDifference', '%_Choice_1*']], fragment: true }, { name: '%_Choice_1', bnf: [['RULE_S*', '"|"', 'RULE_S*', 'SequenceOrDifference']], fragment: true }, { name: 'SequenceOrDifference', bnf: [['%Item', 'RULE_WHITESPACE*', '%_Item_1?']] }, { name: '%_Item_1', bnf: [['Minus', '%Item'], ['%Item*']], fragment: true }, { name: 'Minus', bnf: [['"-"']] }, { name: '%Item', bnf: [['RULE_WHITESPACE*', 'PrimaryPreDecoration?', '%Primary', 'PrimaryDecoration?']], fragment: true }, { name: 'PrimaryDecoration', bnf: [['"?"'], ['"*"'], ['"+"']] }, { name: 'PrimaryPreDecoration', bnf: [['"&"'], ['"!"'], ['"~"']] }, { name: '%Primary', bnf: [['NCName'], ['StringLiteral'], ['CharCode'], ['CharClass'], ['SubItem']], fragment: true }, { name: 'SubItem', bnf: [['"("', 'RULE_S*', '%Choice', 'RULE_S*', '")"']] }, { name: 'StringLiteral', bnf: [[`'"'`, /[^"]*/, `'"'`], [`"'"`, /[^']*/, `"'"`]] }, { name: 'CharCode', bnf: [['"#x"', /[0-9a-zA-Z]+/]] }, { name: 'CharClass', bnf: [["'['", "'^'?", '%RULE_CharClass_1+', '"]"']] }, { name: '%RULE_CharClass_1', bnf: [['CharCodeRange'], ['CharRange'], ['CharCode'], ['RULE_Char']], fragment: true }, { name: 'RULE_Char', bnf: [[/\x09/], [/\x0A/], [/\x0D/], [/[\x20-\x5c]/], [/[\x5e-\uD7FF]/], [/[\uE000-\uFFFD]/]] }, { name: 'CharRange', bnf: [['RULE_Char', '"-"', 'RULE_Char']] }, { name: 'CharCodeRange', bnf: [['CharCode', '"-"', 'CharCode']] }, { name: 'RULE_WHITESPACE', bnf: [['%RULE_WHITESPACE_CHAR*'], ['Comment', 'RULE_WHITESPACE*']] }, { name: 'RULE_S', bnf: [['RULE_WHITESPACE', 'RULE_S*'], ['RULE_EOL', 'RULE_S*']] }, { name: '%RULE_WHITESPACE_CHAR', bnf: [[/\x09/], [/\x20/]], fragment: true }, { name: 'Comment', bnf: [['"/*"', '%RULE_Comment_Body*', '"*/"']] }, { name: '%RULE_Comment_Body', bnf: [[/[^*]/], ['"*"+', /[^/]*/]], fragment: true }, { name: 'RULE_EOL', bnf: [[/\x0D/, /\x0A/], [/\x0A/], [/\x0D/]] }, { name: 'Link', bnf: [["'['", 'Url', "']'"]] }, { name: 'Url', bnf: [[/[^\x5D:/?#]/, '"://"', /[^\x5D#]+/, '%Url1?']] }, { name: '%Url1', bnf: [['"#"', 'NCName']], fragment: true } ]; BNF.defaultParser = new Parser_1.Parser(BNF.RULES, { debug: false }); const preDecorationRE = /^(!|&)/; const decorationRE = /(\?|\+|\*)$/; const subExpressionRE = /^%/; function getBNFRule(name, parser) { if (typeof name == 'string') { let decoration = decorationRE.exec(name); let preDecoration = preDecorationRE.exec(name); let preDecorationText = preDecoration ? preDecoration[0] : ''; let decorationText = decoration ? decoration[0] + ' ' : ''; let subexpression = subExpressionRE.test(name); if (subexpression) { let lonely = isLonelyRule(name, parser); if (lonely) return preDecorationText + getBNFBody(name, parser) + decorationText; return preDecorationText + '(' + getBNFBody(name, parser) + ')' + decorationText; } return name.replace(preDecorationRE, preDecorationText); } else { return name.source .replace(/\\(?:x|u)([a-zA-Z0-9]+)/g, '#x$1') .replace(/\[\\(?:x|u)([a-zA-Z0-9]+)-\\(?:x|u)([a-zA-Z0-9]+)\]/g, '[#x$1-#x$2]'); } } /// Returns true if the rule is a string literal or regular expression without a descendant tree function isLonelyRule(name, parser) { let rule = Parser_1.findRuleByName(name, parser); return (rule && rule.bnf.length == 1 && rule.bnf[0].length == 1 && (rule.bnf[0][0] instanceof RegExp || rule.bnf[0][0][0] == '"' || rule.bnf[0][0][0] == "'")); } function getBNFChoice(rules, parser) { return rules.map(x => getBNFRule(x, parser)).join(' '); } function getBNFBody(name, parser) { let rule = Parser_1.findRuleByName(name, parser); if (rule) return rule.bnf.map(x => getBNFChoice(x, parser)).join(' | '); return 'RULE_NOT_FOUND {' + name + '}'; } function emit(parser) { let acumulator = []; parser.grammarRules.forEach(l => { if (!/^%/.test(l.name)) { let recover = l.recover ? ' { recoverUntil=' + l.recover + ' }' : ''; acumulator.push(l.name + ' ::= ' + getBNFBody(l.name, parser) + recover); } }); return acumulator.join('\n'); } BNF.emit = emit; let subitems = 0; function restar(total, resta) { console.log('reberia restar ' + resta + ' a ' + total); throw new Error('Difference not supported yet'); } function convertRegex(txt) { return new RegExp(txt .replace(/#x([a-zA-Z0-9]{4})/g, '\\u$1') .replace(/#x([a-zA-Z0-9]{3})/g, '\\u0$1') .replace(/#x([a-zA-Z0-9]{2})/g, '\\x$1') .replace(/#x([a-zA-Z0-9]{1})/g, '\\x0$1')); } function getSubItems(tmpRules, seq, parentName, parentAttributes) { let anterior = null; let bnfSeq = []; seq.children.forEach((x, i) => { if (x.type == 'Minus') { restar(anterior, x); } else { } let decoration = seq.children[i + 1]; decoration = (decoration && decoration.type == 'PrimaryDecoration' && decoration.text) || ''; let preDecoration = ''; if (anterior && anterior.type == 'PrimaryPreDecoration') { preDecoration = anterior.text; } let pinned = preDecoration == '~' ? 1 : undefined; if (pinned) { preDecoration = ''; } switch (x.type) { case 'SubItem': let name = '%' + (parentName + subitems++); createRule(tmpRules, x, name, parentAttributes); bnfSeq.push(preDecoration + name + decoration); break; case 'NCName': bnfSeq.push(preDecoration + x.text + decoration); break; case 'StringLiteral': if (decoration || preDecoration || !/^['"/()a-zA-Z0-9&_.:=,+*\-\^\\]+$/.test(x.text)) { bnfSeq.push(preDecoration + x.text + decoration); } else { for (const c of x.text.slice(1, -1)) { if (parentAttributes && parentAttributes["ignoreCase"] == "true" && /[a-zA-Z]/.test(c)) { bnfSeq.push(new RegExp("[" + c.toUpperCase() + c.toLowerCase() + "]")); } else { bnfSeq.push(new RegExp(Parser_1.escapeRegExp(c))); } } } break; case 'CharCode': case 'CharClass': if (decoration || preDecoration) { let newRule = { name: '%' + (parentName + subitems++), bnf: [[convertRegex(x.text)]], pinned }; tmpRules.push(newRule); bnfSeq.push(preDecoration + newRule.name + decoration); } else { bnfSeq.push(convertRegex(x.text)); } break; case 'PrimaryPreDecoration': case 'PrimaryDecoration': break; default: throw new Error(' HOW SHOULD I PARSE THIS? ' + x.type + ' -> ' + JSON.stringify(x.text)); } anterior = x; }); return bnfSeq; } function createRule(tmpRules, token, name, parentAttributes = undefined) { let attrNode = token.children.filter(x => x.type == 'Attributes')[0]; let attributes = {}; if (attrNode) { attrNode.children.forEach(x => { let name = x.children.filter(x => x.type == 'NCName')[0].text; if (name in attributes) { throw new TokenError_1.TokenError('Duplicated attribute ' + name, x); } else { attributes[name] = x.children.filter(x => x.type == 'AttributeValue')[0].text; } }); } let bnf = token.children.filter(x => x.type == 'SequenceOrDifference').map(s => getSubItems(tmpRules, s, name, parentAttributes ? parentAttributes : attributes)); let rule = { name, bnf }; if (name.indexOf('%') == 0) rule.fragment = true; if (attributes['recoverUntil']) { rule.recover = attributes['recoverUntil']; if (rule.bnf.length > 1) throw new TokenError_1.TokenError('only one-option productions are suitable for error recovering', token); } if ('pin' in attributes) { let num = parseInt(attributes['pin']); if (!isNaN(num)) { rule.pinned = num; } if (rule.bnf.length > 1) throw new TokenError_1.TokenError('only one-option productions are suitable for pinning', token); } if ('ws' in attributes) { rule.implicitWs = attributes['ws'] != 'explicit'; } else { rule.implicitWs = null; } rule.fragment = rule.fragment || attributes['fragment'] == 'true'; rule.simplifyWhenOneChildren = attributes['simplifyWhenOneChildren'] == 'true'; tmpRules.push(rule); } function getRules(source, parser = BNF.defaultParser) { let ast = parser.getAST(source); if (!ast) throw new Error('Could not parse ' + source); if (ast.errors && ast.errors.length) { throw ast.errors[0]; } let implicitWs = null; let attrNode = ast.children.filter(x => x.type == 'Attributes')[0]; let attributes = {}; if (attrNode) { attrNode.children.forEach(x => { let name = x.children.filter(x => x.type == 'NCName')[0].text; if (name in attributes) { throw new TokenError_1.TokenError('Duplicated attribute ' + name, x); } else { attributes[name] = x.children.filter(x => x.type == 'AttributeValue')[0].text; } }); } implicitWs = attributes['ws'] == 'implicit'; let tmpRules = []; ast.children.filter(x => x.type == 'Production').map((x) => { let name = x.children.filter(x => x.type == 'NCName')[0].text; createRule(tmpRules, x, name); }); tmpRules.forEach(rule => { if (rule.implicitWs === null) rule.implicitWs = implicitWs; }); return tmpRules; } BNF.getRules = getRules; function Transform(source, subParser = BNF.defaultParser) { return getRules(source.join(''), subParser); } BNF.Transform = Transform; class Parser extends Parser_1.Parser { constructor(source, options) { const subParser = options && options.debugRulesParser === true ? new Parser_1.Parser(BNF.RULES, { debug: true }) : BNF.defaultParser; super(getRules(source, subParser), options); } emitSource() { return emit(this); } } BNF.Parser = Parser; })(BNF || (BNF = {})); exports.default = BNF; //# sourceMappingURL=Custom.js.map