# -*- coding: utf-8 -*- # # frozen_string_literal: true module Rouge module Lexers class J < RegexLexer title 'J' desc "The J programming language (jsoftware.com)" tag 'j' filenames '*.ijs', '*.ijt' # For J-specific terms we use, see: # https://code.jsoftware.com/wiki/Vocabulary/AET # https://code.jsoftware.com/wiki/Vocabulary/Glossary # https://code.jsoftware.com/wiki/Vocabulary/PartsOfSpeech def self.token_map @token_map ||= { noun: Keyword::Constant, verb: Name::Function, modifier: Operator, name: Name, param: Name::Builtin::Pseudo, other: Punctuation, nil => Error, } end # https://code.jsoftware.com/wiki/NuVoc def self.inflection_list @inflection_list ||= ['', '.', ':', '..', '.:', ':.', '::'] end def self.primitive_table @primitive_table ||= Hash.new([:name]).tap do |h| { '()' => [:other], '=' => [:verb, :other, :other], '<>+-*%$|,#' => [:verb, :verb, :verb], '^' => [:verb, :verb, :modifier], '~"' => [:modifier, :verb, :verb], '.:@' => [:modifier, :modifier, :modifier], ';' => [:verb, :modifier, :verb], '!' => [:verb, :modifier, :modifier], '/\\' => [:modifier, :modifier, :verb], '[' => [:verb, nil, :verb], ']' => [:verb], '{' => [:verb, :verb, :verb, nil, nil, nil, :verb], '}' => [:modifier, :verb, :verb, nil, nil, nil, :modifier], '`' => [:modifier, nil, :modifier], '&' => [:modifier, :modifier, :modifier, nil, :modifier], '?' => [:verb, :verb], 'a' => [:name, :noun, :noun], 'ACeEIjorv' => [:name, :verb], 'bdfHMT' => [:name, :modifier], 'Dt' => [:name, :modifier, :modifier], 'F' => [:name, :modifier, :modifier, :modifier, :modifier, :modifier, :modifier], 'iu' => [:name, :verb, :verb], 'L' => [:name, :verb, :modifier], 'mny' => [:param], 'p' => [:name, :verb, :verb, :verb], 'qsZ' => [:name, nil, :verb], 'S' => [:name, nil, :modifier], 'u' => [:param, :verb, :verb], 'v' => [:param, :verb], 'x' => [:param, nil, :verb], }.each {|k, v| k.each_char {|c| h[c] = v } } end end def self.primitive(char, inflection) i = inflection_list.index(inflection) or return Error token_map[primitive_table[char][i]] end def self.control_words @control_words ||= Set.new %w( assert break case catch catchd catcht continue do else elseif end fcase for if return select throw try while whilst ) end def self.control_words_id @control_words_id ||= Set.new %w(for goto label) end state :expr do rule %r/\s+/, Text rule %r'([!-&(-/:-@\[-^`{-~]|[A-Za-z]\b)([.:]*)' do |m| token J.primitive(m[1], m[2]) end rule %r/(?:\d|_\d?):([.:]*)/ do |m| token m[1].empty? ? J.token_map[:verb] : Error end rule %r/[\d_][\w.]*([.:]*)/ do |m| token m[1].empty? ? Num : Error end rule %r/'/, Str::Single, :str rule %r/NB\.(?![.:]).*/, Comment::Single rule %r/([A-Za-z]\w*)([.:]*)/ do |m| if m[2] == '.' word, sep, id = m[1].partition '_' list = if sep.empty? J.control_words elsif not id.empty? J.control_words_id end if list and list.include? word token Keyword, word + sep token((word == 'for' ? Name : Name::Label), id) token Keyword, m[2] else token Error end else token m[2].empty? ? Name : Error end end end state :str do rule %r/''/, Str::Escape rule %r/[^'\n]+/, Str::Single rule %r/'|$/, Str::Single, :pop! end start do @note_next = false end state :root do rule %r/\n/ do token Text if @note_next push :note @note_next = false end end # https://code.jsoftware.com/wiki/Vocabulary/com # https://code.jsoftware.com/wiki/Vocabulary/NounExplicitDefinition rule %r/ ([0-4]|13|adverb|conjunction|dyad|monad|noun|verb)([\ \t]+) (def(?:ine)?\b|:)(?![.:])([\ \t]*) /x do |m| groups Keyword::Pseudo, Text, Keyword::Pseudo, Text @def_body = (m[1] == '0' || m[1] == 'noun') ? :noun : :code if m[3] == 'define' # stack: [:root] # or [:root, ..., :def_next] pop! if stack.size > 1 push @def_body push :def_next # [:root, ..., @def_body, :def_next] else push :expl_def end end rule %r/^([ \t]*)(Note\b(?![.:]))([ \t\r]*)(?!=[.:]|$)/ do groups Text, Name, Text @note_next = true end rule %r/[mnuvxy]\b(?![.:])/, Name mixin :expr end state :def_next do rule %r/\n/, Text, :pop! mixin :root end state :expl_def do rule %r/0\b(?![.:])/ do token Keyword::Pseudo # stack: [:root, :expl_def] # or [:root, ..., :def_next, :expl_def] pop! if stack.size > 2 goto @def_body push :def_next # [:root, ..., @def_body, :def_next] end rule %r/'/ do if @def_body == :noun token Str::Single goto :str else token Punctuation goto :q_expr end end rule(//) { pop! } end # `q_expr` lexes the content of a string literal which is a part of an # explicit definition. # e.g. dyad def 'x + y' state :q_expr do rule %r/''/, Str::Single, :q_str rule %r/'|$/, Punctuation, :pop! rule %r/NB\.(?![.:])([^'\n]|'')*/, Comment::Single mixin :expr end state :q_str do rule %r/''''/, Str::Escape rule %r/[^'\n]+/, Str::Single rule %r/''/, Str::Single, :pop! rule(/'|$/) { token Punctuation; pop! 2 } end state :note do mixin :delimiter rule %r/.+\n?/, Comment::Multiline end state :noun do mixin :delimiter rule %r/.+\n?/, Str::Heredoc end state :code do mixin :delimiter rule %r/^([ \t]*)(:)([ \t\r]*)$/ do groups Text, Punctuation, Text end mixin :expr end state :delimiter do rule %r/^([ \t]*)(\))([ \t\r]*$\n?)/ do groups Text, Punctuation, Text pop! end end end end end