diff --git a/lib/rouge/demos/moonscript b/lib/rouge/demos/moonscript new file mode 100644 index 0000000000..4c8511ddcf --- /dev/null +++ b/lib/rouge/demos/moonscript @@ -0,0 +1,16 @@ +util = require "my.module" + +a_table = { + foo: 'bar' + interpolated: "foo-#{other.stuff 2 + 3}" + "string": 2 + do: 'keyword' +} + +class MyClass extends SomeClass + new: (@init, arg2 = 'default') => + @derived = @init + 2 + super! + + other: => + @foo + 2 diff --git a/lib/rouge/lexers/moonscript.rb b/lib/rouge/lexers/moonscript.rb new file mode 100644 index 0000000000..949b7b1691 --- /dev/null +++ b/lib/rouge/lexers/moonscript.rb @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- # +# +require 'rouge/lexers/lua' + +module Rouge + module Lexers + class Moonscript < RegexLexer + desc "Moonscript (http://www.moonscript.org)" + tag 'moonscript' + aliases 'moon' + filenames '*.moon' + mimetypes 'text/x-moonscript', 'application/x-moonscript' + + def initialize(opts={}) + @function_highlighting = opts.delete(:function_highlighting) { true } + @disabled_modules = opts.delete(:disabled_modules) { [] } + super(opts) + end + + def self.analyze_text(text) + return 1 if text.shebang? 'moon' + end + + def builtins + return [] unless @function_highlighting + + @builtins ||= Set.new.tap do |builtins| + Rouge::Lexers::Lua.builtins.each do |mod, fns| + next if @disabled_modules.include? mod + builtins.merge(fns) + end + end + end + + state :root do + rule %r(#!(.*?)$), Comment::Preproc # shebang + rule //, Text, :main + end + + state :base do + ident = '(?:[\w_][\w\d_]*)' + + rule %r((?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?'), Num::Float + rule %r((?i)\d+e[+-]?\d+), Num::Float + rule %r((?i)0x[0-9a-f]*), Num::Hex + rule %r(\d+), Num::Integer + rule %r(@#{ident}*), Name::Variable::Instance + rule %r([A-Z][\w\d_]*), Name::Class + rule %r("?#{ident}+"?:), Literal::String::Symbol + rule %r(:#{ident}), Literal::String::Symbol + + rule %r(\s+), Text::Whitespace + rule %r((==|~=|!=|<=|>=|\.\.\.|\.\.|->|=>|[=+\-*/%^<>#!\\])), Operator + rule %r([\[\]\{\}\(\)\.,:;]), Punctuation + rule %r((and|or|not)\b), Operator::Word + + keywords = %w{ + break class continue do else elseif end extends for if import in + repeat return switch super then unless until using when with while + } + rule %r((#{keywords.join('|')})\b), Keyword + rule %r((local|export)\b), Keyword::Declaration + rule %r((true|false|nil)\b), Keyword::Constant + + rule %r([A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?) do |m| + name = m[0] + if self.builtins.include?(name) + token Name::Builtin + elsif name =~ /\./ + a, b = name.split('.', 2) + token Name, a + token Punctuation, '.' + token Name, b + else + token Name + end + end + end + + state :main do + rule %r(--.*$), Comment::Single + rule %r(\[(=*)\[.*?\]\1\])m, Str::Heredoc + + mixin :base + + rule %r('), Str::Single, :sqs + rule %r("), Str::Double, :dqs + end + + state :sqs do + rule %r('), Str::Single, :pop! + rule %r([^']+), Str::Single + end + + state :interpolation do + rule %r(\}), Str::Interpol, :pop! + mixin :base + end + + state :dqs do + rule %r(#\{), Str::Interpol, :interpolation + rule %r("), Str::Double, :pop! + rule %r(#[^{]), Str::Double + rule %r([^"#]+), Str::Double + end + end + end +end diff --git a/spec/lexers/moonscript_spec.rb b/spec/lexers/moonscript_spec.rb new file mode 100644 index 0000000000..015842b55a --- /dev/null +++ b/spec/lexers/moonscript_spec.rb @@ -0,0 +1,21 @@ +describe Rouge::Lexers::Moonscript do + let(:subject) { Rouge::Lexers::Moonscript.new } + + describe 'guessing' do + include Support::Guessing + + it 'guesses by filename' do + assert_guess :filename => 'foo.moon' + end + + it 'guesses by mimetype' do + assert_guess :mimetype => 'text/x-moonscript' + assert_guess :mimetype => 'application/x-moonscript' + end + + it 'guesses by source' do + assert_guess :source => '#!/usr/local/bin/moon' + assert_guess :source => '#! /usr/bin/env moon' + end + end +end diff --git a/spec/visual/samples/moonscript b/spec/visual/samples/moonscript new file mode 100644 index 0000000000..630e94158e --- /dev/null +++ b/spec/visual/samples/moonscript @@ -0,0 +1,63 @@ +#! /usr/bin/moon + +-- A heap of sample Moonscript syntax + +util = require "my.module" + +import SomeClass, member from util +import other from require "my.other_module" + +a_table = { + foo: 'bar' + interpolated: "foo-#{other.stuff 2 + 3}" + "string-key": 2 + do: 'keyword' +} + +short_table_def = foo: 'bar', interpolated: "foo-#{other.stuff 2 + 3}" +scoped_table = :util, :a_table + +multiline_string = "line 1 + for the alliance! +line2" + +other_multiline_string = [[ for +the +win +]] + +local x +export y + +x or= 1 +x += 1 +y and= x + +empty_function -> +args_function (arg1, arg2) -> arg1 + arg2 +var_args_function (...) -> table.concat {...}, '|' + +while cond == true do empty_function! + +comprehension = [item * 2 for i, item in ipairs items when item != 3] + +for i = 1,10 + continue unless i != 2 + +SomeClass(0xdeadbeef)\method 'foo' + +with a_table + .foobar = {} + +switch i + when 2 + "not first" + +class MyClass extends SomeClass + new: (@init, arg2 = 'default') => + @derived = @init + 2 + super! + + other: => + @@foo + 2 + @