Eliminación de llaves opcionales en el código

¿Hay alguna herramienta disponible que elimine las llaves opcionales del código escrito en lenguajes de programación con llaves?

Sin iniciar una guerra de llamas, me gustaría seguir el estilo K&R, donde los frenos solo aparecen cuando son necesarios. Estilo artístico (astyle) me permite eliminar automáticamente las llaves de las declaraciones de una sola línea, por lo que

if (something) {
    do_something();
}

se convierte

if (something)
    do_something();

usando el comando --remove-brackets (-xj). Sin embargo, un estilo no puede manejar el caso de

for (int i = 0; i < 5; i++) {
    if (x == y) {
        q();
    } else if (x > y) {
        w();
    } else {
        r();
    }
}

y lo convierte en

for (int i = 0; i < 5; i++) {
    if (x == y)
        q();
    else if (x > y)
        w();
    else
        r();
}

donde las llaves del bucle for no se eliminan, aunque pueden eliminarse.

Parece que clang-format tiene configuraciones de BraceWrapping, pero establecer todo esto en falseno parece tener ningún efecto cuando ejecuto clang-format en el archivo.

Editar: el CDT de Eclipse pudo hacer esto y estoy buscando una herramienta que no requiera la instalación de Eclipse.

Si te pones eso, te arrepentirás. Tal vez no hoy, tal vez no mañana, pero pronto y por el resto de tu vida (con disculpas a Bogie)

Respuestas (1)

Nuestro kit de herramientas de reingeniería de software DMS puede hacer esto.

DMS es un sistema de transformación de programas que analiza el texto fuente en estructuras de datos del compilador, aplica transformaciones en esas estructuras de datos para producir esencialmente un programa diferente y luego puede regenerar el código fuente para el programa modificado a partir de esas estructuras de datos. DMS utiliza front -end con precisión de compilador para realizar el análisis (y otros análisis apropiados para el lenguaje). DMS tiene interfaces completas y maduras para C y C++.

Las transformaciones DMS generalmente se escriben como fuente a transformación, ese significado esencial

if you see *this_pattern*, replace it by *that_pattern*

escrito en este formato:

rule rulename( named_syntax_parameters ) : syntax_category -> syntax_category =
   " surface-syntax_pattern " -> " surface-syntax-replacement-pattern ";

Las comillas no son comillas de cadena, sino meta-comillas ; distinguen la sintaxis necesaria para expresar las reglas de la sintaxis del lenguaje de programación que desea manipular. Para una descripción más precisa, consulte el enlace.

Para el problema de OP, en unos 10 minutos definí un conjunto de reglas de reescritura de DMS para el dialecto GCC4 de C:

default base domain C~GCC4;

rule remove_useless_bracket_anywhere(s: statement): statement -> statement
  = "  { \s } " ->  " \s " ;

public ruleset remove_useless_brackets_all = {
  remove_useless_bracket_anywhere 
};

rule remove_useless_brackets_for1( fisc: for_init_statement, c: condition,
               e:expression, s: statement): statement -> statement
  = "  for ( \fisc \c ; \e ) { \s } " ->  " for ( \fisc ; \e ) \s " ;

public ruleset remove_useless_brackets_for = {
  remove_useless_brackets_for1
};

pattern not_single_statement_block(s: statement): statement
  = s if ~[s2:statement. s <: "\:compound_statement { \s2 }"];

rule remove_useless_bracket_while( e: expression, s: statement): statement -> statement
  = "  while (\e) { \s } " ->  " while (\e) \s " ;

rule remove_useless_bracket_if_then( e: expression, s: statement): statement -> statement
  = "  if (\e) { \s } " ->  " if (\e) \s " ;

rule remove_useless_bracket_if_then_else( e: expression, s1: statement, s2: statement ): statement -> statement
  = "  if (\e) { \s1 } else { \s2 } " ->  " if (\e) \s1 else \s2 " ;

rule remove_useless_bracket_if_then_statement_else_compound_statement( e: expression,
    s: statement, nssb: statement ): statement -> statement
  = "  if (\e) { \s } else \not_single_statement_block\(\nssb\) " ->  " if (\e) \s else \nssb " ;

rule remove_useless_bracket_if_then_compound_statement_else_statement( e: expression,
    s: statement, nssb: statement ): statement -> statement
  = "  if (\e) \not_single_statement_block\(\nssb\) else { \s } " ->  " if (\e) \nssb else \s " ;

public ruleset remove_useless_brackets_not_for = {
   remove_useless_bracket_while,
   remove_useless_bracket_if_then,
   remove_useless_bracket_if_then_else,
   remove_useless_bracket_if_then_statement_else_compound_statement,
   remove_useless_bracket_if_then_compound_statement_else_statement
};

Puede ver reglas individuales que expresan la sintaxis de la superficie C con parámetros con nombre n :" en las listas de parámetros de regla y con el nombre \n incrustado dentro de las metacitas de los patrones.

Cada regla individual maneja la transformación de una construcción de control C; hay unos para sentencias for, bucles while y sentencias if. El motivo de las múltiples reglas de declaraciones if es manejar las combinaciones. Los dividí de esta manera porque OP quiere eliminar {...} de todas las construcciones excepto las declaraciones.

Hay conjuntos de reglas que recopilan un conjunto de reglas; eso hace que sea fácil invocar un conjunto de reglas para lograr un propósito.

Algunas reglas utilizan la construcción de patrón denominada not_single_statement_block para detectar bloques que no contienen declaraciones únicas.

Por ejemplo, convertí el fragmento de código de OP en un programa completo de una manera trivial:

void main() {

for (int i = 0; i < 5; i++) {
    if (x == y) {
        q();
    } else if (x > y) {
        w();
    } else {
        r();
    }
    }

}

Si ejecutamos una herramienta de transformador C generada por DMS en este ejemplo utilizando el conjunto de reglas más general para quitar llaves remove_useless_brackets_all , obtenemos:

C:\DMS\Domains\C\GCC4\Tools\RuleApplier\Source>run ..\DomainRuleApplier.P0B C:\temp\brackets.c Tools/RuleApplier/eliminate_useless_b
rackets/remove_useless_brackets_all
Domain RuleApplier 1.5.0
Parsing "C:\temp\brackets.c"
Done Parsing file
Registry: Loading RSL definitions from "C:/DMS/Domains/C/GCC4/Tools/RuleApplier/eliminate_useless_brackets.rsl" ...
Registry: Successfully loaded RSL definitions from "C:/DMS/Domains/C/GCC4/Tools/RuleApplier/eliminate_useless_brackets.rsl".

void main() {

for (int i = 0
 ;          i < 5; i++)
if (x == y)
    q();
else   if (x > y)
    w();
       else
    r();
    }

Puede ver que esto elimina todo el bloque de declaración única, incluido el de la declaración for. OP no quería eso.

Entonces ejecutamos en su lugar usando el conjunto de reglas remove_useless_brackets_not_for , y obtenemos:

C:\DMS\Domains\C\GCC4\Tools\RuleApplier\Source>run ..\DomainRuleApplier.P0B C:\temp\brackets.c Tools/RuleApplier/eliminate_useless_brackets/remove_useless_brackets_not_for
Domain RuleApplier 1.5.0
Parsing "C:\temp\brackets.c"
Done Parsing file
Registry: Loading RSL definitions from "C:/DMS/Domains/C/GCC4/Tools/RuleApplier/eliminate_useless_brackets.rsl" ...
Registry: Successfully loaded RSL definitions from "C:/DMS/Domains/C/GCC4/Tools/RuleApplier/eliminate_useless_brackets.rsl".

void main() {

for (int i = 0
 ;          i < 5; i++) {
if (x == y)
    q();
else   if (x > y)
    w();
       else
    r();
            }
    }

Esto es lo que OP muestra en su ejemplo como el resultado deseado.

El formato de salida para estos resultados ciertamente parece divertido. Eso es porque la herramienta de juguete que construí para hacer esto es "prettyprinting" en modo "preservar"; está tratando de preservar las columnas originales donde puede; donde no puede conservar un número de columna, inserta un salto de línea o agrega espacios para llegar a la siguiente columna de destino del token. En un programa grande con una pequeña cantidad de transformaciones realmente aplicadas, esto es lo que desea, si desea que el programa luzca prácticamente intacto. O bien, puede configurar DMS para que imprima el texto e ignore los números de columna originales; entonces todo se alinea muy bien, pero ese ejemplo no está aquí.

OK, entonces con respecto a la solicitud de OP:

  • DMS es una herramienta que eliminará las llaves opcionales del código escrito en lenguajes de programación de llaves usando reglas de reescritura
  • Puede manejar muchos idiomas (específicamente a las etiquetas explícitas de OP, C y C++)
  • Se ejecuta bajo Windows (o Linux con Wine)
  • No requiere la instalación de Eclipse (con respecto a la intención original de OP, DMS es un sistema bastante grande; en su lugar, debe instalarlo y, en mi humilde opinión, probablemente no valga la pena para este problema específico)
  • Comercialmente disponible

Dado que esta es mi herramienta, no lo tome como una "recomendación". Me limito a informar de su existencia.

Gracias. Esto fue muy útil. Gracias por la larga y muy detallada respuesta.
Ira, no puedo encontrar el precio en el sitio, ¿me lo perdí?
@Mawg: No, no lo hiciste. DMS es un producto empresarial. El precio varía según los componentes que adquiera y los modelos de implementación. Comuníquese con el sitio para solicitar detalles.