UNPKG

apg-js-examples

Version:

Examples of using the suite of apg-js applications and libraries.

147 lines (145 loc) 5.64 kB
/* eslint-disable func-names */ /* ************************************************************************************* * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause) * ********************************************************************************* */ // With recursive descent parsers, an Abstract Syntax Tree (AST) can be thought of as a subset of the full parse tree. // Each node of the AST holds the phrase that was matched at the corresponding parse tree node. // The AST is very useful for translating the parsed string. // `apg` parsers generate ASTs and `apg-exp` uses the AST internally for all except the `test()` functions. // Additionally, it always makes it available to the user as well. // // This example assumes that you are already familiar with the AST object in `apg` parsers. // If not, check out the `./src/ast` examples to get started. // We will use the floating point grammar and put the numbers in normal form as we did in the `./src/Folat` example // except this time we will do it with a translation of the AST. /* get some utility tools */ (function ast() { const apgJs = require('apg-js'); const writeHtml = require('../writeHtml'); const Float = require('./grammars/float'); const { apgExp: ApgExp } = apgJs; const { apgLib } = apgJs; const id = apgLib.ids; const { charsToString } = apgLib.utils; /* define the AST callback functions */ const astFloat = function (state, chars, phraseIndex, phraseLength, data) { const ret = id.SEM_OK; if (state === id.SEM_PRE) { /* set all values to default */ data.sign = ''; data.integer = '0'; data.fraction = '0'; data.esign = '+'; data.exp = 0; } else if (state === id.SEM_POST) { /* compute the normalized form */ const exponent = data.exp === 0 ? '' : `e${data.esign}${data.exp}`; data.normal = `${data.sign + data.integer}.${data.fraction}${exponent}`; } return ret; }; const astSign = function (state, chars, phraseIndex, phraseLength, data) { const ret = id.SEM_OK; if (state === id.SEM_PRE) { if (chars[phraseIndex] === 45) { data.sign = '-'; } } return ret; }; const astInteger = function (state, chars, phraseIndex, phraseLength, data) { const ret = id.SEM_OK; if (state === id.SEM_PRE) { data.integer = charsToString(chars, phraseIndex, phraseLength); } return ret; }; const astFraction = function (state, chars, phraseIndex, phraseLength, data) { const ret = id.SEM_OK; if (state === id.SEM_PRE) { data.fraction = charsToString(chars, phraseIndex, phraseLength); } return ret; }; const astEsign = function (state, chars, phraseIndex, phraseLength, data) { const ret = id.SEM_OK; if (state === id.SEM_PRE) { data.esign = charsToString(chars, phraseIndex, phraseLength); } return ret; }; const astExp = function (state, chars, phraseIndex, phraseLength, data) { const ret = id.SEM_OK; if (state === id.SEM_PRE) { const exp = charsToString(chars, phraseIndex, phraseLength); data.exp = parseInt(exp, 10); } return ret; }; try { let result; let str; let html; const grammar = new Float(); console.log(); console.log('AST demonstration'); str = ''; str += '|||123|||123.|||.123|||-1.23|||+.123|||123.e2|||+.123E+1|||-123.123456789e-10|||'; str += '123e0|||+1.23e-0|||-.123e-001|||123e-000|||'; console.log(); console.log('input string:'); console.log(str); const flags = 'g'; const exp = new ApgExp(grammar, flags); // The AST callback translation functions need to be configured before the call to exec(). exp.ast.callbacks.float = astFloat; exp.ast.callbacks.sign = astSign; exp.ast.callbacks.integer = astInteger; exp.ast.callbacks.fraction = astFraction; exp.ast.callbacks.esign = astEsign; exp.ast.callbacks.exp = astExp; /* initialize the HTML output */ html = ''; html += '<h3>grammar source</h3>\n'; html += exp.sourceToHtml(); html += '<h3>input string</h3>\n'; html += `<pre>${str}</pre>\n`; html += '<h3>floating point numbers</h3>\n'; html += `<table class="${apgLib.style.CLASS_STATS}">\n`; html += '<tr><th>result[0]</th><th>normal form</th></tr>\n'; console.log(); console.log('apg-exp result:'); const data = {}; const TRUE = true; while (TRUE) { result = exp.exec(str); if (result === null) { break; } exp.ast.translate(data); html += `<tr><td>${result[0]}</td><td>${data.normal}</td></tr>\n`; console.log(`result[0]: ${result[0]} :normal form: ${data.normal}`); } html += '</table>\n'; // Just for fun, let's also use the AST in a replacement function. const func = function (resultArg, expArg) { const thisData = {}; expArg.ast.translate(thisData); return thisData.normal; }; str = exp.replace(str, func); console.log(); console.log('replacement string:'); console.log(str); console.log(); html += '<h3>replacement function exp.replace()</h3>\n'; html += `<pre>${str}</pre>\n`; /* finally, write the HTML page */ const page = apgLib.utils.htmlToPage(html); const htmlName = 'ast'; writeHtml(page, htmlName); } catch (e) { console.log(`EXCEPTION: ${e.message}`); } })();