From 9390c7033ac6d8dcd4ab4155fbbfff763716316c Mon Sep 17 00:00:00 2001 From: Nicolas Date: Mon, 13 Apr 2020 12:27:39 -0700 Subject: [PATCH] Improve cpp code generation (#51) Add the following effect during cpp code generation: Properly generate field and methods with the right level of privacy (public/private/protected). Generate .h file instead of .cpp Add #include statement for other class in the puml file Protect against multiple inclusion with preprocessor --- src/index.js | 2 +- src/parser/plantuml.pegjs | 17 ++- src/templates/cpp.hbs | 69 ++++++--- test/cli.js | 23 +-- test/data/car.cpp.cpp | 124 --------------- test/data/car.cpp.h | 312 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 388 insertions(+), 159 deletions(-) delete mode 100644 test/data/car.cpp.cpp create mode 100644 test/data/car.cpp.h diff --git a/src/index.js b/src/index.js index 61869c4..67934df 100644 --- a/src/index.js +++ b/src/index.js @@ -120,7 +120,7 @@ class PlantUmlToCode { python: 'py', ruby: 'rb', typescript: 'ts', - cpp: 'cpp', + cpp: 'h', }; } diff --git a/src/parser/plantuml.pegjs b/src/parser/plantuml.pegjs index 4c4a83f..4d8ac5f 100644 --- a/src/parser/plantuml.pegjs +++ b/src/parser/plantuml.pegjs @@ -47,10 +47,10 @@ extends / connectorsize "|>" { var Extension = require("./Extension"); return new Extension(false) } connectorsize = ".." - / "-up-" - / "-down-" - / "-left-" - / "-right-" + / [-]+ "up" [-]+ + / [-]+ "down" [-]+ + / [-]+ "left" [-]+ + / [-]+ "right" [-]+ / "---" / "--" / [.] @@ -110,11 +110,16 @@ methodparameter = noise item:returntype membername:([ ] membername)? [=] defaultValue:(defaultvalue) [,]? { var Parameter = require("./Parameter"); return new Parameter(item, membername ? membername[1] : null, defaultValue); } / noise item:returntype membername:([ ] membername)? [,]? { var Parameter = require("./Parameter"); return new Parameter(item, membername ? membername[1] : null); } returntype - = items:[^ ,\n\r\t(){}]+ { return items.join("") } + = items:[^ ,\n\r\t(){}<>]+ template:([<] templateargs [>])? typeinfo:[*\[\]&]* { return items.join("") + (template ? template.join("") : "") + typeinfo.join(""); } +templateargs + = items:templatearg+ { return items; } +templatearg + = noise item:returntype noise [,]? { return item; } objectname = objectname:([A-Za-z_][A-Za-z0-9.]*) { return [objectname[0], objectname[1].join("")].join("") } membername - = items:([A-Za-z_\*][A-Za-z0-9_]*) { return [items[0], items[1].join("")].join("") } + = "operator" op:[+=*/<>!~^&|,\[\]]+ { return "operator" + op.join("") } + / items:([A-Za-z_\*][A-Za-z0-9_]*) { return [items[0], items[1].join("")].join("")} defaultvalue = items:([{}\[\]A-Za-z0-9_\'\"]*) { return items.join("") } accessortype diff --git a/src/templates/cpp.hbs b/src/templates/cpp.hbs index 4973464..602ad50 100644 --- a/src/templates/cpp.hbs +++ b/src/templates/cpp.hbs @@ -1,31 +1,64 @@ /** - * \file {{getFullName}}.cpp + * \file {{getFullName}}.h */ +#ifndef {{getFullName}}_h +#define {{getFullName}}_h + +{{#if getExtends}}#include "{{#with getExtends}}{{getFullName}}{{/with}}.h"{{/if}} + + class {{getFullName}}{{#if getExtends}}: public {{#with getExtends}}{{getFullName}}{{/with}}{{/if}} { -{{#if hasPrivateFields}} - private: -{{#each getPrivateFields}} - {{this.getReturnType}} {{this.getName}}; -{{/each}}{{/if}} +{{!#if hasPrivateFields}} + private:{{#each getPrivateFields}} + {{this.getReturnType}} {{this.getName}};{{/each}}{{!/if}} + protected:{{#each getFields}}{{#if this.isProtected}} + {{this.getReturnType}} {{this.getName}};{{/if}}{{/each}} + public:{{#each getFields}}{{#if this.isPublic}} + {{this.getReturnType}} {{this.getName}};{{/if}}{{/each}} + public: - {{getFullName}}({{#each getConstructorArgs}}{{#if @first}}{{else}}, {{/if}}{{#if getName}}{{getName}}{{else}}param{{@index}}{{/if}}{{/each}}) { + {{getFullName}}({{#each getConstructorArgs}}{{#if @first}}{{else}}, {{/if}}{{#if getName}}{{getName}}{{#if getDefaultValue}}={{SafeString this getDefaultValue}}{{/if}}{{else}}param{{@index}}{{/if}}{{/each}}){{#if getExtends}}: {{#with getExtends}}{{getFullName}}{{/with}}(){{/if}} + { // @todo - }{{#if hasPublichMethods}}{{#each getPublichMethods}}{{#if isNotConstructor}} - {{this.getReturnType}} {{this.getName}}{{#if this.getParameters}}({{#each this.getParameters}}{{#if @first}}{{else}}, {{/if}}{{this.getReturnType}} {{#if this.getName}}{{this.getName}}{{else}}param{{@index}}{{/if}}{{/each}}){{else}}(){{/if}} { - {{#if this.needsReturnStatement}} - return null;{{/if}} } -{{/if}}{{/each}}{{/if}} -{{#if hasPrivateMethods}} - private:{{#each getMethods}}{{#if isNotConstructor}} + // Public methods +{{#each getMethods}}{{#if isNotConstructor}}{{#if isPublic}} /**{{#each this.getParameters}} * @param {{getName}} TBD{{/each}}{{#if this.needsReturnStatement}} * @return {{this.getReturnType}}{{/if}} */ - {{this.getReturnType}} {{this.getName}}{{#if this.getParameters}}({{#each this.getParameters}}{{#if @first}}{{else}}, {{/if}}{{this.getReturnType}} {{#if this.getName}}{{this.getName}}{{else}}param{{@index}}{{/if}}{{/each}}){{else}}(){{/if}} { -{{#if this.needsReturnStatement}} - return null;{{/if}} + {{this.getReturnType}} {{this.getName}}{{#if this.getParameters}}({{#each this.getParameters}}{{#if @first}}{{else}}, {{/if}}{{this.getReturnType}} {{#if this.getName}}{{this.getName}}{{#if getDefaultValue}}={{SafeString this getDefaultValue}}{{/if}}{{else}}param{{@index}}{{/if}}{{/each}}){{else}}(){{/if}} { + // @todo {{#if this.needsReturnStatement}} + return {{this.getReturnType}}();{{/if}} } -{{/if}}{{/each}}{{/if}} +{{/if}}{{/if}}{{/each}} + +{{!#if hasProtectedMethods}} + // Protected methods + protected:{{#each getMethods}}{{#if isNotConstructor}}{{#if isProtected}} + /**{{#each this.getParameters}} + * @param {{getName}} TBD{{/each}}{{#if this.needsReturnStatement}} + * @return {{this.getReturnType}}{{/if}} + */ + {{this.getReturnType}} {{this.getName}}{{#if this.getParameters}}({{#each this.getParameters}}{{#if @first}}{{else}}, {{/if}}{{this.getReturnType}} {{#if this.getName}}{{this.getName}}{{#if getDefaultValue}}={{SafeString this getDefaultValue}}{{/if}}{{else}}param{{@index}}{{/if}}{{/each}}){{else}}(){{/if}} { + // @todo {{#if this.needsReturnStatement}} + return {{this.getReturnType}}();{{/if}} + } +{{/if}}{{/if}}{{/each}}{{!/if}} + +{{!#if hasPrivateMethods}} + // Private methods + private:{{#each getMethods}}{{#if isNotConstructor}}{{#if isPrivate}} + /**{{#each this.getParameters}} + * @param {{getName}} TBD{{/each}}{{#if this.needsReturnStatement}} + * @return {{this.getReturnType}}{{/if}} + */ + {{this.getReturnType}} {{this.getName}}{{#if this.getParameters}}({{#each this.getParameters}}{{#if @first}}{{else}}, {{/if}}{{this.getReturnType}} {{#if this.getName}}{{this.getName}}{{#if getDefaultValue}}={{SafeString this getDefaultValue}}{{/if}}{{else}}param{{@index}}{{/if}}{{/each}}){{else}}(){{/if}} { + // @todo {{#if this.needsReturnStatement}} + return {{this.getReturnType}}();{{/if}} + } +{{/if}}{{/if}}{{/each}}{{!/if}} } + +#endif // {{getFullName}}_h diff --git a/test/cli.js b/test/cli.js index 87ba7c0..0c01d81 100644 --- a/test/cli.js +++ b/test/cli.js @@ -7,6 +7,8 @@ const chaiAsPromised = require('chai-as-promised'); // module under test const cli = require('../src/cli'); const { languages, getExtension } = require('../src'); +// list of files to use for test +const inputPumlList = ['./test/data/car']; const { expect } = chai; chai.use(chaiAsPromised); @@ -41,16 +43,17 @@ describe('cli', () => { cli(['node', 'puml2code', '-a']); expect(process.exit.calledOnceWith(1)).to.be.true; }); - describe('input', () => { - languages.forEach((lang) => { - it(`${lang}`, async () => { - let stdout = ''; - const input = './test/data/car.puml'; - const printer = (data) => { stdout += `${data}\n`; }; - const shoulFile = `./test/data/car.${lang}.${getExtension(lang)}`; - const retcode = await cli(['node', 'puml2code', '-l', lang, '-i', input], printer); - expect(stdout).to.be.equal(readFileSync(shoulFile).toString()); - expect(retcode).to.be.equal(0); + inputPumlList.forEach((input) => { + describe(input, () => { + languages.forEach((lang) => { + it(`${lang}`, async () => { + let stdout = ''; + const printer = (data) => { stdout += `${data}\n`; }; + const shoulFile = `${input}.${lang}.${getExtension(lang)}`; + const retcode = await cli(['node', 'puml2code', '-l', lang, '-i', `${input}.puml`], printer); + expect(stdout).to.be.equal(readFileSync(shoulFile).toString()); + expect(retcode).to.be.equal(0); + }); }); }); }); diff --git a/test/data/car.cpp.cpp b/test/data/car.cpp.cpp deleted file mode 100644 index e99ca7d..0000000 --- a/test/data/car.cpp.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/** - * \file Vehicle.cpp - */ - -class Vehicle { - - public: - Vehicle() { - // @todo - } - -} - -/** - * \file Car.cpp - */ - -class Car: public Vehicle { - private: - String model; - String make; - Number year; - - public: - Car() { - // @todo - } - -} - -/** - * \file NamesInThings.cpp - */ - -class NamesInThings { - private: - String _some_private; - String_2 field_2; - - public: - NamesInThings() { - // @todo - } - private: - /** - */ - void member() { - - } - - /** - * @return String1 - */ - String1 member2() { - return null; - } - - /** - */ - void member3() { - - } - - /** - * @return String2 - */ - String2 member_s() { - return null; - } - -} - -/** - * \file Toyota.cpp - */ - -class Toyota: public Car { - - public: - Toyota() { - // @todo - } - -} - -/** - * \file Honda.cpp - */ - -class Honda: public Car { - - public: - Honda() { - // @todo - } - -} - -/** - * \file Ford.cpp - */ - -class Ford: public Car { - - public: - Ford() { - // @todo - } - -} - -/** - * \file Hyundai.cpp - */ - -class Hyundai: public Car { - - public: - Hyundai() { - // @todo - } - -} - diff --git a/test/data/car.cpp.h b/test/data/car.cpp.h new file mode 100644 index 0000000..a851661 --- /dev/null +++ b/test/data/car.cpp.h @@ -0,0 +1,312 @@ +/** + * \file Vehicle.h + */ + +#ifndef Vehicle_h +#define Vehicle_h + + + + +class Vehicle { + private: + protected: + public: + + public: + Vehicle() + { + // @todo + } + // Public methods + + /** + * @return String + */ + String getType() { + // @todo + return String(); + } + + + // Protected methods + protected: + + // Private methods + private: +} + +#endif // Vehicle_h + +/** + * \file Car.h + */ + +#ifndef Car_h +#define Car_h + +#include "Vehicle.h" + + +class Car: public Vehicle { + private: + String model; + String make; + Number year; + protected: + public: + + public: + Car(): Vehicle() + { + // @todo + } + // Public methods + + /** + * @param model TBD + */ + void setModel(String model='lada') { + // @todo + } + + /** + * @param make TBD + */ + void setMake(String make) { + // @todo + } + + /** + * @param TBD + */ + void setYear(Number param0) { + // @todo + } + + /** + * @return String + */ + String getModel() { + // @todo + return String(); + } + + /** + * @return String + */ + String getMake() { + // @todo + return String(); + } + + /** + * @return Number + */ + Number getYear() { + // @todo + return Number(); + } + + + // Protected methods + protected: + + // Private methods + private: +} + +#endif // Car_h + +/** + * \file NamesInThings.h + */ + +#ifndef NamesInThings_h +#define NamesInThings_h + + + + +class NamesInThings { + private: + String _some_private; + String_2 field_2; + protected: + public: + String field; + String1 field1; + + public: + NamesInThings() + { + // @todo + } + // Public methods + + /** + */ + void member() { + // @todo + } + + + // Protected methods + protected: + /** + */ + void member3() { + // @todo + } + + + // Private methods + private: + /** + * @return String1 + */ + String1 member2() { + // @todo + return String1(); + } + + /** + * @return String2 + */ + String2 member_s() { + // @todo + return String2(); + } + +} + +#endif // NamesInThings_h + +/** + * \file Toyota.h + */ + +#ifndef Toyota_h +#define Toyota_h + +#include "Car.h" + + +class Toyota: public Car { + private: + protected: + public: + + public: + Toyota(): Car() + { + // @todo + } + // Public methods + + + // Protected methods + protected: + + // Private methods + private: +} + +#endif // Toyota_h + +/** + * \file Honda.h + */ + +#ifndef Honda_h +#define Honda_h + +#include "Car.h" + + +class Honda: public Car { + private: + protected: + public: + + public: + Honda(): Car() + { + // @todo + } + // Public methods + + + // Protected methods + protected: + + // Private methods + private: +} + +#endif // Honda_h + +/** + * \file Ford.h + */ + +#ifndef Ford_h +#define Ford_h + +#include "Car.h" + + +class Ford: public Car { + private: + protected: + public: + + public: + Ford(): Car() + { + // @todo + } + // Public methods + + + // Protected methods + protected: + + // Private methods + private: +} + +#endif // Ford_h + +/** + * \file Hyundai.h + */ + +#ifndef Hyundai_h +#define Hyundai_h + +#include "Car.h" + + +class Hyundai: public Car { + private: + protected: + public: + + public: + Hyundai(): Car() + { + // @todo + } + // Public methods + + + // Protected methods + protected: + + // Private methods + private: +} + +#endif // Hyundai_h +