From 136f222a98446ddb8d091c2da16ff24475c03135 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 18 Jan 2026 22:17:08 +0900 Subject: [PATCH 1/7] support lshift, rshift --- autoload/vimlparser.vim | 57 ++++++++++++++++++++++++++++++++++--- js/vimlparser.js | 63 ++++++++++++++++++++++++++++++++++++++--- py/vimlparser.py | 52 +++++++++++++++++++++++++++++++--- test/test_bitshift.ok | 6 ++++ test/test_bitshift.vim | 6 ++++ 5 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 test/test_bitshift.ok create mode 100644 test/test_bitshift.vim diff --git a/autoload/vimlparser.vim b/autoload/vimlparser.vim index 25db0a4..b4dad04 100644 --- a/autoload/vimlparser.vim +++ b/autoload/vimlparser.vim @@ -121,6 +121,8 @@ let s:NODE_REMAINDER = 72 let s:NODE_NOT = 73 let s:NODE_MINUS = 74 let s:NODE_PLUS = 75 +let s:NODE_LSHIFT = 99 +let s:NODE_RSHIFT = 100 let s:NODE_SUBSCRIPT = 76 let s:NODE_SLICE = 77 let s:NODE_CALL = 78 @@ -213,6 +215,8 @@ let s:TOKEN_BLOB = 66 let s:TOKEN_LITCOPEN = 67 let s:TOKEN_DOTDOT = 68 let s:TOKEN_HEREDOC = 69 +let s:TOKEN_LSHIFT = 70 +let s:TOKEN_RSHIFT = 71 let s:MAX_FUNC_ARGS = 20 @@ -3525,6 +3529,9 @@ function! s:ExprTokenizer.get2() abort elseif r.p(1) ==# '#' call r.seek_cur(2) return self.token(s:TOKEN_GTCS, '>#', pos) + elseif r.p(1) ==# '>' + call r.seek_cur(2) + return self.token(s:TOKEN_RSHIFT, '>>', pos) else call r.seek_cur(1) return self.token(s:TOKEN_GT, '>', pos) @@ -3536,6 +3543,9 @@ function! s:ExprTokenizer.get2() abort elseif r.p(1) ==# '#' call r.seek_cur(2) return self.token(s:TOKEN_LTCS, '<#', pos) + elseif r.p(1) ==# '<' + call r.seek_cur(2) + return self.token(s:TOKEN_LSHIFT, '<<', pos) else call r.seek_cur(1) return self.token(s:TOKEN_LT, '<', pos) @@ -4021,7 +4031,7 @@ endfunction " expr6 . expr6 .. " expr6 .. expr6 .. function! s:ExprParser.parse_expr5() abort - let left = self.parse_expr6() + let left = self.parse_expr5_5() while s:TRUE let pos = self.reader.tell() let token = self.tokenizer.get() @@ -4029,24 +4039,51 @@ function! s:ExprParser.parse_expr5() abort let node = s:Node(s:NODE_ADD) let node.pos = token.pos let node.left = left - let node.right = self.parse_expr6() + let node.right = self.parse_expr5_5() let left = node elseif token.type ==# s:TOKEN_MINUS let node = s:Node(s:NODE_SUBTRACT) let node.pos = token.pos let node.left = left - let node.right = self.parse_expr6() + let node.right = self.parse_expr5_5() let left = node elseif token.type ==# s:TOKEN_DOTDOT " TODO check scriptversion? let node = s:Node(s:NODE_CONCAT) let node.pos = token.pos let node.left = left - let node.right = self.parse_expr6() + let node.right = self.parse_expr5_5() let left = node elseif token.type ==# s:TOKEN_DOT " TODO check scriptversion? let node = s:Node(s:NODE_CONCAT) let node.pos = token.pos let node.left = left + let node.right = self.parse_expr5_5() + let left = node + else + call self.reader.seek_set(pos) + break + endif + endwhile + return left +endfunction + +" expr5_5: expr6 << expr6 .. +" expr6 >> expr6 .. +function! s:ExprParser.parse_expr5_5() abort + let left = self.parse_expr6() + while s:TRUE + let pos = self.reader.tell() + let token = self.tokenizer.get() + if token.type ==# s:TOKEN_LSHIFT + let node = s:Node(s:NODE_LSHIFT) + let node.pos = token.pos + let node.left = left + let node.right = self.parse_expr6() + let left = node + elseif token.type ==# s:TOKEN_RSHIFT + let node = s:Node(s:NODE_RSHIFT) + let node.pos = token.pos + let node.left = left let node.right = self.parse_expr6() let left = node else @@ -5151,6 +5188,10 @@ function! s:Compiler.compile(node) abort return self.compile_divide(a:node) elseif a:node.type ==# s:NODE_REMAINDER return self.compile_remainder(a:node) + elseif a:node.type ==# s:NODE_LSHIFT + return self.compile_lshift(a:node) + elseif a:node.type ==# s:NODE_RSHIFT + return self.compile_rshift(a:node) elseif a:node.type ==# s:NODE_NOT return self.compile_not(a:node) elseif a:node.type ==# s:NODE_PLUS @@ -5600,6 +5641,14 @@ function! s:Compiler.compile_remainder(node) abort return printf('(%% %s %s)', self.compile(a:node.left), self.compile(a:node.right)) endfunction +function! s:Compiler.compile_lshift(node) abort + return printf('(<< %s %s)', self.compile(a:node.left), self.compile(a:node.right)) +endfunction + +function! s:Compiler.compile_rshift(node) abort + return printf('(>> %s %s)', self.compile(a:node.left), self.compile(a:node.right)) +endfunction + function! s:Compiler.compile_not(node) abort return printf('(! %s)', self.compile(a:node.left)) endfunction diff --git a/js/vimlparser.js b/js/vimlparser.js index 91fc9c0..0a62c4d 100644 --- a/js/vimlparser.js +++ b/js/vimlparser.js @@ -302,6 +302,8 @@ var NODE_REMAINDER = 72; var NODE_NOT = 73; var NODE_MINUS = 74; var NODE_PLUS = 75; +var NODE_LSHIFT = 99; +var NODE_RSHIFT = 100; var NODE_SUBSCRIPT = 76; var NODE_SLICE = 77; var NODE_CALL = 78; @@ -393,6 +395,8 @@ var TOKEN_BLOB = 66; var TOKEN_LITCOPEN = 67; var TOKEN_DOTDOT = 68; var TOKEN_HEREDOC = 69; +var TOKEN_LSHIFT = 70; +var TOKEN_RSHIFT = 71; var MAX_FUNC_ARGS = 20; function isalpha(c) { return viml_eqregh(c, "^[A-Za-z]$"); @@ -2709,6 +2713,10 @@ ExprTokenizer.prototype.get2 = function() { r.seek_cur(2); return this.token(TOKEN_GTCS, ">#", pos); } + else if (r.p(1) == ">") { + r.seek_cur(2); + return this.token(TOKEN_RSHIFT, ">>", pos); + } else { r.seek_cur(1); return this.token(TOKEN_GT, ">", pos); @@ -2723,6 +2731,10 @@ ExprTokenizer.prototype.get2 = function() { r.seek_cur(2); return this.token(TOKEN_LTCS, "<#", pos); } + else if (r.p(1) == "<") { + r.seek_cur(2); + return this.token(TOKEN_LSHIFT, "<<", pos); + } else { r.seek_cur(1); return this.token(TOKEN_LT, "<", pos); @@ -3274,7 +3286,7 @@ ExprParser.prototype.parse_expr4 = function() { // expr6 . expr6 .. // expr6 .. expr6 .. ExprParser.prototype.parse_expr5 = function() { - var left = this.parse_expr6(); + var left = this.parse_expr5_5(); while (TRUE) { var pos = this.reader.tell(); var token = this.tokenizer.get(); @@ -3282,14 +3294,14 @@ ExprParser.prototype.parse_expr5 = function() { var node = Node(NODE_ADD); node.pos = token.pos; node.left = left; - node.right = this.parse_expr6(); + node.right = this.parse_expr5_5(); var left = node; } else if (token.type == TOKEN_MINUS) { var node = Node(NODE_SUBTRACT); node.pos = token.pos; node.left = left; - node.right = this.parse_expr6(); + node.right = this.parse_expr5_5(); var left = node; } else if (token.type == TOKEN_DOTDOT) { @@ -3297,7 +3309,7 @@ ExprParser.prototype.parse_expr5 = function() { var node = Node(NODE_CONCAT); node.pos = token.pos; node.left = left; - node.right = this.parse_expr6(); + node.right = this.parse_expr5_5(); var left = node; } else if (token.type == TOKEN_DOT) { @@ -3305,6 +3317,35 @@ ExprParser.prototype.parse_expr5 = function() { var node = Node(NODE_CONCAT); node.pos = token.pos; node.left = left; + node.right = this.parse_expr5_5(); + var left = node; + } + else { + this.reader.seek_set(pos); + break; + } + } + return left; +} + +// expr5_5: expr6 << expr6 .. +// expr6 >> expr6 .. +ExprParser.prototype.parse_expr5_5 = function() { + var left = this.parse_expr6(); + while (TRUE) { + var pos = this.reader.tell(); + var token = this.tokenizer.get(); + if (token.type == TOKEN_LSHIFT) { + var node = Node(NODE_LSHIFT); + node.pos = token.pos; + node.left = left; + node.right = this.parse_expr6(); + var left = node; + } + else if (token.type == TOKEN_RSHIFT) { + var node = Node(NODE_RSHIFT); + node.pos = token.pos; + node.left = left; node.right = this.parse_expr6(); var left = node; } @@ -4536,6 +4577,12 @@ Compiler.prototype.compile = function(node) { else if (node.type == NODE_REMAINDER) { return this.compile_remainder(node); } + else if (node.type == NODE_LSHIFT) { + return this.compile_lshift(node); + } + else if (node.type == NODE_RSHIFT) { + return this.compile_rshift(node); + } else if (node.type == NODE_NOT) { return this.compile_not(node); } @@ -5023,6 +5070,14 @@ Compiler.prototype.compile_remainder = function(node) { return viml_printf("(%% %s %s)", this.compile(node.left), this.compile(node.right)); } +Compiler.prototype.compile_lshift = function(node) { + return viml_printf("(<< %s %s)", this.compile(node.left), this.compile(node.right)); +} + +Compiler.prototype.compile_rshift = function(node) { + return viml_printf("(>> %s %s)", this.compile(node.left), this.compile(node.right)); +} + Compiler.prototype.compile_not = function(node) { return viml_printf("(! %s)", this.compile(node.left)); } diff --git a/py/vimlparser.py b/py/vimlparser.py index f0562b6..17476c0 100644 --- a/py/vimlparser.py +++ b/py/vimlparser.py @@ -289,6 +289,8 @@ def viml_stridx(a, b): NODE_NOT = 73 NODE_MINUS = 74 NODE_PLUS = 75 +NODE_LSHIFT = 99 +NODE_RSHIFT = 100 NODE_SUBSCRIPT = 76 NODE_SLICE = 77 NODE_CALL = 78 @@ -380,6 +382,8 @@ def viml_stridx(a, b): TOKEN_LITCOPEN = 67 TOKEN_DOTDOT = 68 TOKEN_HEREDOC = 69 +TOKEN_LSHIFT = 70 +TOKEN_RSHIFT = 71 MAX_FUNC_ARGS = 20 @@ -2209,6 +2213,9 @@ def get2(self): elif r.p(1) == "#": r.seek_cur(2) return self.token(TOKEN_GTCS, ">#", pos) + elif r.p(1) == ">": + r.seek_cur(2) + return self.token(TOKEN_RSHIFT, ">>", pos) else: r.seek_cur(1) return self.token(TOKEN_GT, ">", pos) @@ -2219,6 +2226,9 @@ def get2(self): elif r.p(1) == "#": r.seek_cur(2) return self.token(TOKEN_LTCS, "<#", pos) + elif r.p(1) == "<": + r.seek_cur(2) + return self.token(TOKEN_LSHIFT, "<<", pos) else: r.seek_cur(1) return self.token(TOKEN_LT, "<", pos) @@ -2666,7 +2676,7 @@ def parse_expr4(self): # expr6 . expr6 .. # expr6 .. expr6 .. def parse_expr5(self): - left = self.parse_expr6() + left = self.parse_expr5_5() while TRUE: pos = self.reader.tell() token = self.tokenizer.get() @@ -2674,26 +2684,50 @@ def parse_expr5(self): node = Node(NODE_ADD) node.pos = token.pos node.left = left - node.right = self.parse_expr6() + node.right = self.parse_expr5_5() left = node elif token.type == TOKEN_MINUS: node = Node(NODE_SUBTRACT) node.pos = token.pos node.left = left - node.right = self.parse_expr6() + node.right = self.parse_expr5_5() left = node elif token.type == TOKEN_DOTDOT: # TODO check scriptversion? node = Node(NODE_CONCAT) node.pos = token.pos node.left = left - node.right = self.parse_expr6() + node.right = self.parse_expr5_5() left = node elif token.type == TOKEN_DOT: # TODO check scriptversion? node = Node(NODE_CONCAT) node.pos = token.pos node.left = left + node.right = self.parse_expr5_5() + left = node + else: + self.reader.seek_set(pos) + break + return left + + # expr5_5: expr6 << expr6 .. + # expr6 >> expr6 .. + def parse_expr5_5(self): + left = self.parse_expr6() + while TRUE: + pos = self.reader.tell() + token = self.tokenizer.get() + if token.type == TOKEN_LSHIFT: + node = Node(NODE_LSHIFT) + node.pos = token.pos + node.left = left + node.right = self.parse_expr6() + left = node + elif token.type == TOKEN_RSHIFT: + node = Node(NODE_RSHIFT) + node.pos = token.pos + node.left = left node.right = self.parse_expr6() left = node else: @@ -3647,6 +3681,10 @@ def compile(self, node): return self.compile_divide(node) elif node.type == NODE_REMAINDER: return self.compile_remainder(node) + elif node.type == NODE_LSHIFT: + return self.compile_lshift(node) + elif node.type == NODE_RSHIFT: + return self.compile_rshift(node) elif node.type == NODE_NOT: return self.compile_not(node) elif node.type == NODE_PLUS: @@ -4007,6 +4045,12 @@ def compile_divide(self, node): def compile_remainder(self, node): return viml_printf("(%% %s %s)", self.compile(node.left), self.compile(node.right)) + def compile_lshift(self, node): + return viml_printf("(<< %s %s)", self.compile(node.left), self.compile(node.right)) + + def compile_rshift(self, node): + return viml_printf("(>> %s %s)", self.compile(node.left), self.compile(node.right)) + def compile_not(self, node): return viml_printf("(! %s)", self.compile(node.left)) diff --git a/test/test_bitshift.ok b/test/test_bitshift.ok new file mode 100644 index 0000000..ae9ad00 --- /dev/null +++ b/test/test_bitshift.ok @@ -0,0 +1,6 @@ +; Test bitshift operators +(let = a (<< 1 2)) +(let = b (>> 8 2)) +(let = c (+ (<< 2 3) (>> 16 2))) +(let = d (<< (<< 1 2) 3)) +(let = e (>> (>> 32 1) 2)) diff --git a/test/test_bitshift.vim b/test/test_bitshift.vim new file mode 100644 index 0000000..1ef4660 --- /dev/null +++ b/test/test_bitshift.vim @@ -0,0 +1,6 @@ +" Test bitshift operators +let a = 1 << 2 +let b = 8 >> 2 +let c = (2 << 3) + (16 >> 2) +let d = 1 << 2 << 3 +let e = 32 >> 1 >> 2 From fcfe723b975acb4262ccaf3fdec539bbcc029c88 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 18 Jan 2026 22:19:29 +0900 Subject: [PATCH 2/7] fix --- autoload/vimlparser.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimlparser.vim b/autoload/vimlparser.vim index b4dad04..b691659 100644 --- a/autoload/vimlparser.vim +++ b/autoload/vimlparser.vim @@ -121,8 +121,6 @@ let s:NODE_REMAINDER = 72 let s:NODE_NOT = 73 let s:NODE_MINUS = 74 let s:NODE_PLUS = 75 -let s:NODE_LSHIFT = 99 -let s:NODE_RSHIFT = 100 let s:NODE_SUBSCRIPT = 76 let s:NODE_SLICE = 77 let s:NODE_CALL = 78 @@ -145,6 +143,8 @@ let s:NODE_EVAL = 95 let s:NODE_HEREDOC = 96 let s:NODE_METHOD = 97 let s:NODE_ECHOCONSOLE = 98 +let s:NODE_LSHIFT = 99 +let s:NODE_RSHIFT = 100 let s:TOKEN_EOF = 1 let s:TOKEN_EOL = 2 From 846e0554182398b1a163452a048c27bb8511480f Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 18 Jan 2026 22:28:28 +0900 Subject: [PATCH 3/7] update generated files for bitshift operators --- js/vimlparser.js | 4 ++-- py/vimlparser.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/vimlparser.js b/js/vimlparser.js index 0a62c4d..47e4c87 100644 --- a/js/vimlparser.js +++ b/js/vimlparser.js @@ -302,8 +302,6 @@ var NODE_REMAINDER = 72; var NODE_NOT = 73; var NODE_MINUS = 74; var NODE_PLUS = 75; -var NODE_LSHIFT = 99; -var NODE_RSHIFT = 100; var NODE_SUBSCRIPT = 76; var NODE_SLICE = 77; var NODE_CALL = 78; @@ -326,6 +324,8 @@ var NODE_EVAL = 95; var NODE_HEREDOC = 96; var NODE_METHOD = 97; var NODE_ECHOCONSOLE = 98; +var NODE_LSHIFT = 99; +var NODE_RSHIFT = 100; var TOKEN_EOF = 1; var TOKEN_EOL = 2; var TOKEN_SPACE = 3; diff --git a/py/vimlparser.py b/py/vimlparser.py index 17476c0..3a1c1ad 100644 --- a/py/vimlparser.py +++ b/py/vimlparser.py @@ -289,8 +289,6 @@ def viml_stridx(a, b): NODE_NOT = 73 NODE_MINUS = 74 NODE_PLUS = 75 -NODE_LSHIFT = 99 -NODE_RSHIFT = 100 NODE_SUBSCRIPT = 76 NODE_SLICE = 77 NODE_CALL = 78 @@ -313,6 +311,8 @@ def viml_stridx(a, b): NODE_HEREDOC = 96 NODE_METHOD = 97 NODE_ECHOCONSOLE = 98 +NODE_LSHIFT = 99 +NODE_RSHIFT = 100 TOKEN_EOF = 1 TOKEN_EOL = 2 TOKEN_SPACE = 3 From fbf72d173b41c1b8e1e1ce293527d0f886b8b174 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 18 Jan 2026 22:48:33 +0900 Subject: [PATCH 4/7] fix test_bitshift.ok to include coverage warning --- test/test_bitshift.ok | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_bitshift.ok b/test/test_bitshift.ok index ae9ad00..ca29051 100644 --- a/test/test_bitshift.ok +++ b/test/test_bitshift.ok @@ -1,3 +1,4 @@ +Coverage.py warning: Plugin file tracers (covimerage.CoveragePlugin) aren't supported with PyTracer ; Test bitshift operators (let = a (<< 1 2)) (let = b (>> 8 2)) From f2f3bcc560c9f14fba9090eed9ad2926504530a2 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 18 Jan 2026 22:51:56 +0900 Subject: [PATCH 5/7] add -q --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b4ab95d..2ace2eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: - vim_version: installed make_target: fast-test test_profile: vim-profile-installed.txt - test_python: "python -m coverage run --append" + test_python: "python -m coverage run --append -q" env: VIM_VERSION: ${{ matrix.vim_version }} From 0d89ac445184ff9cf18006e0b10d261349700d22 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 18 Jan 2026 22:56:19 +0900 Subject: [PATCH 6/7] fix test --- test/test_bitshift.ok | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_bitshift.ok b/test/test_bitshift.ok index ca29051..ae9ad00 100644 --- a/test/test_bitshift.ok +++ b/test/test_bitshift.ok @@ -1,4 +1,3 @@ -Coverage.py warning: Plugin file tracers (covimerage.CoveragePlugin) aren't supported with PyTracer ; Test bitshift operators (let = a (<< 1 2)) (let = b (>> 8 2)) From edff9c92f9d81938e27c73ef3f1e6b814955269b Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 18 Jan 2026 23:02:42 +0900 Subject: [PATCH 7/7] fix --- .github/workflows/test.yml | 2 +- test/run_command.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2ace2eb..b4ab95d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: - vim_version: installed make_target: fast-test test_profile: vim-profile-installed.txt - test_python: "python -m coverage run --append -q" + test_python: "python -m coverage run --append" env: VIM_VERSION: ${{ matrix.vim_version }} diff --git a/test/run_command.sh b/test/run_command.sh index 2a079d8..ed43612 100755 --- a/test/run_command.sh +++ b/test/run_command.sh @@ -26,7 +26,7 @@ test_file() { rm -f ${outfile} - ${vimlparser} ${neovim} ${vimfile} &> ${outfile} + ${vimlparser} ${neovim} ${vimfile} 2>&1 | grep -v "^Coverage.py warning" > ${outfile} diffout=$(diff -u ${outfile} ${okfile}) if [ -n "$diffout" ]; then