Giter Club home page Giter Club logo

plantcode's Introduction

plantcode

Provides a command line utility to generate code in various languages given a plantuml class diagram.

Command line options

Usage: plantcode [options] <inputFile>

Generates classfile(s) for the provided PlantUML file specified by <input_file>
and writes to standard output.

Options:
  -l, --language <language>          name of the programming language
                                     which the produced class files
                                     will be written in
  -o, --out <output_path>            the path to output the file(s) to
      --show-languages               displays all the current supported
                                     programming languages for use
                                     for with the language option
  -h, --help                         print help and exit

The currently supported languages are

  • CoffeeScript (coffeescript) [default]
  • C# (csharp)
  • ECMAScript5 (javascript)
  • ECMAScript6 (javascript2.0)
  • Java (java)
  • PHP (php)
  • Python (python)
  • Ruby (ruby)
  • TypeScript (typescript)
  • Swift (swift)
  • Kotlin (kotlin)

PEG.js

The most recent version of PlantUML does not have a defined grammar to use with parsing the PlantUML code. Below is a guess as to what the grammer for the language should be, relative to the PEG.js parser. This creates a parser which is then used in the creation of all output files. This grammar should validate to any valid PlantUML file.

plantumlfile
  = items:((noise newline { return null }) / (noise "@startuml" noise newline filelines:umllines noise "@enduml" noise { var UMLBlock = require("./UMLBlock"); return new UMLBlock(filelines) }))* { for (var i = 0; i < items.length; i++) { if (items[i] === null) { items.splice(i, 1); i--; } } return items }
umllines
  = lines:(umlline*) { for (var i = 0; i < lines.length; i++) { if (lines[i]===null) { lines.splice(i, 1); i--; } } return lines; }
umlline
  = propertyset newline { return null }
  / titleset newline { return null }
  / noise newline { return null }
  / commentline { return null }
  / noteline { return null }
  / hideline newline { return null }
  / skinparams newline { return null }
  / declaration:packagedeclaration newline { return declaration }
  / declaration:namespacedeclaration newline { return declaration }
  / declaration:classdeclaration newline { return declaration }
  / declaration:interfacedeclaration newline { return declaration }
  / declaration:abstractclassdeclaration newline { return declaration }
  / declaration:memberdeclaration newline { return declaration }
  / declaration:connectordeclaration newline { return declaration }
hideline
  = noise "hide empty members" noise
skinparams
  = noise "skinparam" noise [^\r\n]+
connectordeclaration
  = noise leftObject:objectname noise connectordescription? noise connector:connectortype noise connectordescription? noise rightObject:objectname noise ([:] [^\r\n]+)? { var Connection = require("./Connection"); return new Connection(leftObject, connector, rightObject) }
connectordescription
  = noise ["]([\\]["]/[^"])*["] noise
titleset
  = noise "title " noise [^\r\n]+ noise
commentline
  = noise "'" [^\r\n]+ noise
  / noise ".." [^\r\n\.]+ ".." noise
  / noise "--" [^\r\n\-]+ "--" noise
  / noise "__" [^\r\n\_]+ "__" noise
noteline
  = noise "note " noise [^\r\n]+ noise
connectortype
  = item:extends { return item }
  / concatenates { var Composition = require("./Composition"); return new Composition() }
  / aggregates { var Aggregation = require("./Aggregation"); return new Aggregation() }
  / connectorsize { return null }
extends
  = "<|" connectorsize { var Extension = require("./Extension"); return new Extension(true) }
  / connectorsize "|>" { var Extension = require("./Extension"); return new Extension(false) }
connectorsize
  = ".."
  / "-up-"
  / "-down-"
  / "-left-"
  / "-right-"
  / "---"
  / "--"
  / [.]
  / [-]
concatenates
  = "*" connectorsize
  / connectorsize [*]
aggregates
  = "o" connectorsize
  / connectorsize [o]
startblock
  = noise [{] noise
endblock
  = noise [}]
propertyset
  = "setpropname.*"
packagedeclaration
  = "package " objectname startblock newline umllines endblock
  / "package " objectname newline umllines "end package"
abstractclassdeclaration
  = noise "abstract " noise "class "? noise classname:objectname noise startblock lines:umllines endblock { var AbstractClass = require("./AbstractClass"); return new AbstractClass(classname, lines) }
  / noise "abstract " noise "class "? noise classname:objectname noise { var AbstractClass = require("./AbstractClass"); return new AbstractClass(classname) }
  / noise "abstract " noise "class "? noise classname:objectname noise newline noise lines:umllines "end class" { var AbstractClass = require("./AbstractClass"); return new AbstractClass(classname, lines) }
noise
  = [ \t]*
newline
  = [\r\n]
  / [\n]
classdeclaration
  = noise "class " noise classname:objectname noise startblock lines:umllines endblock { var Class = require("./Class"); return new Class(classname, lines) }
  / noise "class " noise classname:objectname noise "<<" noise [^>]+ noise ">>" noise { var Class = require("./Class"); return new Class(classname) }
  / noise "class " noise classname:objectname noise { var Class = require("./Class"); return new Class(classname) }
  / noise "class " noise classname:objectname noise newline noise lines:umllines "end class" { var Class = require("./Class"); return new Class(classname, lines) }
interfacedeclaration
  = noise "interface " noise interfacename:objectname noise startblock lines:umllines endblock { var Interface = require("./Interface"); return new Interface(interfacename, lines) }
  / noise "interface " noise interfacename:objectname noise "<<" noise [^>]+ noise ">>" noise { var Interface = require("./Interface"); return new Interface(interfacename) }
  / noise "interface " noise interfacename:objectname noise { var Interface = require("./Interface"); return new Interface(interfacename) }
  / noise "interface " noise interfacename:objectname noise newline noise lines:umllines "end interface" { var  Interface = require("./Interface"); return new Interface(interfacename, lines) }
color
  = [#][0-9a-fA-F]+
namespacedeclaration
  = noise "namespace " noise namespacename:objectname noise color? noise startblock lines:umllines endblock { var Namespace = require("./Namespace"); return new Namespace(namespacename, lines) }
  / noise "namespace " noise namespacename:objectname noise newline umllines "end namespace" { var Namespace = require("./Namespace"); return new Namespace(namespacename) }
staticmemberdeclaration
  = "static " memberdeclaration
memberdeclaration
  = declaration:methoddeclaration { return declaration }
  / declaration:fielddeclaration { return declaration }
fielddeclaration
  = noise accessortype:accessortype noise returntype:returntype noise membername:membername noise { var Field = require("./Field"); return new Field(accessortype, returntype, membername) }
  / noise accessortype:accessortype noise membername:membername noise [:] noise returntype:returntype noise { var Field = require("./Field"); return new Field(accessortype, returntype, membername) }
  / noise accessortype:accessortype noise membername:membername noise { var Field = require("./Field"); return new Field(accessortype, "void", membername) }
  / noise returntype:returntype noise membername:membername noise { var Field = require("./Field"); return new Field("+", returntype, membername) }
  / noise membername:membername noise [:] noise returntype:returntype noise { var Field = require("./Field"); return new Field("+", returntype, membername) }
methoddeclaration
  = noise field:fielddeclaration [(] parameters:methodparameters [)] noise [:] noise returntype:returntype noise { var Method = require("./Method"); return new Method(field.getAccessType(), returntype, field.getName(), parameters); }
  / noise field:fielddeclaration [(] parameters:methodparameters [)] noise { var Method = require("./Method"); return new Method(field.getAccessType(), field.getReturnType(), field.getName(), parameters); }
methodparameters
  = items:methodparameter* { return items; }
methodparameter
  = 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("") }
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("") }
accessortype
  = publicaccessor
  / privateaccessor
  / protectedaccessor
publicaccessor
  = [+]
privateaccessor
  = [-]
protectedaccessor
  = [#]

Goals

Initially this project will only run with node.js and output coffeescript classes. The general idea is that, given any PlantUML file, we will be able to generate class files in any output language. Eventually moving on from node.js and supporting other tools to use for conversion.

Example

The current example is very basic and features a common example of a car.

PlantUML Code:

@startuml

hide empty members

abstract Car {
  + void setModel(String model)
  + void setMake(String make)
  + void setYear(Number)
  + String getModel()
  + String getMake()
  + Number getYear()
}
  
class Toyota
class Honda
class Ford
  
Toyota --|> Car
Honda --|> Car
Ford --|> Car

@enduml

CoffeeScript Produced:

class Car

  setModel: (model) ->

  setMake: (make) ->

  setYear: (paramX) ->

  getModel:  ->

  getMake:  ->

  getYear:  ->

class Toyota extends Car

class Honda extends Car

class Ford extends Car

Running:

npm install
plantcode -l coffescript tests/car.pegjs > tests/car.coffee

Testing:

npm test

Updating PEGJS grammar:

If you update the PEGJS grammar file src/plantuml.pegjs you must run this command to update the corresponding src/plantuml.js file.

npm run build

plantcode's People

Contributors

bafolts avatar dependabot[bot] avatar hfhbd avatar kogepan159 avatar loren138 avatar potherca avatar prantlf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plantcode's Issues

"end class" isn't valid plantuml?

Hi,

one way to create a class seems to be

class classname
...
end class

But as far as I know, that's not valid PlantUML? Am i missing something?

Failed on skinparam

When processing a .plu like this:

@startuml

skinparam defaultFontSize 20

hide empty members
class Toyota
@enduml

It shows an error msg:

Error parsing input file:

npx not working due to missing dependencies

This does not work, I think it is because packages dependencies like pegjs are set as devDependencies

npx plantcode -l coffescript tests/car.pegjs > tests/car.coffee
internal/modules/cjs/loader.js:783
    throw err;
    ^

Error: Cannot find module '/home/adelin/.npm/_npx/1104692/lib/node_modules/plantcode/node_modules/pegjs/bin/pegjs'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:780:15)
    at Function.Module._load (internal/modules/cjs/loader.js:685:27)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1014:10)
    at internal/main/run_main_module.js:17:11 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}

Suggestion: create XMI output to open the way to use other tools

In addition to generating code from templates, generate XMI representing the UML diagram. There are many tools that can parse this. See e.g. http://agx.me/ (doc source) and related packages.

That project implements UML profiles to generate various kinds of projects and Python structures (apps using the Pyramid web framework, content types for the Plone CMS, SQLAlchemy classes, etc.) from class diagrams.

If plantuml-code-generator generated UML in XMI format, then the output could be further processed using tools like AGX.

  • Here's one that converts XMI statecharts to C code: ufsm.
  • Target the .NET Platform: NMF.
  • Generate desktop/web prototypes from XMI: U-Fabrik.
  • Generate PHP from XMI: php-xmi-tools.

It would also allow sketching diagrams in PlantUML before importing them to more unwieldy full-featured tools.

Add support for plantuml notes

In plantuml class diagram there are two ways to provide documentation/comments: notes and free text inside the class body.
As plantcode is very strict about the grammar, it doesn't allow the puml source to have them.

Copying the docs to the code is a nice to have -- for the purpose of the issue, just ignoring them would be ok.

Broken on https://www.planttext.com/

It looks that this project is used on https://www.planttext.com/ but I'm not sure where to file an issue for the fact that the instance is broken (the reporting link is wrong).

When I try any conversion, I get:

Error parsing input file: 
/home/ubuntu/bin/PlantCode/Log/2019-17-3-11-34-07-typescript.txt
{ message: 'Expected [ \\t] or [A-Za-z_] but "-" found.',
  expected: 
   [ { type: 'class', value: '[ \\t]', description: '[ \\t]' },
     { type: 'class', value: '[A-Za-z_]', description: '[A-Za-z_]' } ],
  found: '-',
  location: 
   { start: { offset: 40, line: 4, column: 5 },
     end: { offset: 41, line: 4, column: 6 } },
  name: 'SyntaxError' }

Parser doesn't support interfaces or headers

Parser doesn't parse:

header
DRAFT
Authors: xxx
Version: 0.1
endheader

Or:

interface Type {
+URI getIdentifier()
}

I tried to update the parser but it looks like the peg-update script is missing?

Create example for running the converter client side instead of from node.

Since the code base is in javascript the code should run just fine from any browser. This will allow applications to convert code on the client side as well as the server side. The code shouldn't have to be re-factored much. Most likely just some controller class created to run the conversion and retrieve the results.

plantuml-code-generator can't handle comments

Following plant file fails:

  @startuml
  ' this is a comment
  class A
  class B
  A --|> B
  @enduml

with message:

Error parsing input file: 
SyntaxError: Expected "@enduml", "abstract ", "class ", "hide empty members", "namespace ", "package ", "setpropname.*", "skinparam", "title ", [ \t], [#], [+], [-], [A-Za-z_], [\n] or [\r\n] but "'" found.

Tested with upstream code

Cannot find module './Aggregation'

...

Ciudad "1" o-- "many" Direccion
Ciudadano "1" *-- "many" Direccion

...

Error: Cannot find module './Aggregation'
Require stack:

  • /home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js
  • /home/aor/Descargas/plantuml-code-generator-master/plantcode
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1085:15)
    at Function.Module._load (internal/modules/cjs/loader.js:928:27)
    at Module.require (internal/modules/cjs/loader.js:1145:19)
    at require (internal/modules/cjs/helpers.js:75:18)
    at peg$c44 (/home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js:83:50)
    at peg$parseconnectortype (/home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js:1594:18)
    at peg$parseconnectordeclaration (/home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js:929:22)
    at peg$parseumlline (/home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js:785:38)
    at peg$parseumllines (/home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js:567:14)
    at peg$parseplantumlfile (/home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js:415:22) {
    code: 'MODULE_NOT_FOUND',
    requireStack: [
    '/home/aor/Descargas/plantuml-code-generator-master/src/plantuml.js',
    '/home/aor/Descargas/plantuml-code-generator-master/plantcode'
    ]
    }

Extends does not work if right hand side of connection is not present

Extends is not present if the right hand side of a connection is not present.

Example:

class A {
        + someProp string
        - someOtherProp number

        + number computeSomething(string x)
    }
A --|> B
@enduml```

Code:
```class A {
  private string : someProp;
  private number : someOtherProp;
  computeSomething(x) {
    return;
  }
}```

Expected:
```class A extends B {
  private string : someProp;
  private number : someOtherProp;
  computeSomething(x) {
    return;
  }
}```

Opposite Way

Hi, I see this converts plantUML files to TypeScript (well, TypScript is what I care about right now ๐Ÿ‘ )

But I'm looking for something that will go the other way, taking a TypeScript class and converting it into PlantUML file...

Does dot accept numbers on field/method names

When I tried to compile with any number on the field or method names, it shows a message like this:

> plantcode $ ./plantcode -l java ../../project/classes.puml
Error parsing input file: 
SyntaxError: Expected [ \t], [(], [A-Za-z], [\n] or [\r\n] but "2" found.

This compiles:

@startuml
class Dummy {
    -String field
    #int field
    -int method()
    +void method()
}
@enduml

this too (note the number inside the type):

@startuml
class Dummy {
    -String field
    #Cli2ent field
    -int method()
    +void method()
}
@enduml

This doesn't compile:

@startuml
class Dummy {
    -String field1
    #int field2
    -int method1()
    +void method2()
}
@enduml

@edit
Underscore _ generates the same error
@EndEdit

Package is an unknown type

When using the package keyword in a UML, plantcode throws an error.

Example:
input:

@startuml
package test1 {
    class A {
        + c string
        - d string
    }
}

package test2 {
    class B {}
}

A --|> B
@enduml

output:

Error parsing input file: 
../../uml
Unknown type

This is a valid input according to plantuml but doesn't seem to be accepted by plantcode, although, when looking at the plantcode code, there seems to be support for the package keyword.

Class relations not fully supported

It appears some less commonly used class relationship syntax is not fully supported and returns an error of "Error parsing input file: ". I don't know node.js well but it appears the error object does not display a detailed error message about the problem that was thrown in the parser routine. Should more verbose error messaging be concatenated to the right of the colon? Onc example case is the use of -down-, -up-, -left-, and -right- in relationship text...like...

class Dwelling {
+Int Windows()
+void LockTheDoor()
}
class Apartment
Dwelling <|-down- Apartment: Inheritance

Support for C# language

It would be nice to write a template to support C#. Perhaps the language name should be "csharp" in order to avoid special characters in the name.

Implements is not supported

If a connection is between a class and an interface, the connection between them is compiled as an extension.

Example:

class A {
        + someProp string
        - someOtherProp number

        + number computeSomething(string x)
    }
interface B
A --|> B
@enduml```

Code:
```class A extends B {
  private string : someProp;
  private number : someOtherProp;
  computeSomething(x) {
    return;
  }
}

interface B {
}```

Expected:
```class A implements B {
  private string : someProp;
  private number : someOtherProp;
  computeSomething(x) {
    return;
  }
}

interface B {
}```

Class Properties are Not Converted

It looks like methods are converted to all other languages, but class properties are not. Example...

class Dwelling {
+Int Windows
+void Lock()
}

===CoffeeScript===

class Dwelling
Lock: ->

In this case only the Lock method is represented in the output. Not the Windows property.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.