Skip to content

Commit

Permalink
WIP(template) Generate markdown_template from templatemark
Browse files Browse the repository at this point in the history
Signed-off-by: Jerome Simeon <[email protected]>
  • Loading branch information
jeromesimeon committed Aug 31, 2020
1 parent 1752554 commit 9fefb45
Show file tree
Hide file tree
Showing 35 changed files with 276 additions and 44 deletions.
11 changes: 11 additions & 0 deletions packages/markdown-template/lib/TemplateMarkTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const ParserManager = require('./parsermanager');
const normalizeToMarkdownCicero = require('./normalize').normalizeToMarkdownCicero;
const normalizeFromMarkdownCicero = require('./normalize').normalizeFromMarkdownCicero;

const ToMarkdownTemplateVisitor = require('./ToMarkdownTemplateVisitor');
const ToCiceroMarkVisitor = require('./ToCiceroMarkVisitor');

/**
Expand Down Expand Up @@ -95,6 +96,16 @@ class TemplateMarkTransformer {
return this.tokensToMarkdownTemplate(tokenStream, modelManager, templateKind, options);
}

/**
* Converts a TemplateMark DOM to a template markdown string
* @param {object} input TemplateMark DOM
* @returns {string} the template markdown text
*/
toMarkdownTemplate(input) {
const visitor = new ToMarkdownTemplateVisitor();
return visitor.toMarkdownTemplate(templateMarkManager.serializer,input);
}

/**
* Parse a CiceroMark DOM against a TemplateMark DOM
* @param {{fileName:string,content:string}} input the ciceromark input
Expand Down
79 changes: 79 additions & 0 deletions packages/markdown-template/lib/ToMarkdownTemplateVisitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

const CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils;
const FromCommonMarkVisitor = require('@accordproject/markdown-common').FromCommonMarkVisitor;
const fromcommonmarkrules = require('@accordproject/markdown-common').fromcommonmarkrules;
const fromtemplatemarkrules = require('./fromtemplatemarkrules');

/**
* Fixes up the root note, removing Clause or Contract indication
* @param {object} input the input templatemark
* @return {object} the fixed up templatemark
*/
function fixupRootNode(input) {
const rootNode = {
'$class': 'org.accordproject.commonmark.Document',
'xmlns' : 'http://commonmark.org/xml/1.0',
'nodes': input.nodes[0].nodes
};
return rootNode;
}

/**
* Converts a TemplateMark DOM to a template markdown string.
*/
class ToMarkdownTemplateVisitor extends FromCommonMarkVisitor {
/**
* Construct the visitor.
* @param {object} [options] configuration options
* @param {*} resultSeq how to sequentially combine results
* @param {object} rules how to process each node type
*/
constructor(options) {
const resultString = (result) => {
return result;
};
const resultSeq = (parameters,result) => {
result.forEach((next) => {
parameters.result += next;
});
};
const setFirst = (thingType) => {
return thingType === 'Item' || thingType === 'ClauseDefinition' || thingType === 'ListBlockDefinition' ? true : false;
};
const rules = fromcommonmarkrules;
Object.assign(rules,fromtemplatemarkrules);
super(options,resultString,resultSeq,rules,setFirst);
}

/**
* Converts a TemplateMark DOM to a template markdown string.
* @param {*} serializer - TemplateMark serializer
* @param {*} input - TemplateMark DOM (JSON)
* @returns {string} the template markdown string
*/
toMarkdownTemplate(serializer,input) {
const parameters = {};
const fixedInput = serializer.fromJSON(fixupRootNode(input));
parameters.result = this.resultString('');
parameters.stack = CommonMarkUtils.blocksInit();
fixedInput.accept(this, parameters);
return parameters.result.trim();
}
}

module.exports = ToMarkdownTemplateVisitor;
98 changes: 98 additions & 0 deletions packages/markdown-template/lib/fromtemplatemarkrules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

const CommonMarkUtils = require('@accordproject/markdown-common').CommonMarkUtils;

const rules = {};
// Inlines
rules.VariableDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const result = [resultString('{{'),resultString(thing.name),resultString('}}')];
resultSeq(parameters,result);
};
rules.FormattedVariableDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const result = [resultString('{{'),resultString(thing.name),resultString(' as "'),resultString(thing.format),resultString('"}}')];
resultSeq(parameters,result);
};
rules.EnumVariableDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const result = [resultString('{{'),resultString(thing.name),resultString('}}')];
resultSeq(parameters,result);
};
rules.FormulaDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const result = [resultString('{{%'),resultString(thing.code),resultString('%}}')];
resultSeq(parameters,result);
};
rules.ConditionalDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const next1 = `{{#if ${thing.name}}}`;
const whenTrue = visitor.visitChildren(visitor,thing,parameters,'whenTrue');
const whenFalse = visitor.visitChildren(visitor,thing,parameters,'whenFalse');
const next2 = '{{/if}}';
let result;
if (whenFalse) {
const next3 = '{{else}}';
result = [resultString(next1),resultString(whenTrue),resultString(next3),resultString(whenFalse),resultString(next2)];
} else {
result = [resultString(next1),resultString(whenTrue),resultString(next2)];
}
resultSeq(parameters,result);
};
rules.OptionalDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const next1 = `{{#optional ${thing.name}}}`;
const whenSome = visitor.visitChildren(visitor,thing,parameters,'whenSome');
const whenNone = visitor.visitChildren(visitor,thing,parameters,'whenNone');
const next2 = '{{/optional}}';
let result;
if (whenNone) {
const next3 = '{{else}}';
result = [resultString(next1),resultString(whenSome),resultString(next3),resultString(whenNone),resultString(next2)];
} else {
result = [resultString(next1),resultString(whenSome),resultString(next2)];
}
resultSeq(parameters,result);
};
rules.WithDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const next1 = `{{#with ${thing.name}}}`;
const next2 = '{{/with}}';
const result = [resultString(next1),children,resultString(next2)];
resultSeq(parameters,result);
};
rules.JoinDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const sepAttr = thing.separator ? ' separator="' + thing.separator + '"' : '';
const next1 = `{{#join ${thing.name}${sepAttr}}}`;
const next2 = '{{/join}}';
const result = [resultString(next1),children,resultString(next2)];
resultSeq(parameters,result);
};
// Container blocks
rules.ListBlockDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const listKind = thing.type === 'bullet' ? 'ulist' : 'olist';
const prefix = CommonMarkUtils.mkPrefix(parameters,1);
const next1 = prefix;
const next2 = `{{#${listKind} ${thing.name}}}\n`;
const next3 = prefix;
const next4 = `{{/${listKind}}}`;
const result = [resultString(next1),resultString(next2),children,resultString(next3),resultString(next4)];
resultSeq(parameters,result);
};
rules.ClauseDefinition = (visitor,thing,children,parameters,resultString,resultSeq) => {
const next1 = CommonMarkUtils.mkPrefix(parameters,2);
const srcAttr = thing.src ? ' src="' + thing.src + '"' : '';
const next2 = `{{#clause ${thing.name}${srcAttr}}}\n`;
const next3 = '\n{{/clause}}';
const result = [resultString(next1),resultString(next2),children,resultString(next3)];
resultSeq(parameters,result);
};

module.exports = rules;
37 changes: 23 additions & 14 deletions packages/markdown-template/test/TemplateMarkTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ const ModelLoader = require('@accordproject/concerto-core').ModelLoader;
const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer;
const TemplateMarkTransformer = require('../lib/TemplateMarkTransformer');

const normalizeNLs = require('../lib/normalize').normalizeNLs;
const normalizeToMarkdownCicero = require('../lib/normalize').normalizeToMarkdownCicero;
const normalizeFromMarkdownCicero = require('../lib/normalize').normalizeFromMarkdownCicero;

const loadFile = (x) => { return { fileName: x, content: fs.readFileSync(x, 'utf8') }; };
const loadFile = (x) => { return { fileName: x, content: normalizeNLs(fs.readFileSync(x, 'utf8')) }; };
const loadPlugin = (x) => {
return fs.existsSync(x) ? require(path.join('..',x)) : {};
};
Expand All @@ -42,8 +43,8 @@ const currentTime = datetimeutil.setCurrentTime('2000-07-22T10:45:05+04:00');

// Workloads
const successes = [
{name:'testSpec',kind:'clause'},
{name:'testSpecChanged',kind:'clause'},
{name:'testSpec',kind:'clause',skipGrammar:true}, // Full spec roundtrip
{name:'testSpecChanged',kind:'clause',skipGrammar:true}, // Full spec roundtrip
{name:'test1',kind:'clause'},
{name:'test2',kind:'contract'},
{name:'test3',kind:'contract'},
Expand All @@ -69,19 +70,19 @@ const successes = [
{name:'testMonetaryAmount2',kind:'clause'},
{name:'testMonetaryAmount3',kind:'clause'},
{name:'testMonetaryAmount4',kind:'clause'},
{name:'testLarge',kind:'contract'},
{name:'testLarge',kind:'contract',skipGrammar:true}, // Just to be double checked
{name:'testRepeat',kind:'clause'},
{name:'testMd1',kind:'clause'},
{name:'testMd2',kind:'contract'},
{name:'testMd3',kind:'contract'},
{name:'testMd4',kind:'clause'},
{name:'testMd5',kind:'clause'},
{name:'testHeading',kind:'clause'},
{name:'testMd1',kind:'clause',skipGrammar:true}, // Need alternative versions of md.
{name:'testMd2',kind:'contract',skipGrammar:true}, // Need alternative versions of md.
{name:'testMd3',kind:'contract',skipGrammar:true}, // Need alternative versions of md.
{name:'testMd4',kind:'clause',skipGrammar:true}, // Need alternative versions of md.
{name:'testMd5',kind:'clause',skipGrammar:true}, // Need alternative versions of md.
{name:'testHeading',kind:'clause',skipGrammar:true}, // Need alternative versions of md.
{name:'testUList',kind:'contract'},
{name:'testOList',kind:'contract'},
{name:'testOList2',kind:'contract'},
{name:'testQuoteOList',kind:'contract'},
{name:'testOListOList2',kind:'contract'},
{name:'testQuoteOList',kind:'contract',skipGrammar:true}, // Issue with prefixes on the content of the list
{name:'testOListOList2',kind:'contract',skipGrammar:true}, // Issue with prefixes on the content of the list
{name:'testUListThis',kind:'contract'},
{name:'testJoin',kind:'contract'},
{name:'testWith',kind:'contract'},
Expand All @@ -102,11 +103,11 @@ const successes = [
{name:'helloworld',kind:'clause'},
{name:'installment-sale',kind:'contract'},
{name:'interest-rate-swap',kind:'contract'},
{name:'ip-payment',kind:'clause'},
{name:'ip-payment',kind:'clause',skipGrammar:true}, // Issue #??? -- should be filed about ordered lists with a custom starting number (even on plain commonmark)
{name:'latedeliveryandpenalty',kind:'contract'},
{name:'rental-deposit-with',kind:'contract'},
{name:'signature-name-date',kind:'clause'},
{name:'volumediscountulist',kind:'contract'},
{name:'volumediscountulist',kind:'contract',skipGrammar:true}, // Need alternative versions of md.
];

const templateFailures = [
Expand Down Expand Up @@ -172,6 +173,14 @@ function runSuccesses(tests) {
result.should.deep.equal(grammarJson);
});

if (!test.skipGrammar) {
it('should draft template grammar back', async () => {
const grammarJson = templateMarkTransformer.fromMarkdownTemplate(grammar,modelManager,kind);
const result = templateMarkTransformer.toMarkdownTemplate(grammarJson);
result.should.equal(grammar.content);
});
}

it('should parse sample', async () => {
const result = templateMarkTransformer.fromMarkdownCicero(sample,grammar,modelManager,kind,currentTime);
if (kind === 'clause') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
## Acceptance of Delivery.
Acceptance of Delivery.
----

{{shipper}} will be deemed to have completed its delivery obligations
if in {{receiver}}'s opinion, the {{deliverable}} satisfies the
Acceptance Criteria, and {{receiver}} notifies {{shipper}} in writing
that it is accepting the {{deliverable}}.

## Inspection and Notice.
Inspection and Notice.
----

{{receiver}} will have {{businessDays}} Business Days to inspect and
evaluate the {{deliverable}} on the delivery date before notifying
{{shipper}} that it is either accepting or rejecting the
{{deliverable}}.

## Acceptance Criteria.
Acceptance Criteria.
----

The "Acceptance Criteria" are the specifications the {{deliverable}}
must meet for the {{shipper}} to comply with its requirements and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ General.

Interpretation. For purposes of this Agreement, (a) the words "include," "includes," and "including" are deemed to be followed by the words "without limitation"; (b) the word "or" is not exclusive; and (c) the words "herein," "hereof," "hereby," "hereto," and "hereunder" refer to this Agreement as a whole. This Agreement is intended to be construed without regard to any presumption or rule requiring construction or interpretation against the party drafting an instrument or causing any instrument to be drafted.

Entire Agreement. This Agreement, including and together with any related attachments, constitutes the sole and entire agreement of the parties with respect to the subject matter contained herein, and supersedes all prior and contemporaneous understandings, agreements, representations, and warranties, both written and oral, with respect to such subject matter.
Entire Agreement. This Agreement, including and together with any related attachments, constitutes the sole and entire agreement of the parties with respect to the subject matter contained herein, and supersedes all prior and contemporaneous understandings, agreements, representations, and warranties, both written and oral, with respect to such subject matter.

Severability. If any term or provision of this Agreement is invalid, illegal, or unenforceable in any jurisdiction, such invalidity, illegality, or unenforceability will not affect the enforceability of any other term or provision of this Agreement, or invalidate or render unenforceable such term or provision in any other jurisdiction. [Upon a determination that any term or provision is invalid, illegal, or unenforceable, [the parties shall negotiate in good faith to/the court may] modify this Agreement to effect the original intent of the parties as closely as possible in order that the transactions contemplated hereby be consummated as originally contemplated to the greatest extent possible.]

Assignment. Licensee may freely assign or otherwise transfer all or any of its rights, or delegate or otherwise transfer all or any of its obligations or performance, under this Agreement without Licensor's consent. This Agreement is binding upon and inures to the benefit of the parties hereto and their respective permitted successors and assigns.

Assignment. Licensee may freely assign or otherwise transfer all or any of its rights, or delegate or otherwise transfer all or any of its obligations or performance, under this Agreement without Licensor's consent. This Agreement is binding upon and inures to the benefit of the parties hereto and their respective permitted successors and assigns.
3 changes: 2 additions & 1 deletion packages/markdown-template/test/data/empty/grammar.tem.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# Write your clause template here
Write your clause template here
====
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

{{MIN_PAYMENT}} or more per month on the first day of each and every month, and continuing until the entire balance, including both principal and interest, shall be paid in full -- provided, however, that the entire balance due plus accrued interest and any other amounts due here-under shall be paid in full on or before 24 months.

Monthly payments, which shall start on month {{FIRST_MONTH}}, include both principal and interest with interest at the rate of {{INTEREST_RATE}}%, computed monthly on the remaining balance from time to time unpaid.
Monthly payments, which shall start on month {{FIRST_MONTH}}, include both principal and interest with interest at the rate of {{INTEREST_RATE}}%, computed monthly on the remaining balance from time to time unpaid.
Loading

0 comments on commit 9fefb45

Please sign in to comment.