Skip to content

Commit

Permalink
Add MiniZinc lexer (#1329)
Browse files Browse the repository at this point in the history
This commit adds a lexer for the MiniZinc language.

* Shorten link

* Simplify ID regex

* Remove unnecessary newline rule

* Simplify single comment rule

* Change priority of generic identifier rule

* Remove unnecessary parentheses
  • Loading branch information
abevoelker authored and pyrmont committed Oct 3, 2019
1 parent be51612 commit d318419
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 0 deletions.
23 changes: 23 additions & 0 deletions lib/rouge/demos/minizinc
@@ -0,0 +1,23 @@
% from MiniZinc Handbook:
% https://www.minizinc.org/doc-latest/en/modelling.html

% Colouring Australia using nc colours
int: nc = 3;

var 1..nc: wa; var 1..nc: nt; var 1..nc: sa; var 1..nc: q;
var 1..nc: nsw; var 1..nc: v; var 1..nc: t;

constraint wa != nt;
constraint wa != sa;
constraint nt != sa;
constraint nt != q;
constraint sa != q;
constraint sa != nsw;
constraint sa != v;
constraint q != nsw;
constraint nsw != v;
solve satisfy;

output ["wa=\(wa)\t nt=\(nt)\t sa=\(sa)\n",
"q=\(q)\t nsw=\(nsw)\t v=\(v)\n",
"t=", show(t), "\n"];
87 changes: 87 additions & 0 deletions lib/rouge/lexers/minizinc.rb
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

# Based on Chroma's MiniZinc lexer:
# https://github.com/alecthomas/chroma/blob/5152194c717b394686d3d7a7e1946a360ec0728f/lexers/m/minizinc.go

module Rouge
module Lexers
class MiniZinc < RegexLexer
title "MiniZinc"
desc "MiniZinc is a free and open-source constraint modeling language (minizinc.org)"
tag 'minizinc'
filenames '*.mzn', '*.fzn', '*.dzn'
mimetypes 'text/minizinc'

def self.builtins
@builtins = Set.new %w[
abort abs acosh array_intersect array_union array1d array2d array3d
array4d array5d array6d asin assert atan bool2int card ceil concat
cos cosh dom dom_array dom_size fix exp floor index_set
index_set_1of2 index_set_2of2 index_set_1of3 index_set_2of3
index_set_3of3 int2float is_fixed join lb lb_array length ln log log2
log10 min max pow product round set2array show show_int show_float
sin sinh sqrt sum tan tanh trace ub ub_array
]
end

def self.keywords
@keywords = Set.new %w[
ann annotation any constraint else endif function for forall if
include list of op output minimize maximize par predicate record
satisfy solve test then type var where
]
end

def self.keywords_type
@keywords_type ||= Set.new %w(
array set bool enum float int string tuple
)
end

def self.operators
@operators ||= Set.new %w(
in subset superset union diff symdiff intersect
)
end

id = /[$a-zA-Z_]\w*/

state :root do
rule %r(\s+)m, Text::Whitespace
rule %r(\\\n)m, Text::Whitespace
rule %r(%.*), Comment::Single
rule %r(/(\\\n)?[*](.|\n)*?[*](\\\n)?/)m, Comment::Multiline
rule %r/"(\\\\|\\"|[^"])*"/, Literal::String

rule %r(not|<->|->|<-|\\/|xor|/\\), Operator
rule %r(<|>|<=|>=|==|=|!=), Operator
rule %r(\+|-|\*|/|div|mod), Operator
rule %r(\\|\.\.|\+\+), Operator
rule %r([|()\[\]{},:;]), Punctuation
rule %r((true|false)\b), Keyword::Constant
rule %r(([+-]?)\d+(\.(?!\.)\d*)?([eE][-+]?\d+)?), Literal::Number

rule id do |m|
if self.class.keywords.include? m[0]
token Keyword
elsif self.class.keywords_type.include? m[0]
token Keyword::Type
elsif self.class.builtins.include? m[0]
token Name::Builtin
elsif self.class.operators.include? m[0]
token Operator
else
token Name::Other
end
end

rule %r(::\s*([^\W\d]\w*)(\s*\([^\)]*\))?), Name::Decorator
rule %r(\b([^\W\d]\w*)\b(\()) do
groups Name::Function, Punctuation
end
rule %r([^\W\d]\w*), Name::Other
end
end
end
end
20 changes: 20 additions & 0 deletions spec/lexers/minizinc_spec.rb
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

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

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.mzn'
assert_guess :filename => 'foo.fzn'
assert_guess :filename => 'foo.dzn'
end

it 'guesses by mimetype' do
assert_guess :mimetype => 'text/minizinc'
end
end
end
48 changes: 48 additions & 0 deletions spec/visual/samples/minizinc
@@ -0,0 +1,48 @@
% from MiniZinc Handbook:
% https://www.minizinc.org/doc-latest/en/predicates.html

% Simple nurse rostering
include "regular.mzn";
enum NURSE;
enum DAY;
int: req_day;
int: req_night;
int: min_night;

enum SHIFT = { d, n, o };
int: S = card(SHIFT);

int: Q = 6; int: q0 = 1; set of int: STATE = 1..Q;
array[STATE,SHIFT] of int: t =
[| 2, 3, 1 % state 1
| 4, 4, 1 % state 2
| 4, 5, 1 % state 3
| 6, 6, 1 % state 4
| 6, 0, 1 % state 5
| 0, 0, 1|]; % state 6

array[NURSE,DAY] of var SHIFT: roster;

constraint forall(j in DAY)(
sum(i in NURSE)(roster[i,j] == d) == req_day /\
sum(i in NURSE)(roster[i,j] == n) == req_night
);
constraint forall(i in NURSE)(
regular([roster[i,j] | j in DAY], Q, S, t, q0, STATE) /\
sum(j in DAY)(roster[i,j] == n) >= min_night
);

solve satisfy;

output [ show(roster[i,j]) ++ if j==card(DAY) then "\n" else " " endif
| i in NURSE, j in DAY ];

/*
Multiline
comment
test
*/

predicate atmostone(array[int] of var bool:x) =
forall(i,j in index_set(x) where i < j)(
(not x[i] \/ not x[j]));

0 comments on commit d318419

Please sign in to comment.