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

Support for Mathematica and Wolfram Language #854

Merged
merged 10 commits into from
Aug 7, 2018
Next Next commit
Support for Mathematica and Wolfram Language
* Provides highlighting of standard built-in symbols that are extracted from
an online list of functions provided by Wolfram.
* Support for the various number formats that Mathematica supports. Handling of context symbols
* Support for named characters like \[Alpha]
* Support for messages
* Support for slots and association slots of pure functions
* Support for In[1]:= and Out[1]= markers
* Support for nested comments and comment markup
  • Loading branch information
halirutan committed Jan 2, 2018
commit d3ea404226389f06a798856e31f6501597761d7d
8 changes: 8 additions & 0 deletions lib/rouge/demos/mathematica
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(* Fibonacci numbers with memoization *)

fib::usage = "f[n] calculates the n'th Fibonacci number.";
fib[0] = fib[1] = 1;
fib[n_Integer?Positive]:= fib[n] = fib[n-1] + fib[n-2];

In[4]:= fib[42]
Out[4]= 433494437
95 changes: 95 additions & 0 deletions lib/rouge/lexers/mathematica.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*- #

module Rouge
module Lexers
class Mathematica < RegexLexer
title "Mathematica"
desc "Wolfram Mathematica, the world's definitive system for modern technical computing."
tag 'mathematica'
aliases 'wl'
filenames '*.m', '*.wl'
mimetypes 'application/vnd.wolfram.mathematica.package', 'application/vnd.wolfram.wl'

# Mathematica has various input forms for numbers. We need to handle numbers in bases, precision, accuracy,
# and *^ scientific notation. All this works for integers and real numbers. Some examples
# 1 1234567 1.1 .3 0.2 1*^10 2*^+10 3*^-10
# 1`1 1``1 1.2` 1.2``1.234*^-10 1.2``1.234*^+10 1.2``1.234*^10
# 2^^01001 10^^1.2``20.1234*^-10
base = /(?:\d+)/
number = /(?:\.\d+|\d+\.\d*|\d+)/
number_base = /(?:\.\w+|\w+\.\w*|\w+)/
precision = /`(`?#{number})?/

operators = /[+\-*\/|,;.:@~=><&`'^?!_%]/
braces = /[\[\](){}]/

string = /"(\\\\|\\"|[^"])*"/

# symbols and namespaced symbols. Note the special form \[Gamma] for named characters. These are also symbols.
# Module With Block Integrate Table Plot
# x32 $x x$ $Context` Context123`$x `Private`Context
# \[Gamma] \[Alpha]x32 Context`\[Xi]
identifier = /[a-zA-Z$][$a-zA-Z0-9]*/
named_character = /\\\[#{identifier}\]/
symbol = /(#{identifier}|#{named_character})+/
context_symbol = /`?#{symbol}(`#{symbol})*`?/

# Slots for pure functions.
# Examples: # ## #1 ##3 #Test #"Test" #[Test] #["Test"]
association_slot = /#(#{identifier}|\"#{identifier}\")| /
slot = /#{association_slot}|#[0-9]*/

# Handling of message like symbol::usage or symbol::"argx"
message = /::(#{identifier}|#{string})/

# Highlighting of the special in and out markers that are prepended when you copy a cell
in_out = /(In|Out)\[[0-9]+\]:?=/

# Although Module, With and Block are normal built-in symbols, we give them a special treatment as they are
# the most important expressions for defining local variables
def self.keywords
@keywords = Set.new %w(
Module With Block
)
end

# The list of built-in symbols comes from a wolfram server and is created automatically by rake
def self.builtins
load Pathname.new(__FILE__).dirname.join('mathematica/builtins.rb')
self.builtins
end

state :root do
rule /\s+/, Text::Whitespace
rule /\(\*/, Comment, :comment
rule /#{base}\^\^#{number_base}#{precision}?(\*\^[+-]?\d+)?/, Num # a number with a base
rule /(?:#{number}#{precision}?(?:\*\^[+-]?\d+)?)/, Num # all other numbers
rule message, Operator::Word
rule in_out, Generic::Prompt
rule /#{context_symbol}/m do |m|
match = m[0]
if self.class.keywords.include? match
token Keyword::Reserved
elsif self.class.builtins.include? match
token Name::Builtin
else
token Name
end
end
rule slot, Name::Namespace
rule operators, Operator
rule braces, Operator::Word
rule string, Str::Single
end

# Allow for nested comments and special treatment of ::Section:: or :Author: markup
state :comment do
rule /\(\*/, Comment, :comment
rule /\*\)/, Comment, :pop!
rule /::#{identifier}::/, Comment::Preproc
rule /:(#{identifier}|[^\S])+:/, Comment::Preproc
rule /./, Comment
end
end
end
end
11 changes: 11 additions & 0 deletions lib/rouge/lexers/mathematica/builtins.rb

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions spec/lexers/mathematica_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*- #

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

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.wl'
assert_guess :filename => 'foo.m'
end

it 'guesses by mimetype' do
assert_guess :mimetype => 'application/vnd.wolfram.mathematica.package'
assert_guess :mimetype => 'application/vnd.wolfram.wl'
end
end
end

29 changes: 29 additions & 0 deletions spec/visual/samples/mathematica
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
(* :Title: Collatz Visualization *)
(* :Author: halirutan *)
(* :Mathematica Version: 10+ *)

CollatzSequence::usage = "CollatzSequence[list] creates a Collatz sequence.";
CollatzSequence[list_] := Module[{memory, tmp, chain, result = Internal`Bag[]},
memory[1] = False;
memory[n_] := (memory[n] = False; True);

Do[
chain = Internal`Bag[];
tmp = l;
While[memory[tmp],
Internal`StuffBag[chain, tmp];
tmp = If[EvenQ[tmp], tmp/2, 3 tmp + 1];
];
Internal`StuffBag[chain, tmp];
Internal`StuffBag[result, chain],
{l, list}];
Internal`BagPart[#, All] & /@ Internal`BagPart[result, All]
];

Graph[
Flatten[(Rule @@@ Partition[#, 2, 1]) & /@
CollatzSequence[Range[50000]]],
PerformanceGoal -> "Speed",
GraphLayout -> {"PackingLayout" -> "ClosestPacking"},
VertexStyle -> Opacity[0.2, RGBColor[44/51, 10/51, 47/255]],
EdgeStyle -> RGBColor[38/255, 139/255, 14/17]]
42 changes: 42 additions & 0 deletions tasks/mathematica.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*- #

require 'open-uri'

def mathematica_builtins(&b)
return enum_for :mathematica_builtins unless block_given?

mathematica_doc_url = 'http://reference.wolfram.com/language/guide/AlphabeticalListing.html'

mathematica_docs = open(mathematica_doc_url).read

p :docs => mathematica_docs

mathematica_docs.scan %r(<span class="IFSans"><a href="/language/ref/(\w+)[.]html">)m do |word|
p :word => word
yield word
end
end

def mathematica_builtins_source
yield "# -*- coding: utf-8 -*- #"
yield "# automatically generated by `rake builtins:mathematica`"
yield "module Rouge"
yield " module Lexers"
yield " class Mathematica"
yield " def self.builtins"
yield " @builtins ||= Set.new %w(#{mathematica_builtins.to_a.join(' ')})"
yield " end"
yield " end"
yield " end"
yield "end"
end

namespace :builtins do
task :mathematica do
File.open('lib/rouge/lexers/mathematica/builtins.rb', 'w') do |f|
mathematica_builtins_source do |line|
f.puts line
end
end
end
end