-
Notifications
You must be signed in to change notification settings - Fork 740
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds support for the SuperCollider language. As the standard file extension for SuperCollider files (`.sc`) was already registered to the Python lexer, this commit also adds a disambiguation.
- Loading branch information
Showing
6 changed files
with
320 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// modulate a sine frequency and a noise amplitude with another sine | ||
// whose frequency depends on the horizontal mouse pointer position | ||
~myFunction = { | ||
var x = SinOsc.ar(MouseX.kr(1, 100)); | ||
SinOsc.ar(300 * x + 800, 0, 0.1) | ||
+ | ||
PinkNoise.ar(0.1 * x + 0.1) | ||
}; | ||
|
||
~myFunction.play; | ||
"that's all, folks!".postln; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# -*- coding: utf-8 -*- # | ||
# frozen_string_literal: true | ||
|
||
module Rouge | ||
module Lexers | ||
class SuperCollider < RegexLexer | ||
tag 'supercollider' | ||
filenames '*.sc', '*.scd' | ||
|
||
title "SuperCollider" | ||
desc 'A cross-platform interpreted programming language for sound synthesis, algorithmic composition, and realtime performance' | ||
|
||
def self.keywords | ||
@keywords ||= Set.new %w( | ||
var arg classvar const super this | ||
) | ||
end | ||
|
||
# these aren't technically keywords, but we treat | ||
# them as such because it makes things clearer 99% | ||
# of the time | ||
def self.reserved | ||
@reserved ||= Set.new %w( | ||
case do for forBy loop if while new newCopyArgs | ||
) | ||
end | ||
|
||
def self.constants | ||
@constants ||= Set.new %w( | ||
true false nil inf thisThread | ||
thisMethod thisFunction thisProcess | ||
thisFunctionDef currentEnvironment | ||
topEnvironment | ||
) | ||
end | ||
|
||
state :whitespace do | ||
rule /\s+/m, Text | ||
end | ||
|
||
state :comments do | ||
rule %r(//.*?$), Comment::Single | ||
rule %r(/[*]) do | ||
token Comment::Multiline | ||
push :nested_comment | ||
end | ||
end | ||
|
||
state :nested_comment do | ||
rule %r(/[*]), Comment::Multiline, :nested_comment | ||
rule %r([*]/), Comment::Multiline, :pop! | ||
rule %r([^*/]+)m, Comment::Multiline | ||
rule /./, Comment::Multiline | ||
end | ||
|
||
state :root do | ||
mixin :whitespace | ||
mixin :comments | ||
|
||
rule /[\-+]?0[xX]\h+/, Num::Hex | ||
|
||
# radix float | ||
rule /[\-+]?\d+r[0-9a-zA-Z]*(\.[0-9A-Z]*)?/, Num::Float | ||
|
||
# normal float | ||
rule /[\-+]?((\d+(\.\d+)?([eE][\-+]?\d+)?(pi)?)|pi)/, Num::Float | ||
|
||
rule /[\-+]?\d+/, Num::Integer | ||
|
||
rule /\$(\\.|.)/, Str::Char | ||
|
||
rule /"([^\\"]|\\.)*"/, Str | ||
|
||
# symbols (single-quote notation) | ||
rule /'([^\\']|\\.)*'/, Str::Other | ||
|
||
# symbols (backslash notation) | ||
rule /\\\w+/, Str::Other | ||
|
||
# symbol arg | ||
rule /[A-Za-z_]\w*:/, Name::Label | ||
|
||
rule /[A-Z]\w*/, Name::Class | ||
|
||
# primitive | ||
rule /_\w+/, Name::Function | ||
|
||
# main identifiers section | ||
rule /[a-z]\w*/ do |m| | ||
if self.class.keywords.include? m[0] | ||
token Keyword | ||
elsif self.class.constants.include? m[0] | ||
token Keyword::Constant | ||
elsif self.class.reserved.include? m[0] | ||
token Keyword::Reserved | ||
else | ||
token Name | ||
end | ||
end | ||
|
||
# environment variables | ||
rule /~\w+/, Name::Variable::Global | ||
|
||
rule /[\{\}()\[\];,\.]/, Punctuation | ||
|
||
# operators. treat # (array unpack) as an operator | ||
rule /[\+\-\*\/&\|%<>=]+/, Operator | ||
rule /[\^:#]/, Operator | ||
|
||
# treat curry argument as a special operator | ||
rule /\b_\b/, Name::Builtin | ||
end | ||
end | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# -*- coding: utf-8 -*- # | ||
# frozen_string_literal: true | ||
|
||
describe Rouge::Lexers::SuperCollider do | ||
let(:subject) { Rouge::Lexers::SuperCollider.new } | ||
|
||
describe 'guessing' do | ||
include Support::Guessing | ||
|
||
it 'guesses by filename' do | ||
assert_guess :filename => 'foo.scd' | ||
assert_guess :filename => 'foo.sc', :source => '~x = 3' | ||
assert_guess :filename => 'foo.sc', :source => '0;' | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
// modulate a sine frequency and a noise amplitude with another sine | ||
// whose frequency depends on the horizontal mouse pointer position | ||
~myFunction = { | ||
var x = SinOsc.ar(MouseX.kr(1, 100)); | ||
SinOsc.ar(300 * x + 800, 0, 0.1) | ||
+ | ||
PinkNoise.ar(0.1 * x + 0.1) | ||
}; | ||
|
||
~myFunction.play; | ||
"that's all, folks!".postln; | ||
|
||
/* Test Class */ | ||
Collection { | ||
*newFrom { | aCollection | | ||
var newCollection = this.new(aCollection.size); | ||
aCollection.do {| item | newCollection.add(item) }; | ||
^newCollection | ||
} | ||
*with { | ... args | | ||
var newColl; | ||
// answer a collection of my class of the given arguments | ||
// the class Array has a simpler implementation | ||
newColl = this.new(args.size); | ||
newColl.addAll(args); | ||
^newColl | ||
} | ||
*fill { | size, function | | ||
var obj; | ||
if(size.isSequenceableCollection) { ^this.fillND(size, function) }; | ||
obj = this.new(size); | ||
size.do { | i | | ||
obj.add(function.value(i)); | ||
}; | ||
^obj | ||
} | ||
*fill2D { | rows, cols, function | | ||
var obj = this.new(rows); | ||
rows.do { |row| | ||
var obj2 = this.new(cols); | ||
cols.do { |col| | ||
obj2 = obj2.add(function.value(row, col)) | ||
}; | ||
obj = obj.add(obj2); | ||
}; | ||
^obj | ||
} | ||
*fill3D { | planes, rows, cols, function | | ||
var obj = this.new(planes); | ||
planes.do { |plane| | ||
var obj2 = this.new(rows); | ||
rows.do { |row| | ||
var obj3 = this.new(cols); | ||
cols.do { |col| | ||
obj3 = obj3.add(function.value(plane, row, col)) | ||
}; | ||
obj2 = obj2.add(obj3); | ||
}; | ||
obj = obj.add(obj2); | ||
}; | ||
^obj | ||
} | ||
|
||
// from Array | ||
mirror2 { | ||
_ArrayMirror2 | ||
^this.primitiveFailed | ||
} | ||
} | ||
|
||
// class inheritance | ||
MyClass : Object { | ||
classvar <>decorations; | ||
var <>igloos; | ||
const magicNumber = 3; | ||
} | ||
|
||
/* This is a multiline comment | ||
/* With nesting! */ | ||
End of multiline comment */ | ||
|
||
~environmentVariable = { 3.do(_.postln) }; | ||
|
||
~environmentVariable.value(); | ||
|
||
~myFunction = { arg size, offset, freq; | ||
postln("Bye!"); | ||
}; | ||
|
||
// function calls with symbol arguments: | ||
s.boot(startAliveThread: false, recover: true); | ||
Array.geom(size: 5, start: 1, grow: 8); | ||
[ 1, 8, 64, 512, 4096 ].collect { arg n; | ||
n.squared | ||
}; | ||
|
||
// array unpacking | ||
#a, b, c = [1, 4, 9]; | ||
|
||
// some numbers | ||
// integers | ||
0; | ||
10; | ||
33; | ||
-235; | ||
0x15; | ||
0x159abcdef; | ||
0x159ABCDEF; | ||
2r01; | ||
8r711; | ||
36rabczyblkgh; | ||
36rAZ19GH; | ||
|
||
// floats | ||
0.0; | ||
0.0e5; | ||
1e5; | ||
1e-5; | ||
0.5e-5; | ||
10r2034.5; | ||
36r2358.ABCDZ; | ||
-36r2358.ABCDZ; | ||
15pi; | ||
15 pi; | ||
-10 pi; | ||
-10pi; | ||
-1.5e7pi; | ||
|
||
// strings | ||
"abcdefg hijklmnop"; | ||
"\"quoted\""; | ||
"\ttabbed\t"; | ||
"\\\ttabbed with backslashes\t\\"; | ||
|
||
// symbols | ||
\abc; | ||
\a1; | ||
\a_symbol; | ||
'a symbol'; | ||
'a cl3v3r\'_symb0l"'; | ||
|
||
// characters | ||
$a; | ||
$ ; | ||
$b; | ||
$0; | ||
$$; | ||
$\t; | ||
$\0; | ||
$\&; | ||
$\\; | ||
|
||
// curry argument vs underscore in name | ||
[1, 2, 3].do(_.postln); | ||
[1, 2, 3].collect(_ + _); | ||
variable_name.function_name(Class_Name); | ||
|
||
// comments | ||
/* abc */ notComment; | ||
/* /* */ comment */ notComment; | ||
/* /* * */ comment */ notComment; | ||
/* /*/ comment */ comment */ notComment; | ||
/* /**/ comment */ notComment; | ||
// /* | ||
notComment; | ||
// */ |