Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APL Lexer #1879

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/rouge/demos/apl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
⍝ simple tradfn
∇ output←f input
int←'a'(≠⊆⊢)input
output←1∘⎕C¨int

⍝ 2048 game
C ← ⎕UCS
'you ', {
2048 ∊ ⍵: 'win'
n ← ⍵
z ← 0 = ∊n
(z / ∊n)[? +/z] ← 2
⎕ ← 4 ↑¨ ⍕¨n
1 ∊ (⍉ 2 =⌿ n) , (2 =/ n) , 0 = n: ∇ {
R←↓⍉∘⌽⍣⎕
r ← R⍣¯1 ⎕UCS {
t ← 2 /¨ ⍺⍺ 2 * ⍳16
4 ↑ ⍺⍺ t ⎕R (1 ⌽ ∪¨t) ⍺⍺ ⍵ ~ 0
}¨ R ⍵
⍵ ≡ r: ∇ ⍵
r
} n
'lose'
} 4 4⍴0
40 changes: 40 additions & 0 deletions lib/rouge/lexers/apl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module Rouge
module Lexers
class APL < RegexLexer
# Many rules taken from https://github.com/Alhadis/language-apl/blob/master/grammars/apl.cson
title "APL"
desc "APL, a tool of thought for array programming"
tag 'apl'
filenames '*.apl', '*.apla', '*.aplf', '*.aplo', '*.apln', '*.aplc', '*.apli', '*.mipage'
mimetypes 'text/apl'

def self.detect?(text)
return true if text.shebang? 'apl'
end

state :root do
rule %r/^#!.+$/, Comment::Special
rule %r/'/, Str::Single, :str
rule %r/⍝.*/, Comment::Single
rule %r/:[A-Za-z]+/, Keyword
rule %r/[◊⋄;:←→\[\]\{\}\(\).]/, Punctuation
rule %r/⎕[A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ][A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ¯0-9]*/, Keyword::Reserved
rule %r/(?x)^\s*([A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ][A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ¯0-9]*)(:)/, Name::Label
rule %r/[A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ][A-Z_a-zÀ-ÖØ-Ýßà-öø-üþ∆⍙Ⓐ-Ⓩ¯0-9]*/, Name::Variable
rule %r/¯?[0-9][¯0-9A-Za-z]*(?:\.[¯0-9Ee][¯0-9A-Za-z]*)*|¯?\.[0-9Ee][¯0-9A-Za-z]/, Num::Number
rule %r'[¨⍨⍣∘⍤⍥@⌸⍨⍣⌿⍀/\\∘⍠&⌶⌺@]', Operator
rule %r'[+-×÷*⍟⌹○!?|⌈⌊⊥⊤⊣⊢=≠≤<>≥≡≢∨∧⍲⍱↑↓⊂⊃⊆⌷⍋⍒⍳⍸∊⍷∪∩~,⍪⍴⌽⊖⍉⍎⎕⍞⍕]', Keyword
rule %r/[⍬]/, Keyword::Constant
rule %r/[⍺⍵⍶⍹χ∇λ]/, Keyword::Variable
rule %r/\s+/, Text
end

state :str do
rule %r/''/, Str::Escape
rule %r/[^'\n]+/, Str::Single
rule %r/'|$/, Str::Single, :pop!
end

end
end
end
77 changes: 77 additions & 0 deletions spec/lexers/apl_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

describe Rouge::Lexers::APL do
let(:subject) { Rouge::Lexers::APL.new }

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.apl'
assert_guess :filename => 'foo.apla'
end
end

describe 'lexing' do
include Support::Lexing

describe 'primitives' do
it 'covers all primitive functions' do
'+-×÷*⍟⌹○!?|⌈⌊⊥⊤⊣⊢=≠≤<>≥≡≢∨∧⍲⍱↑↓⊂⊃⊆⌷⍋⍒⍳⍸∊⍷∪∩~,⍪⍴⌽⊖⍉⎕⍎⍕'.chars.each do |f|
assert_tokens_equal f, ['Keyword', f]
end
end
it 'covers all primitive operators' do
'¨⍨⍣∘⍤⍥@⌸⍨⍣⌿⍀/\\∘⍠&⌶⌺@'.chars.each do |op|
assert_tokens_equal op, ['Operator', op]
end
end
end

describe 'strings' do
it 'recognizes correct strings' do
assert_tokens_equal "'single quotes'", ['Literal.String.Single', "'single quotes'"]
assert_tokens_equal "'single '' quotes'",
['Literal.String.Single', "'single "],
['Literal.String.Escape', "''"],
['Literal.String.Single', " quotes'"]
end
it 'recognizes incorrect strings' do
assert_tokens_equal "'single quote", ['Literal.String.Single', "'single quote"]
end
end

describe 'numbers' do
it 'recognizes integers' do
assert_tokens_equal "123", ['Literal.Number', "123"]
assert_tokens_equal "123456", ['Literal.Number', "123456"]
end
it 'recognizes floats' do
assert_tokens_equal "123.456", ['Literal.Number', '123.456']
assert_tokens_equal "¯123.456", ['Literal.Number', '¯123.456']
assert_tokens_equal "123E5", ['Literal.Number', '123E5']
assert_tokens_equal "123e5", ['Literal.Number', '123e5']
assert_tokens_equal "¯12.3e5", ['Literal.Number', '¯12.3e5']
end
it 'recognizes imaginary numbers' do
assert_tokens_equal "124j456", ['Literal.Number', '124j456']
assert_tokens_equal "¯124j456", ['Literal.Number', '¯124j456']
assert_tokens_equal "¯124j¯456", ['Literal.Number', '¯124j¯456']
end
end

describe 'comments' do
it 'recognizes shebang' do
assert_tokens_equal "#!/usr/bin/env mapl", ["Comment.Special", "#!/usr/bin/env mapl"]
end
it 'recognizes line comments' do
assert_tokens_equal "⍝ line comment", ["Comment.Single", "⍝ line comment"]
assert_tokens_equal "123 ⍝ line comment",
["Literal.Number", "123"],
["Text", " "],
["Comment.Single", "⍝ line comment"]
end
end
end
end
36 changes: 36 additions & 0 deletions spec/visual/samples/apl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env mapl

⍝ RIDE sample:
{R}←{X}tradfn(Y Z);local
dfn←{ ⍝ comment
0 ¯1.2e¯3j¯.45 'string' ⍬
+/-⍣(×A):⍺∇⍵[i;j]
{{{{nested ⍺:∇⍵}⍺:∇⍵}⍺:∇⍵}⍺:∇⍵}
}
label:
:For i :In ⍳X ⋄ :EndFor
:If condition
{⍵[⍋⍵]} ⋄ global←local←0
⎕error ) ] } :error 'unclosed
:EndIf
search match

⍝ strings
'fdsfsdfd'
'dsafdsf''dsfsdfasdf'
'⍺⌊⌊⌈_⍺∊'
'a'

⍝ numbers
123
0
123.456
123E¯13
¯23

⍝ special:
⎕⍬→←⍞{⍺⍵⍺⍺⍵⍵⍶⍹}

⍝ names
fdsfasd dfadsf
a1 a2 a3