Skip to content

Commit

Permalink
enh(haskell) Restucture Haskell number parsing
Browse files Browse the repository at this point in the history
Restructured Haskell's number parsing, taking a cue from Swift's
implementation. As a happy coincidence, this patch adds support for
Haskell's `HexFloatLiterals` language extension.
  • Loading branch information
martijnbastiaan committed Apr 19, 2021
1 parent c55ad76 commit 249ba7f
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Grammars:
- enh(javascript/typescript) highlight object properties (#3072) [Josh Goebel][]
- enh(haskell) add support for BinaryLiterals (#3150) [Martijn Bastiaan][]
- enh(haskell) add support for NumericUnderscores (#3150) [Martijn Bastiaan][]
- enh(haskell) add support for HexFloatLiterals (#3150) [Martijn Bastiaan][]

New Languages:

Expand Down
44 changes: 37 additions & 7 deletions src/languages/haskell.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,42 @@ export default function(hljs) {
contains: LIST.contains
};

/* See:
- https://www.haskell.org/onlinereport/lexemes.html
- https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/binary_literals.html
- https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/numeric_underscores.html
- https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/hex_float_literals.html
*/
const decimalDigits = '([0-9]_*)+';
const hexDigits = '([0-9a-gA-F]_*)+';
const binaryDigits = '([01]_*)+';
const octalDigits = '([0-7]_*)+';

const NUMBER = {
className: 'number',
relevance: 0,
variants: [
// decimal floating-point-literal (subsumes decimal-literal)
{
match: `(-?)\\b(${decimalDigits})(\\.(${decimalDigits}))?` + `([eE][+-]?(${decimalDigits}))?\\b`
},
// hexadecimal floating-point-literal (subsumes hexadecimal-literal)
{
match: `(-?)\\b0x_*(${hexDigits})(\\.(${hexDigits}))?` + `([pP][+-]?(${decimalDigits}))?\\b`
},
// octal-literal
{
match: `(-?)\\b0b(${octalDigits})\\b`
},
// binary-literal
{
match: `(-?)\\b0b(${binaryDigits})\\b`
}
]
};

return {
name: 'Haskell',
aliases: ['hs'],
Expand Down Expand Up @@ -154,13 +190,7 @@ export default function(hljs) {
PREPROCESSOR,

// Literals and names.
hljs.inherit(hljs.BINARY_NUMBER_MODE, {
begin: '\\b(0b[01_]+)'
}),

hljs.inherit(hljs.C_NUMBER_MODE, {
begin: '(-?)(\\b0[xX][a-fA-F0-9_]+|(\\b(\\d|_)+(\\.(\\d|_)*)?|\\.(\\d|_)+)([eE][-+]?(\\d|_)+)?)'
}),
NUMBER,

// TODO: characters.
hljs.QUOTE_STRING_MODE,
Expand Down
112 changes: 109 additions & 3 deletions test/markup/haskell/numbers.expect.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,109 @@
<span class="hljs-number">0b001_001</span>
<span class="hljs-number">0x000_0FF</span>
<span class="hljs-number">132_15</span>
<span class="hljs-comment">-- decimal</span>
<span class="hljs-title">million</span> = <span class="hljs-number">1_000_000</span>
<span class="hljs-title">billion</span> = <span class="hljs-number">1_000_000_000</span>
<span class="hljs-title">lightspeed</span> = <span class="hljs-number">299_792_458</span>
<span class="hljs-title">version</span> = <span class="hljs-number">8_04_1</span>
<span class="hljs-title">date</span> = <span class="hljs-number">2017_12_31</span>
<span class="hljs-title">the_answer</span> = <span class="hljs-number">42</span>

<span class="hljs-comment">-- hexadecimal</span>
<span class="hljs-title">red_mask</span> = <span class="hljs-number">0xff_00_00</span>
<span class="hljs-title">size1G</span> = <span class="hljs-number">0x3fff_fffg</span>

<span class="hljs-comment">-- binary</span>
<span class="hljs-title">bit8th</span> = <span class="hljs-number">0b01_0000_0000</span>
<span class="hljs-title">packbits</span> = <span class="hljs-number">0b1_11_01_0000_0_111</span>
<span class="hljs-title">bigbits</span> = <span class="hljs-number">0b1100_1011__1110_1111__0101_0011</span>

<span class="hljs-comment">-- float</span>
<span class="hljs-title">pi</span> = <span class="hljs-number">3.141_592_653_589_793</span>
<span class="hljs-title">faraday</span> = <span class="hljs-number">96_485.332_89</span>
<span class="hljs-title">avogadro</span> = <span class="hljs-number">6.022_140_857e+23</span>

<span class="hljs-title">x0</span> = <span class="hljs-number">1_000_000</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">x1</span> = <span class="hljs-number">1__000000</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">x2</span> = <span class="hljs-number">1000000_</span> <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">x3</span> = _1000000 <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">e0</span> = <span class="hljs-number">0.0001</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">e1</span> = <span class="hljs-number">0.000_1</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">e2</span> = <span class="hljs-number">0_.0001</span> <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">e3</span> = _0.<span class="hljs-number">0001</span> <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">e4</span> = <span class="hljs-number">0</span>._0001 <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">e5</span> = <span class="hljs-number">0.0001_</span> <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">f0</span> = <span class="hljs-number">1e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">f1</span> = <span class="hljs-number">1_e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">f2</span> = <span class="hljs-number">1__e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">f3</span> = 1e_+<span class="hljs-number">23</span> <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">g0</span> = <span class="hljs-number">1e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">g1</span> = 1e+_23 <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">g2</span> = <span class="hljs-number">1e+23_</span> <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">h0</span> = <span class="hljs-number">0xffff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h1</span> = <span class="hljs-number">0xff_ff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h2</span> = <span class="hljs-number">0x_ffff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h3</span> = <span class="hljs-number">0x__ffff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h4</span> = _0xffff <span class="hljs-comment">-- invalid</span>

<span class="hljs-comment">-- decimal</span>
<span class="hljs-title">million</span> = <span class="hljs-number">1_000_000</span>
<span class="hljs-title">billion</span> = <span class="hljs-number">1_000_000_000</span>
<span class="hljs-title">lightspeed</span> = <span class="hljs-number">299_792_458</span>
<span class="hljs-title">version</span> = <span class="hljs-number">8_04_1</span>
<span class="hljs-title">date</span> = <span class="hljs-number">2017_12_31</span>

<span class="hljs-comment">-- hexadecimal</span>
<span class="hljs-title">red_mask</span> = <span class="hljs-number">0xff_00_00</span>
<span class="hljs-title">size1G</span> = <span class="hljs-number">0x3fff_fffg</span>

<span class="hljs-comment">-- binary</span>
<span class="hljs-title">bit8th</span> = <span class="hljs-number">0b01_0000_0000</span>
<span class="hljs-title">packbits</span> = <span class="hljs-number">0b1_11_01_0000_0_111</span>
<span class="hljs-title">bigbits</span> = <span class="hljs-number">0b1100_1011__1110_1111__0101_0011</span>

<span class="hljs-comment">-- float</span>
<span class="hljs-title">pi</span> = <span class="hljs-number">3.141_592_653_589_793</span>
<span class="hljs-title">faraday</span> = <span class="hljs-number">96_485.332_89</span>
<span class="hljs-title">avogadro</span> = <span class="hljs-number">6.022_140_857e+23</span>

<span class="hljs-title">x0</span> = <span class="hljs-number">1_000_000</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">x1</span> = <span class="hljs-number">1__000000</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">x2</span> = <span class="hljs-number">1000000_</span> <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">x3</span> = _1000000 <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">e0</span> = <span class="hljs-number">0.0001</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">e1</span> = <span class="hljs-number">0.000_1</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">e2</span> = <span class="hljs-number">0_.0001</span> <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">e3</span> = _0.<span class="hljs-number">0001</span> <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">e4</span> = <span class="hljs-number">0</span>._0001 <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">e5</span> = <span class="hljs-number">0.0001_</span> <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">f0</span> = <span class="hljs-number">1e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">f1</span> = <span class="hljs-number">1_e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">f2</span> = <span class="hljs-number">1__e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">f3</span> = 1e_+<span class="hljs-number">23</span> <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">g0</span> = <span class="hljs-number">1e+23</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">g1</span> = 1e+_23 <span class="hljs-comment">-- invalid</span>
<span class="hljs-title">g2</span> = <span class="hljs-number">1e+23_</span> <span class="hljs-comment">-- invalid</span>

<span class="hljs-title">h0</span> = <span class="hljs-number">0xffff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h1</span> = <span class="hljs-number">0xff_ff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h2</span> = <span class="hljs-number">0x_ffff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h3</span> = <span class="hljs-number">0x__ffff</span> <span class="hljs-comment">-- valid</span>
<span class="hljs-title">h4</span> = _0xffff <span class="hljs-comment">-- invalid</span>

<span class="hljs-comment">-- hex float</span>
<span class="hljs-title">hf0</span> = <span class="hljs-number">0x0.1</span>
<span class="hljs-title">hf1</span> = <span class="hljs-number">0x0.01</span>
<span class="hljs-title">hf2</span> = <span class="hljs-number">0xF.FF</span>
<span class="hljs-title">hf3</span> = <span class="hljs-number">0x0.1p41</span>
<span class="hljs-title">hf4</span> = <span class="hljs-number">0x0.1p-4</span>
<span class="hljs-title">hf5</span> = <span class="hljs-number">0x0.1p12</span>

<span class="hljs-comment">-- negative numbers</span>
<span class="hljs-title">n0</span> = <span class="hljs-number">-1</span>
<span class="hljs-title">n0</span> = <span class="hljs-number">-1.0</span>
<span class="hljs-title">n0</span> = <span class="hljs-number">-0xFF0</span>
114 changes: 111 additions & 3 deletions test/markup/haskell/numbers.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,111 @@
0b001_001
0x000_0FF
132_15
-- decimal
million = 1_000_000
billion = 1_000_000_000
lightspeed = 299_792_458
version = 8_04_1
date = 2017_12_31
the_answer = 42

-- hexadecimal
red_mask = 0xff_00_00
size1G = 0x3fff_fffg

-- binary
bit8th = 0b01_0000_0000
packbits = 0b1_11_01_0000_0_111
bigbits = 0b1100_1011__1110_1111__0101_0011

-- float
pi = 3.141_592_653_589_793
faraday = 96_485.332_89
avogadro = 6.022_140_857e+23

x0 = 1_000_000 -- valid
x1 = 1__000000 -- valid
x2 = 1000000_ -- invalid
x3 = _1000000 -- invalid

e0 = 0.0001 -- valid
e1 = 0.000_1 -- valid
e2 = 0_.0001 -- invalid
e3 = _0.0001 -- invalid
e4 = 0._0001 -- invalid
e5 = 0.0001_ -- invalid

f0 = 1e+23 -- valid
f1 = 1_e+23 -- valid
f2 = 1__e+23 -- valid
f3 = 1e_+23 -- invalid

g0 = 1e+23 -- valid
g1 = 1e+_23 -- invalid
g2 = 1e+23_ -- invalid

h0 = 0xffff -- valid
h1 = 0xff_ff -- valid
h2 = 0x_ffff -- valid
h3 = 0x__ffff -- valid
h4 = _0xffff -- invalid

-- decimal
million = 1_000_000
billion = 1_000_000_000
lightspeed = 299_792_458
version = 8_04_1
date = 2017_12_31

-- hexadecimal
red_mask = 0xff_00_00
size1G = 0x3fff_fffg

-- binary
bit8th = 0b01_0000_0000
packbits = 0b1_11_01_0000_0_111
bigbits = 0b1100_1011__1110_1111__0101_0011

-- float
pi = 3.141_592_653_589_793
faraday = 96_485.332_89
avogadro = 6.022_140_857e+23

x0 = 1_000_000 -- valid
x1 = 1__000000 -- valid
x2 = 1000000_ -- invalid
x3 = _1000000 -- invalid

e0 = 0.0001 -- valid
e1 = 0.000_1 -- valid
e2 = 0_.0001 -- invalid
e3 = _0.0001 -- invalid
e4 = 0._0001 -- invalid
e5 = 0.0001_ -- invalid

f0 = 1e+23 -- valid
f1 = 1_e+23 -- valid
f2 = 1__e+23 -- valid
f3 = 1e_+23 -- invalid

g0 = 1e+23 -- valid
g1 = 1e+_23 -- invalid
g2 = 1e+23_ -- invalid

h0 = 0xffff -- valid
h1 = 0xff_ff -- valid
h2 = 0x_ffff -- valid
h3 = 0x__ffff -- valid
h4 = _0xffff -- invalid

-- hex float
hf0 = 0x0.1
hf1 = 0x0.01
hf2 = 0xF.FF
hf3 = 0x0.1p41
hf4 = 0x0.1p-4
hf5 = 0x0.1p12

-- negative numbers
n0 = -1
n0 = -1.0
n0 = -0xFF0

a - b

0 comments on commit 249ba7f

Please sign in to comment.