0

I've got quite a lot of code in which sometimes if-statements have been written, without a matching else-statement. In some places, this is an issue, because when something fails, the failure is not handled correctly. I want to manually confirm all places an if-without-an-else occurs.

I've tried using regex, but I haven't succeeded in finding a way to match the right bracket, since regex cannot really handle nested brackets.

The code I want to find looks like this:

if( ... ) {

...

}

The code I don't want to find, looks like this:

if( ... ) {

...

} 
else {

... 

}

How can I list all these places, so I can go through them?

  • I've asked the same question here: https://superuser.com/questions/1660815/how-can-i-find-all-if-statements-without-an-else But I'm not sure where the right place to ask this is, as it is very general. – John Stoneman Jul 05 '21 at 13:41
  • Do you use some sort if IDE? If not, you should consider to use one ! It gives you highlighting and marks missing else or other wrong synthax. An IDE is like Word, but for programming languages! . What language do you use ? – Silvan Bregy Jul 05 '21 at 14:27
  • Missing an `else` is not really wrong syntax. I use Visual Studio Code for syntax highlighting. – John Stoneman Jul 07 '21 at 07:37
  • Yes, it's not wrong syntax. You are right. I provided you a small script which works as a one time solution for your specific case but it's a bad idea to do it with regex. But it works if some conditions are fullfilled and if you have full control over the text you are parsing! I hope it helps. – Silvan Bregy Jul 08 '21 at 11:45

1 Answers1

1

As you noticed by yourself, generally this has to be done with some sort of parser. With regex it's simply just pain and there are a lot of parser / grammar modules out there.

I created a one time solution script (in nodejs), which uses regex and the number of indents for matching starting to closing if / else statements. It works if following conditions are fullfilled:

  • The file is properly formatted, a starting if has the same numbers of indents as the closing else

this works:

if(a) {
   if(b) {
      
   } else { // i have same amount of indents as my matching if, works!
     
   }
}

this does NOT work:

if(a) {
    if(b) {
        } else { // i have to many indents.
           
        }
  
}

  • There cannot be if and else on the same line ! (should not be ususally..)

Here's the script i came up with, it stores all line numbers with an if which does not have an else. U can check line numbers and click on a given if, then vs code highlights the ending parenthesis and you can add the else. It uses lodash but only for flattening results in the end. It's not truely dependent..

import { EOL } from 'os'
import * as fs from 'fs'
import { flatten } from 'lodash'

// Import your code file:
const code = fs.readFileSync('code.js').toString()

// Regex for matching lines holding if's..
// if(...)
// if() {
// if
const match_if = /if\s?\([^)]+\)\s+?/

// Regex for matching lines holding else:
// }else
// } else
// else {
// }else{
// ...
const match_else = /(\n|})\s?else({|\s|$)/


// small helper fpr calculating indents for first character appearing..
const calculate_indent = (line: string) => {
    let spaces = 0
    while(line.charAt(spaces) === ' ') {
        spaces++
    }
    return spaces
}

// split up the lines by system operator
const lines = code.split(EOL)
const indexes = []

// iterate all lines.
for(let i =0; i < lines.length; i++) {
    const line = lines[i]

    // do not process empty lines..
    if(!line || line.trim().length < 2) {
        continue
    }


    // Match the line against if regex
    const if_match = line.match(match_if)

    // process the match if there is an if found!
    if(if_match && if_match.length > 0) {

        // Count the indents, we need this latere
        // to match any found "else" to the correct if.
        // make sure your code is properly formatted and indent levels are correct!!
        const level = calculate_indent(line)


        // Make sure under given level is an array
        if(Array.isArray(indexes[level]) === false) {
            indexes[level] = []
        }

        // store line number to the given level. 
        // For a human the line number is index + 1
        indexes[level].push(i+1)
    }


    // We check the line for else. Note:
    // The whole function won't work for any if() else combinations on the same line..
    const else_match = line.match(match_else)
    if(else_match && else_match.length > 0) {
        
    
        // count index to identify level for matching the correct if.
        const level = calculate_indent(line)

        if(Array.isArray(indexes[level])) {
            // remove the last stored if. We know it ended here!
            indexes[level].splice(indexes[level].length-1, 1)
        } 

    }

}

console.log('lines:', lines.length)
console.log('results:', indexes)

// flatten / remove empty items / sort
const line_numbers = flatten(indexes).filter(d => !!d).sort((a,b) => a-b)

console.log('final lines:', line_numbers)

I tested it with this code:

if(sdf) {

    if(sdf) {
            
    }
}



if(asdf) {

    if(sdfsdf) {


        if(sdfsdf) {

        } else {

        }
    } else {

    }
} else {
    
}


if(asdf) {

    if(sdfsdf) {

    } else {
        if(sdf) {

        }

        if(sdf) {
            
        } else {

        }


        if(sdf) {

        }
    }
}

Results => final lines: [ 2, 4, 29, 34, 45 ] You may wanna test a little bit more before using it ! But it should work when the indents are correct. This article is also related and nice to read! => why you should not use regex to parse tree structures....

Silvan Bregy
  • 2,544
  • 1
  • 8
  • 21