GithubHelp home page GithubHelp logo

mofmt's Introduction

mofmt - Modelica code formatter

mofmt is a code formatter for Modelica language. It aims to enhance readability and provide style constistency across different Modelica projects.

Code style applied by the mofmt is described in code-style.md.

mofmt assumes that files are encoded as UTF-8 (without BOM). Other encoding will cause it to panic.

Installation and usage

Installation

mofmt can be installed with cargo:

cargo install mofmt

or you can just grab one of the released libraries.

On top of that, repo contains a necessary pre-commit-hooks.yaml file, so if you are using Git, you can delegate running mofmt to pre-commit framework.

Usage

mofmt expects one or more paths that point to either Modelica source files, or to directories with such files:

mofmt <PATHS>

So you can format a single file:

mofmt foo.mo

or multiple files:

mofmt foo.mo bar.mo baz.mo

or all files inside the directory (mofmt searches for files recursively):

mofmt ./foo-dir

or all files inside multiple directories

mofmt ./foo-dir ./bar-dir

or you can mix both files and directories:

mofmt ./foo-dir foo.mo bar.mo ./bar-dir baz.mo

mofmt can run in check mode. In this mode files are not modified, instead mofmt checks the formatting in the original file, and reports an error in case of a failure:

mofmt --check <PATHS>

TODO

[ ] include HTML pretty-printer

License

MPL-2.0

Authors

Eryk Mroczek: [email protected]

mofmt's People

Contributors

erykmroczek avatar m-kessler avatar

Stargazers

 avatar  avatar Krzysztof Dymanowski avatar Tomasz Wojdat avatar  avatar Srikanth Sivaramakrishnan avatar  avatar  avatar

Watchers

 avatar

Forkers

modelica-tools

mofmt's Issues

Wrong indent for annotation in otherwise empty model

Similar to #31, annotation indent is too high if the model only contains an annotation (like for documentation classes or extendable icons).

This code for example:

partial model Example
  "Icon for runnable examples"
  annotation (Icon(graphics = {Ellipse(extent = {{-100, -100}, {100, 100}})}));
end Example;

Produces:

partial model Example
  "Icon for runnable examples"
    annotation (Icon(graphics = {Ellipse(extent = {{-100, -100}, {100, 100}})}));

end Example;

The error does not occur when the model comment is deleted or if variable declarations are added.

Tested with mofmt 0.5.0 and 0.4.2.

Missing support for Q-IDENTs with whitespaces and double quotes

Modelica allows to have spaces and double quotes in Q-IDENTS (see https://specification.modelica.org/maint/3.6/lexical-structure.html#identifiers for details).

However, mofmt cannot format code when it contains such Q-IDENTS:

model 'foo bar'
end 'foo bar';

block '"foo"'
end '"foo"';

Looking at Q-CHAR in ModelicaLexer.g4 it seems like ' ' is missing, same for '"'.

Is this an error or a known limitation of mofmt?

Both cases are very rare, so I think it would be enough to mention in the docs that this is not supported (unless it's easy to fix).

No information on which file causes parser to panic

Before switching to Rust parser, formatter printed out path of the file that could not be formatted properly (due to e.g. incorrect syntax). Now it is not printed making it hard to find the problematic file when formatting directories.

Dropping function return values not supported anymore

mofmt v0.3.6 supported dropping function return values.

In this exemplary code we ignore the first output ms of getTime:

function time
  output Integer sec;
  output Integer min;
  output Integer hour;
algorithm 
    (, sec, min, hour) := Modelica.Utilities.System.getTime();
end time;

The code can be formatted with mofmt 0.3.6. However, mofmt 0.5.0 says:

Syntax errors detected:
time.mo:6:6: expected IDENTIFIER, found ','

Support general subscripting which will be introduced in MLS 3.7

Even though it was not defined in the Modelica Language Specification (MLS), Dymola allows to do the following since many years:

  Real smallest = (Modelica.Math.Vectors.sort({4, 2, 5, 1}))[1];

We only need the first element of the vector returned by sort, so this syntax allows to access it without defining an intermediate variable.

As it is no valid Modelica code today, mofmt does not understand this (neither does OpenModelica in its current version 1.22.1).

However, the syntax will make it to the next MLS version 3.7: General subscripting.
Moreover, multiple libraries are already using this, according to comments in the related issue Subscripting of general expressions

Therefore, I suggest to support it sooner or later.

Indentation of if-expressions

mofmt currently formats the code below as follows:

function sign

  input Real u;
  output Real y;

algorithm

  y :=
  if i == 0 then
    0
  elseif i >= 0 then
    1
  else
    -1;

end sign;

In my opinion the if-expression should be indented by one level:

  y :=
    if i == 0 then
      0
    elseif i >= 0 then
      1
    else
      -1;

UTF8 files with BOM are not understood

Older Dymola versions had the bad habit to store in modelica code in UTF8 encoding with BOM.

If such a file is formatted, mofmt crashes with:

thread 'main' panicked at C:\Users\ME\.cargo\registry\src\index.crates.io-6f17d22bba15001f\moparse-0.1.4\src\lexing.rs:349:38:
byte index 1 is not a char boundary; it is inside '\u{feff}' (bytes 0..3) of `within MyLibrary.UsersGuide;
...

To reproduce, add a BOM to an UTF8-encoded .mo-file, e.g. with

sed -i '1s/^/\xef\xbb\xbf/' myfile.mo

then run mofmt on it.

Meanwhile Dymola does not store with BOM any more. Therefore, I think a note in the mofmt doc would be enough that only UTF8 encoded files without BOM are supported.

Internal error for `redeclare replaceable`

mofmt 0.4.1 does not support redeclare replaceable.

Here is a minimum example:

model M
  extends X(
    redeclare replaceable Sine x);
end M;

mofmt crashes with this message:

thread 'main' panicked at C:\Users\ME\.cargo\registry\src\index.crates.io-6f17d22bba15001f\mofmt-0.4.1\src\formatting.rs:1025:22:
internal error: entered unreachable code
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

When the keyword replaceable is removed, the code can be formatted.

A more complex real-world example is e.g. Modelica.Fluid.Valves.ValveVaporizing.

Whitespaces are added to line end when global paths are used

Fixing #3 resulted in a regression: Now whitespaces are added to the statement in the line before lines with global paths.

Format this with mofmt v0.3.2:

block Foo
  import Modelica.Units.SI;
  .Modelica.Blocks.Interfaces.BooleanInput b;
end Foo;

A single whitespace character is added to the line with the import statement:

⋅⋅⋅⋅import⋅Modelica.Units.SI;⋅

(Using ⋅ to make whitespaces visible).

Newline added and wrong indent for array rows with functions

If functions are used in array definitions, newlines with spaces are added to every row of the array.
Moreover, every item of the array is indented with 2 spaces too much.

The issue can be reproduced by running this code through mofmt:

model f_in_array
  import Modelica.Math;
  Real v1[:] = {1, 2};
  Real v2[:] = {2, 3};
  Real m[:,:] = Math.Matrices.sort(
    [
      Math.Vectors.length(v1),
      Math.Vectors.length(v2);
      Math.Vectors.length(v1 + v2),
      Math.Vectors.length(v2 - v1)
    ]);
end f_in_array;

Which gives for the matrix m:

  Real m[:,:] = Math.Matrices.sort(
    [
      
        Math.Vectors.length(v1),
        Math.Vectors.length(v2);
      
        Math.Vectors.length(v1 + v2),
        Math.Vectors.length(v2 - v1)]);

Ignore git submodules

Running mofmt . is a very convenient way to format all .mo files in a folder.

For git repositories one usually likes to exclude submodules.
Therefore it would be nice if mofmt has a option to skip all submodules (maybe even active by default).

Extra whitespace added to arrays as function arguments

Since mofmt v0.3.3 unnecessary whitespaces are introduced when arrays are passed to functions:

  String A = toString([2.12, -4.34; -2.56, -1.67]);

This gives in v0.3.3 and v0.3.4:

  String A = toString( [2.12, -4.34; -2.56, -1.67]);

Note the extra space before the array.

Single-line comment followed by empty line and multiline comment results in erroneous output

Consider the below model:

model TestComments

  // Single-line comment

  /*
  Multi-line comment 1
  Multi-line comment 2
  */

end TestComments;

If formatter is run on such file, the output has incorrect syntax:

model TestComments

// Single-line comment/*
  Multi-line comment 1
  Multi-line comment 2
  */
end TestComments;

Suprisingly, is blank line between comments is removed, the output has correct syntax, although the result is not desirable, as indentation of lines 3 and 4 is removed:

model TestComments

// Single-line comment
/*
  Multi-line comment 1
  Multi-line comment 2
  */
end TestComments;

Regression: Mixing positional and named arguments not understood anymore

Since v0.3.3 the following code is not understood anymore:

function firstChar
  input String s = "foo.bar";
  output String c = Modelica.Utilities.Strings.substring(s, startIndex=1, endIndex=1);
algorithm 
end firstChar;

It aborts with:

firstChar.mo:3:70: mismatched input '=' expecting ')'
firstChar.mo:3:84: extraneous input ')' expecting ';'
errors met while parsing firstChar.mo. Formatter will not modify it

The reason is the mixing of positional and named arguments in the function call.

The following two variants work:

  // only named args
  output String c = Modelica.Utilities.Strings.substring(string=s, startIndex=1, endIndex=1);
  // only positional args
  output String c = Modelica.Utilities.Strings.substring(s, 1, 1);

`mofmt` won't work with `pre-commit` script when multiple files are selected

pre-commit script invokes mofmt.exe passing list of files as an argument to the hook. Since mofmt accepts only single argument, when multiple files are selected by pre-commit script (e.g. when multiple files are commited) it results in an error:

mofmt....................................................................Failed
- hook id: mofmt
- duration: 0.19s
- exit code: 1

mofmt takes only one argument (file/directory path)

First run does not fully format choices and dymola commands

I found two cases with similar syntax where running mofmt a second time produces new output:

  1. Choices annotations
  2. Custom dymola commands

Format this code with mofmt v0.3.5:

model X
  parameter Integer control = 0 annotation (choices(choice = 0 "Foo", choice = 1 "Bar"));
  annotation (__Dymola_Commands(file = "modelica://MyLib/Resources/plot.mos" "plot"));
end X;

First run gives:

model X

  parameter Integer control = 0
    annotation (choices(choice = 0
      "Foo", choice = 1
      "Bar"));

  annotation (__Dymola_Commands(file = "modelica://MyLib/Resources/plot.mos"
    "plot"));

end X;

Second run:

model X

  parameter Integer control = 0
    annotation (
      choices(
        choice = 0
          "Foo",
        choice = 1
          "Bar"));

  annotation (
    __Dymola_Commands(
      file = "modelica://MyLib/Resources/plot.mos"
        "plot"));

end X;

Running it a third time does not give a new result.

Missing support for new break features of MLS 3.6

Modelica Language Specificaton 3.6 introduced two new features involving the existing break keyword, which are not formatted correct by mofmt:

  1. Removing modifiers
  2. Selective model extension

Here is an example using those new features:

partial model ConditionalExtends
  extends Modelica.Blocks.Examples.PID_Controller(
    break loadTorque,
    inertia1(J=break),
    break connect(speedSensor.w, PI.u_m));
end ConditionalExtends;

Running it through mofmt 0.3.1 gives no errors, but the output is not satisfying:

partial model ConditionalExtends

  extends Modelica.Blocks.Examples.PID_Controller(break loadTorque,
    inertia1(J = break), break connect(speedSensor.w, PI.u_m));

end ConditionalExtends;

Wrong indent after joining strings in function calls

Try formatting the code below, where a long string passed to a function is joined with +:

function f1
algorithm
  Modelica.Utilities.Streams.print(
    "fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
      + "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar"
      + "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz");
  annotation (Documentation(info = "<html>Fooo</html>"));
end f1;

In mofmt v0.3.3 and v0.3.4 this gives:

function f1

algorithm

  Modelica.Utilities.Streams.print(
    "fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
      + "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar"
        + "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz");

    annotation (Documentation(info = "<html>Fooo</html>"));

  end f1;

Note that everything after the first + is indented by 1 level too much.

With v0.3.2 it works as expected.

Line endings are not preserved on Windows

mofmt 0.4.1 stores all changed files with LF line endings, also for files using Windows-style CRLF.

To reproduce, perform the following steps (e.g. with Git Bash on Windows):

$ printf "model M\r\nextends X;\r\nend M;" > M.mo
$ file M.mo
M.mo: ASCII text, with CRLF line terminators
$ mofmt M.mo
$ file M.mo
M.mo: ASCII text

Moreover, line endings in HTML code are not changed, which results in mixed line endings on Windows.
A simple test is to store this code as Doc.mo with CRLF:

model Doc
    annotation (
      Documentation(
        info = "<html>
Foo
Bar
</html>"));
end Doc;

Then run it through mofmt.
cat -e shows us that the updated file uses LF for the lines with regular Modelica code and CRLF in the HTML section:

$ cat -e Doc.mo
model Doc$
    annotation ($
      Documentation($
        info = "<html>^M$
Foo^M$
Bar^M$
</html>"));$
$
end Doc;$

Add --version argument

Currently there is no quick way to get the current version of mofmt on the command line.

With mofmt --version, mofmt tries to format a file named --version.

Whitespaces in front of global paths are removed

If global class paths are used, the whitespace in front of the path is removed.

Running mofmt on this:

model AbsolutePaths
  extends .Modelica.Fluid.Examples.NonCircularPipes(
    redeclare package Medium = .Modelica.Media.Water.StandardWaterOnePhase);
end AbsolutePaths;

Gives:

...
  extends.Modelica.Fluid.Examples.NonCircularPipes(
    redeclare package Medium =.Modelica.Media.Water.StandardWaterOnePhase);
...

Note the missing whitespaces before both global paths starting with .Modelica.

This is no issue in OpenModelica, but Dymola does not like the missing whitespace between extends and the class path and automatically reformats the code with a newline:

  extends 
         .Modelica.Fluid.Examples.NonCircularPipes(
    redeclare package Medium =.Modelica.Media.Water.StandardWaterOnePhase);

Unnecessary whitespace for selective model extension

The selective model extension, which mofmt supports since #6, is not formatted correct.

This code:

model NewPlant
  extends System(
    break controller,
    break reference);
end NewPlant;

Is formatted as:

model NewPlant

  extends System(
    break controller ,
    break reference );

end NewPlant;

Note the extra white spaces at the end of each break statement.

Bad output for arrays with line breaks in rows

mofmt usually formats arrays nicely.
However, it has problems with line breaks in row definitions, which is especially a problem for Dymola users.

Here we see what Dymola generates when you define a matrix of size 2x16 via the GUI (with the default maximum line length of 80):

model array
  parameter Real a[:,:]=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1; 2,2,2,2,2,2,2,2,2,2,2,
      2,2,2,2,2];
end array;

Running the code through mofmt gives:

model array

  parameter Real a[:,:] = [
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1;

      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2,
      2];

end array;

Wrong indent for annotation in otherwise empty sections

With mofmt 0.4.1 annotations of local class definitions in empty sections (equation, algorithm, protected) are indented one level too much:

Exemplary input:

model M
protected
  model X
  equation
    annotation (revisionId = "c04e23a0d");
  end X;
end M;

Output:

model M

protected

  model X

  equation
      annotation (revisionId = "c04e23a0d");

  end X;

end M;

Wrong indentation for Boolean expressions with newlines

Similar to #19, the indentation is broken when a Boolean expression contains newlines.
For every newline the indent is increased by one level .

This is what we get since v0.3.3:

function foo

  input Real u1, u2, u3, u4;
  output Boolean y;

algorithm

  y := u1 > 0
    and u2 > 0
      and u3 > 0
        and u4 > 0;

    end foo;
    

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.