Skip to content
Merged
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
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
3 changes: 3 additions & 0 deletions lib/rouge/guessers/disambiguation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ def match?(filename)
next ObjectiveC if matches?(/@(end|implementation|protocol|property)\b/)
next ObjectiveC if contains?('@"')

next Mathematica if contains?('(*')
next Mathematica if contains?(':=')

next Matlab if matches?(/^\s*?%/)
end

Expand Down
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, Name::Tag
rule in_out, Generic::Prompt
rule /#{context_symbol}/m do |m|
match = m[0]
if self.class.keywords.include? match
token Name::Builtin::Pseudo
elsif self.class.builtins.include? match
token Name::Builtin
else
token Name::Variable
end
end
rule slot, Name::Function
rule operators, Operator
rule braces, Punctuation
rule string, Str
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', :source => '(* Fibonacci numbers with memoization *)'
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