From d31841900ff675661d124e82f8b2d854fe11cb87 Mon Sep 17 00:00:00 2001 From: Abe Voelker Date: Wed, 2 Oct 2019 19:04:02 -0500 Subject: [PATCH] Add MiniZinc lexer (#1329) 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 --- lib/rouge/demos/minizinc | 23 ++++++++++ lib/rouge/lexers/minizinc.rb | 87 ++++++++++++++++++++++++++++++++++++ spec/lexers/minizinc_spec.rb | 20 +++++++++ spec/visual/samples/minizinc | 48 ++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 lib/rouge/demos/minizinc create mode 100644 lib/rouge/lexers/minizinc.rb create mode 100644 spec/lexers/minizinc_spec.rb create mode 100644 spec/visual/samples/minizinc diff --git a/lib/rouge/demos/minizinc b/lib/rouge/demos/minizinc new file mode 100644 index 0000000000..9429d7b11b --- /dev/null +++ b/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"]; diff --git a/lib/rouge/lexers/minizinc.rb b/lib/rouge/lexers/minizinc.rb new file mode 100644 index 0000000000..937a140a00 --- /dev/null +++ b/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 diff --git a/spec/lexers/minizinc_spec.rb b/spec/lexers/minizinc_spec.rb new file mode 100644 index 0000000000..a7bbd476a9 --- /dev/null +++ b/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 diff --git a/spec/visual/samples/minizinc b/spec/visual/samples/minizinc new file mode 100644 index 0000000000..b6fca0374d --- /dev/null +++ b/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]));