0

I have a strange behavior on a switch statement that is supposed to check for possible values of a string.

I'm trying to develop a little parser and using TDD I managed to write (and test) a function that parses a single line at a time, resulting in the expected result for all my cases.

Now I'm developing a bigger function that parses a bunch of lines at once so what it does is essentially split these lines and call the function that parses one line at a time.

The strange behaviour happens when I'm checking a value:

parseLine(terrainLine: string): Terrain | Tile[] | Adventurer {
    const [lineType, ...lineData] = terrainLine.trim().split(' - ');

    switch (lineType) {
      case 'C':
        return Terrain.parseTerrain(lineData);

      case 'T':
        return Terrain.parseTreasures(lineData);

      case 'M':
        return [Terrain.parseMountain(lineData)];

      case 'A':
        return Terrain.parseAdventurer(lineData);

      default: {
        throw new TerrainError(
          `Unable to parse terrain tile with data: "${terrainLine}"`,
        );
      }
    }
  }

This function is tested and should be working properly with strings like 'C - 3 - 4' (this input was tested and passed) but when the following function makes a call, it does not work anymore and instead it triggers the default statement:

parse(terrainString: stirng): Terrain {
  const linesToParse = terrainString
      .split('\n')
      .map((_) => _.trim()) // Get rid of spaces before and after interesting data
      .filter((_) => _.length && !_.startsWith('#')); // Get rid of empty lines && comments lines

  linesToParse.forEach((line) => {
      const parsed = Terrain.parseLine(line);
      // [...]
  }  

  // [...]
}

For reference, here are the tests I use:

// This one passes
it('should parse terrain lines right', () => {
  const terrainLine = 'C - 3 - 4';
  const expectedTerrain = new Terrain(3, 4);
  const parsed = parseLine(terrainLine);

  expect(parsed).toBeInstanceOf(Terrain);
  expect(parsed).toStrictEqual(expectedTerrain);
});

// This one doesn't
it('should accept valid terrains', () => {
  const terrainString = 'C​ - 3 - 4\nM​ - 1 - 0\nM​ - 2 - 1\nT​ - 0 - 3 - 2\nT​ - 1 - 3 - 3\nA​ - Lara - 1 - 1 - S - AADADAGGA\n';

  expect(() => {
    Terrain.parse(terrainString);
  }).not.toThrow();
});
AmiralBl3ndic
  • 406
  • 5
  • 13
  • I tried debugging it and I got a really strange behaviour where `lineType` (from the first code snippet) is `'C'` and it still goes to the `default` statement. Moreover, I tried `lineType === 'C'` and `lineType == 'C'` which both gave me `false` – AmiralBl3ndic Oct 16 '20 at 10:33
  • 1
    Are you sure the string is `"C"` and not `"C "` with an extra space for example? Is that a capital Latin "C" character, char code 67 or is it a different character? – VLAZ Oct 16 '20 at 10:36
  • 1
    It seems that you are right: the charcodes are matching but for some reason `lineType` has length `2` and `lineType.charAt(1)` gives me `8203`. I don't know why though – AmiralBl3ndic Oct 16 '20 at 10:41
  • 1
    You probably have some invisible characters in your string. – VLAZ Oct 16 '20 at 10:42
  • Yes. Thanks for your help – AmiralBl3ndic Oct 16 '20 at 10:43
  • 3
    Oh...you actually have [the most evil character](https://stackoverflow.com/questions/2973698/whats-html-character-code-8203) - it's a zero width space. It's a *space* but it literally cannot be seen at all. It has no width when printed. It's still a character in the string, however. It's a VERY annoying one, as it bypasses some of the withespace checks and it's incredibly hard to find normally. – VLAZ Oct 16 '20 at 10:46

1 Answers1

0

As pointed out by @VLAZ, I had an invisible character of zero width when printed in my string which was causing this bug. Simply removing this character in the first place solved the problem.

AmiralBl3ndic
  • 406
  • 5
  • 13