cue_dsl 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # cue_dsl
2
+
3
+ [![pipeline status](https://gitlab.com/nicb/cue_dsl/badges/%{default_branch}/pipeline.svg)](https://gitlab.com/nicb/cue_dsl/-/commits/%{default_branch})
4
+ [![coverage](https://gitlab.com/nicb/cue_dsl/badges/%{default_branch}/coverage.svg)](https://gitlab.com/nicb/cue_dsl/-/commits/%{default_branch})
5
+
6
+ A small Ruby DSL for generating Pure Data `[text sequence]` cue lists for
7
+ live-electronics performances, plus a cheatsheet of every device parameter.
8
+
9
+ The cue sheet is written to **STDOUT**; the cheatsheet to a file.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ gem build cue_dsl.gemspec
15
+ gem install cue_dsl-*.gem
16
+ ```
17
+
18
+ Requires Ruby >= 3.0. Runtime deps: none (stdlib `yaml` only).
19
+
20
+ ## Files needed next to your DSL
21
+
22
+ - `receivers.txt` — one Pd receiver name per line, e.g. `FILTER_cutoff_recv`.
23
+ Lines starting with a lowercase letter that don't match `reset_<name>_recv`
24
+ or one of the known `system_*` receivers are ignored. Lines containing
25
+ `$1` / `$2` templates are skipped.
26
+ - `devices.yml` — static routing for each device:
27
+
28
+ ```yaml
29
+ matrix:
30
+ rows: 9
31
+ cols: 8
32
+
33
+ devices:
34
+ DRY:
35
+ label: "Dry"
36
+ inputs: [0]
37
+ outputs: [0]
38
+ FILTER:
39
+ label: "Filter"
40
+ inputs: [1]
41
+ outputs: [1]
42
+ filters: 12 # enables filter.fc(0..11).cutoff / .Q
43
+ CE:
44
+ label: "Echo Chamber"
45
+ inputs: [2]
46
+ outputs: [2, 3] # two outputs
47
+ OUT:
48
+ label: "Output"
49
+ inputs: [6, 7] # two inputs
50
+ outputs: []
51
+ ```
52
+
53
+ ## Writing cues
54
+
55
+ ```ruby
56
+ # cues.rb
57
+ cue "Intro" do
58
+ comment "Opening — filter sweep in", kind: :gui
59
+ connect DRY >> FILTER >> CE >> OUT
60
+ filter.cutoff 800
61
+ filter.mod_onoff :off
62
+ filter.fc(0).cutoff 400
63
+ rm.mod_freq 2.5
64
+ mtx.at(0, 1) 1
65
+ system.cue_step "go"
66
+ end
67
+
68
+ cue "Climax" do
69
+ connect DRY >> FILTER >> CE >> OUT
70
+ filter.cutoff 3000
71
+ reset :CE
72
+ system.cue_step "pause"
73
+ end
74
+ ```
75
+
76
+ ### DSL verbs
77
+
78
+ - `cue "name" do … end` — one cue. Name is required and non-empty.
79
+ - `connect A >> B >> C …` — installs matrix cells from the static routing.
80
+ Supports 1→1, 1→2 (fan-out), 2→1 (output 1 only), 2→2 (pairwise).
81
+ Any other geometry raises `ConnectionError`.
82
+ - `device.param value` — emits `<receiver> <value>` in the cue sheet.
83
+ - `device.fc(i).param value` — filter-bank parameter (`i` in `0..11`).
84
+ - `mtx.at(r, c) value` — explicit matrix cell write, in source order.
85
+ - `reset :CE` / `:PS` / `:RM` / `:SA` / `:matrix` — emits `reset_<name>_recv bang`.
86
+ - `system.cue_step "go"` — emits `cue_step_recv go`.
87
+ `system.header_message` is **forbidden** — see below.
88
+ - `comment "text"` (or `kind: :gui` for `##`) — passthrough comment.
89
+
90
+ ### Matrix semantics
91
+
92
+ Rows are inputs, columns are outputs. A receiver MTX_<row>_<col>_recv addresses input <row> and output <col>.
93
+ In devices.yml, inputs: lists rows and outputs: lists columns. A link A >> B enables MTX[B_input_row][A_output_col].
94
+
95
+ ### Header & pause
96
+
97
+ Every rendered cue starts with:
98
+
99
+ ```
100
+ reset_matrix_recv bang # if CueDSL.reset_matrix is on
101
+ header_message_recv N\ -\ <cue name> # N is 0-based
102
+ ```
103
+
104
+ Spaces, commas and semicolons in the header are escaped (`\ `, `\,`, `\;`).
105
+
106
+ Every rendered cue ends with a literal `pause` line.
107
+
108
+ `system.header_message` raises an error; the cue name is the source of truth.
109
+
110
+ ## Running
111
+
112
+ ```ruby
113
+ require "cue_dsl"
114
+
115
+ CueDSL.configure do |c|
116
+ c.reset_matrix = true # default true
117
+ c.cheatsheet_path = "cheatsheet.txt" # default nil (no file)
118
+ c.cue_numbers_path = "cue_list_numbers.txt" # default nil (no file)
119
+ end
120
+
121
+ CueDSL.run("cues.rb") # writes cue sheet to $stdout
122
+ ```
123
+
124
+ `CueDSL.cue_numbers_path = "cue_list_numbers.txt"` (default `nil`) writes an index of `<line> <escaped cue name>`,
125
+ one entry per cue, where `<line>` is the 0-based line in the cue sheet where
126
+ the cue starts (the `reset_system_recv` bang line). Only cues rendered with `reset_system: true` appear;
127
+ the file is intended for building a goto mechanism against `[text sequence]`.
128
+
129
+ ## Tests
130
+
131
+ ```sh
132
+ rake test
133
+ ```
134
+
135
+ ## Layout
136
+
137
+ ```
138
+ lib/cue_dsl.rb # Context, DSL verbs, run()
139
+ lib/cue_dsl/device.rb
140
+ lib/cue_dsl/filter_bank.rb
141
+ lib/cue_dsl/matrix.rb
142
+ lib/cue_dsl/connect.rb
143
+ lib/cue_dsl/cue.rb
144
+ lib/cue_dsl/parameter.rb
145
+ lib/cue_dsl/receivers_loader.rb
146
+ lib/cue_dsl/devices_config.rb
147
+ lib/cue_dsl/cheatsheet.rb
148
+ test/** # minitest
149
+ ```
150
+
151
+ ## License
152
+
153
+ ![GNU GPL3](./doc/gplv3.png){width=66} [GNU GPL 3 License](./LICENSE)
data/bin/run.rb ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Run the cue DSL for a performance directory.
5
+ #
6
+ # Usage:
7
+ # ruby -I path/to/cue_dsl/lib path/to/cue_dsl/bin/run.rb [cue_list.rb] > cue_list.txt
8
+ #
9
+ # Run from the performance directory (the one containing cue_list.rb,
10
+ # receivers.txt and devices.yml). All relative paths below are resolved
11
+ # against the current working directory, not against this script.
12
+ #
13
+ # With no argument, defaults to "cue_list.rb".
14
+
15
+ require "cue_dsl"
16
+
17
+ dsl_path = ARGV[0] || "cue_list.rb"
18
+
19
+ unless File.exist?(dsl_path)
20
+ warn "run.rb: DSL file not found: #{dsl_path}"
21
+ warn "run.rb: usage: ruby -I <lib> bin/run.rb [cue_list.rb] > cue_list.txt"
22
+ exit 1
23
+ end
24
+
25
+ CueDSL.configure do |c|
26
+ c.reset_system = false # default false
27
+ c.cheatsheet_path = "cheatsheet.txt" # default nil (no file)
28
+ c.cue_numbers_path = "cue_list_numbers.txt" # default nil (no file)
29
+ end
30
+
31
+ CueDSL.run(dsl_path) # writes cue sheet to $stdout
@@ -0,0 +1,16 @@
1
+ # lib/cue_dsl/chain.rb
2
+ module CueDSL
3
+ # Built up by Device#> and Chain#>. Passed to Context#connect.
4
+ class Chain
5
+ attr_reader :devices
6
+
7
+ def initialize(devices)
8
+ @devices = devices.freeze
9
+ end
10
+
11
+ def >>(other)
12
+ raise ConnectionError, "cannot append #{other.inspect} to chain" unless other.is_a?(Device)
13
+ Chain.new(@devices + [other])
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ # lib/cue_dsl/cheatsheet.rb
2
+ module CueDSL
3
+ class Cheatsheet
4
+ AUTO_EMITTED = {
5
+ header_message: "auto-emitted from cue name; do not call",
6
+ preview_cue: "auto-emitted from next cue name; do not call"
7
+ }.freeze
8
+
9
+ def self.render(devices:, matrix:, system_params:, reset_verbs:)
10
+ out = []
11
+ devices.each do |dev|
12
+ out << "=== #{dev.name} (#{dev.label}) ==="
13
+ dev.parameters.sort_by { |k, _| k.to_s }.each do |short, param|
14
+ next unless param.kind == :normal
15
+ out << format(" %-24s %-36s %s", short, param.pd_name, "#{dev.name.downcase}.#{short}(value)")
16
+ end
17
+ if dev.filter_count.positive?
18
+ dev.filter_count.times do |i|
19
+ params = dev.filter_fc_params_for(i)
20
+ params.sort_by { |k, _| k.to_s }.each do |name, param|
21
+ out << format(" %-24s %-36s %s",
22
+ "fc(#{i}).#{name}", param.pd_name,
23
+ "#{dev.name.downcase}.fc(#{i}).#{name}(value)")
24
+ end
25
+ end
26
+ end
27
+ out << ""
28
+ end
29
+
30
+ out << "=== MTX (Matrix #{matrix.rows}x#{matrix.cols} — rows=inputs, cols=outputs) ==="
31
+ matrix.rows.times do |r|
32
+ matrix.cols.times do |c|
33
+ out << format(" %-24s %-36s %s",
34
+ "at(#{r},#{c})", "MTX_#{r}_#{c}_recv",
35
+ "mtx.at(#{r}, #{c}, value)")
36
+ end
37
+ end
38
+ out << ""
39
+
40
+ out << "=== system ==="
41
+ system_params.each do |sym, param|
42
+ if AUTO_EMITTED.key?(sym)
43
+ out << format(" %-24s %-36s (%s)", sym, param.pd_name, AUTO_EMITTED[sym])
44
+ else
45
+ out << format(" %-24s %-36s %s", sym, param.pd_name, "system.#{sym}(value)")
46
+ end
47
+ end
48
+ out << ""
49
+
50
+ out << "=== resets ==="
51
+ reset_verbs.each do |sym|
52
+ out << format(" %-24s %-36s %s", sym, "reset_#{sym}_recv", "reset :#{sym}")
53
+ end
54
+ out << ""
55
+
56
+ out.join("\n")
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,28 @@
1
+ # lib/cue_dsl/connect.rb
2
+ module CueDSL
3
+ module Connect
4
+ # Matrix semantics: rows = inputs, cols = outputs.
5
+ # A >> B sets MTX[B_input_row][A_output_col] = 1.
6
+ # Returns [[row, col], ...] of matrix cells to set to 1.
7
+ def self.cells_for(a, b)
8
+ a_out = a.outputs
9
+ b_in = b.inputs
10
+ raise ConnectionError, "#{a.name} has no outputs" if a_out.empty?
11
+ raise ConnectionError, "#{b.name} has no inputs" if b_in.empty?
12
+
13
+ if a_out.length == 1 && b_in.length == 1
14
+ [[b_in[0], a_out[0]]]
15
+ elsif a_out.length == 1 && b_in.length == 2
16
+ [[b_in[0], a_out[0]], [b_in[1], a_out[0]]]
17
+ elsif a_out.length == 2 && b_in.length == 1
18
+ [[b_in[0], a_out[0]]]
19
+ elsif a_out.length == 2 && b_in.length == 2
20
+ [[b_in[0], a_out[0]], [b_in[1], a_out[1]]]
21
+ else
22
+ raise ConnectionError,
23
+ "cannot connect #{a.name}(#{a_out.length} out) >> " \
24
+ "#{b.name}(#{b_in.length} in); unsupported port geometry"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,65 @@
1
+ # lib/cue_dsl/cue.rb
2
+ module CueDSL
3
+ class Cue
4
+ Action = Struct.new(:kind, :payload, keyword_init: true)
5
+ # kind: :param, :matrix, :reset, :system, :comment, :chain
6
+
7
+ attr_reader :name, :line, :devices, :actions, :resets
8
+
9
+ def initialize(name, line, reset_system: nil)
10
+ raise ConfigurationError, "cue name cannot be empty" if name.nil? || name.to_s.strip.empty?
11
+ @name = name.to_s
12
+ @line = line
13
+ @devices = []
14
+ @actions = []
15
+ @resets = []
16
+ @reset_system = reset_system # nil means "use the global default"
17
+ end
18
+
19
+ def add_device(dev)
20
+ @devices << dev unless @devices.include?(dev)
21
+ end
22
+
23
+ def record_param(line); @actions << Action.new(kind: :param, payload: line); end
24
+ def record_matrix(line); @actions << Action.new(kind: :matrix, payload: line); end
25
+ def record_system(line); @actions << Action.new(kind: :system, payload: line); end
26
+ def record_comment(text); @actions << Action.new(kind: :comment, payload: text); end
27
+ def record_reset(line); @actions << Action.new(kind: :reset, payload: line); end
28
+ def record_chain_marker(chain_devices)
29
+ @actions << Action.new(kind: :chain, payload: chain_devices)
30
+ end
31
+
32
+ # preview: nil (no preview line), or a string already formatted as "<n> - <name>".
33
+ def render(index:, matrix:, reset_system: true, header: nil, preview: nil)
34
+ header ||= "#{index} - #{name}"
35
+ out = []
36
+ out << "reset_system_recv bang" if reset_system
37
+ out << "header_message_recv #{escape(header)}"
38
+ out << "preview_cue_recv #{escape(preview)}" unless preview.nil?
39
+
40
+ @actions.each do |a|
41
+ case a.kind
42
+ when :chain
43
+ a.payload.each_cons(2) do |x, y|
44
+ Connect.cells_for(x, y).each { |(r, c)| out << "MTX_#{r}_#{c}_recv 1" }
45
+ end
46
+ else
47
+ out << a.payload
48
+ end
49
+ end
50
+
51
+ out << "pause"
52
+ out
53
+ end
54
+
55
+ def reset_system?
56
+ @reset_system
57
+ end
58
+
59
+ private
60
+
61
+ def escape(str)
62
+ str.gsub(/[ ,;]/) { |m| "\\#{m}" }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,61 @@
1
+ # lib/cue_dsl/device.rb
2
+ module CueDSL
3
+ class Device
4
+ attr_reader :name, :label, :inputs, :outputs, :parameters, :filter_count
5
+
6
+ def initialize(name:, label:, inputs:, outputs:, parameters:, filter_count: 0)
7
+ @name = name
8
+ @label = label
9
+ @inputs = inputs
10
+ @outputs = outputs
11
+ @parameters = parameters
12
+ @filter_count = filter_count
13
+ @filter_bank = {}
14
+ @cue = nil
15
+ @filter_fc_params_by_index = Hash.new { |h, k| h[k] = {} }
16
+
17
+ parameters.each do |short, param|
18
+ next unless param.kind == :normal
19
+ next unless short.is_a?(Symbol) || short.is_a?(String)
20
+ define_singleton_method(short) do |value, ms = nil|
21
+ record_param(param.emit(value, ms))
22
+ end
23
+ end
24
+
25
+ if filter_count.positive?
26
+ define_singleton_method(:fc) do |i|
27
+ unless (0...filter_count).cover?(i)
28
+ raise ArgumentError, "FC index out of range 0..#{filter_count - 1}"
29
+ end
30
+ @filter_bank[i] ||= FilterBank.new(self, i)
31
+ end
32
+ end
33
+ end
34
+
35
+ def >>(other)
36
+ Chain.new([self, other])
37
+ end
38
+
39
+ def filter_fc_params_for(index)
40
+ @filter_fc_params_by_index[index] || {}
41
+ end
42
+
43
+ def register_filter_fc(param)
44
+ name = param.short_name.to_s.sub(/\Afc_#{param.index}_/, "").to_sym
45
+ @filter_fc_params_by_index[param.index][name] = param
46
+ end
47
+
48
+ def bind(cue); @cue = cue; end
49
+ def unbind; @cue = nil; end
50
+
51
+ def record_param(line)
52
+ raise Error, "device #{name} used outside a cue" if @cue.nil?
53
+ @cue.record_param(line)
54
+ end
55
+
56
+ def record_matrix(line)
57
+ raise Error, "matrix used outside a cue" if @cue.nil?
58
+ @cue.record_matrix(line)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ # lib/cue_dsl/devices_config.rb
2
+ require "yaml"
3
+
4
+ module CueDSL
5
+ class DevicesConfig
6
+ Entry = Struct.new(:name, :label, :inputs, :outputs, :filter_count, keyword_init: true)
7
+
8
+ attr_reader :matrix_rows, :matrix_cols, :entries
9
+
10
+ def self.load(path)
11
+ new(YAML.safe_load(File.read(path), permitted_classes: [Symbol]) || {})
12
+ end
13
+
14
+ def initialize(hash)
15
+ @matrix_rows = hash.dig("matrix", "rows") or
16
+ raise ConfigurationError, "devices.yml: missing matrix.rows"
17
+ @matrix_cols = hash.dig("matrix", "cols") or
18
+ raise ConfigurationError, "devices.yml: missing matrix.cols"
19
+ @entries = {}
20
+ (hash["devices"] || {}).each do |name, spec|
21
+ @entries[name] = Entry.new(
22
+ name: name,
23
+ label: spec["label"] || name,
24
+ inputs: Array(spec["inputs"]),
25
+ outputs: Array(spec["outputs"]),
26
+ filter_count: spec["filters"].to_i
27
+ )
28
+ end
29
+ end
30
+
31
+ def resolve(token)
32
+ key = normalize(token)
33
+ return @entries[key] if @entries.key?(key)
34
+ found = @entries.values.find { |e| normalize(e.label) == key }
35
+ return found if found
36
+ raise UnknownDeviceError,
37
+ "unknown device #{token.inspect} (try one of #{@entries.keys.join(', ')})"
38
+ end
39
+
40
+ private
41
+
42
+ def normalize(str)
43
+ str.to_s
44
+ .unicode_normalize(:nfkd)
45
+ .encode("ASCII", invalid: :replace, undef: :replace, replace: "")
46
+ .upcase
47
+ .gsub(/[^\p{Alnum}]+/, "_")
48
+ .sub(/\A_+/, "")
49
+ .sub(/_+\z/, "")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,7 @@
1
+ # lib/cue_dsl/errors.rb
2
+ module CueDSL
3
+ class Error < StandardError; end
4
+ class ConnectionError < Error; end
5
+ class UnknownDeviceError < Error; end
6
+ class ConfigurationError < Error; end
7
+ end
@@ -0,0 +1,17 @@
1
+ # lib/cue_dsl/filter_bank.rb
2
+ module CueDSL
3
+ class FilterBank
4
+ attr_reader :index
5
+
6
+ def initialize(owner, index)
7
+ @owner = owner
8
+ @index = index
9
+ params = owner.filter_fc_params_for(index)
10
+ params.each do |short, param|
11
+ define_singleton_method(short) do |value, ms = nil|
12
+ @owner.record_param(param.emit(value, ms))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ # lib/cue_dsl/matrix.rb
2
+ module CueDSL
3
+ class Matrix < Device
4
+ UNSET = Object.new.freeze
5
+ attr_reader :rows, :cols
6
+
7
+ def initialize(rows:, cols:, parameters:)
8
+ super(name: "MTX", label: "Matrix", inputs: [], outputs: [], parameters: parameters)
9
+ @rows = rows
10
+ @cols = cols
11
+ end
12
+
13
+ # mtx.at(r, c, v) -> records the message immediately
14
+ # mtx.at(r, c).(v) -> defers via CellSetter#call
15
+ def at(row, col, value = UNSET)
16
+ raise ArgumentError, "row #{row} out of range 0..#{rows - 1}" unless (0...rows).cover?(row)
17
+ raise ArgumentError, "col #{col} out of range 0..#{cols - 1}" unless (0...cols).cover?(col)
18
+ return CellSetter.new(self, row, col) if value.equal?(UNSET)
19
+ record_matrix("MTX_#{row}_#{col}_recv #{self.class.format_value(value)}")
20
+ end
21
+
22
+ def self.format_value(v)
23
+ case v
24
+ when Symbol then v.to_s
25
+ when Float then v == v.to_i ? v.to_i.to_s : v.to_s
26
+ else v.to_s
27
+ end
28
+ end
29
+
30
+ class CellSetter
31
+ def initialize(matrix, row, col)
32
+ @matrix, @row, @col = matrix, row, col
33
+ end
34
+
35
+ def set(v)
36
+ @matrix.record_matrix("MTX_#{@row}_#{@col}_recv #{Matrix.format_value(v)}")
37
+ end
38
+ alias_method :call, :set
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ # lib/cue_dsl/parameter.rb
2
+ module CueDSL
3
+ class Parameter
4
+ attr_reader :pd_name, :device_prefix, :short_name, :kind, :index
5
+
6
+ def initialize(pd_name:, device_prefix:, short_name:, kind: :normal, index: nil)
7
+ @pd_name = pd_name
8
+ @device_prefix = device_prefix
9
+ @short_name = short_name
10
+ @kind = kind
11
+ @index = index
12
+ end
13
+
14
+ def emit(value, ms = nil)
15
+ if ms.nil?
16
+ "#{pd_name} #{format_value(value)}"
17
+ else
18
+ "#{pd_name} #{format_value(value)} #{format_value(ms)}"
19
+ end
20
+ end
21
+
22
+ def format_value(value)
23
+ case value
24
+ when Symbol then value.to_s
25
+ when Float
26
+ return value.to_i.to_s if value == value.to_i
27
+ value.to_s
28
+ else
29
+ value.to_s
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,74 @@
1
+ # lib/cue_dsl/receivers_loader.rb
2
+ module CueDSL
3
+ class ReceiversLoader
4
+ RESET_RE = /\Areset_(\w+)_recv\z/
5
+ TEMPLATE_RE = /\$[0-9]/
6
+ SYSTEM_RE = /\A(cue_step|git_describe|header_message|preview_cue|system_init)_recv\z/
7
+ FILTER_FC_RE = /\AFILTER_fc_(\d+)_(\w+)_recv\z/
8
+
9
+ Result = Struct.new(:devices, :matrix_params, :system_params, :reset_verbs, keyword_init: true)
10
+
11
+ def self.load(path)
12
+ new(File.readlines(path, chomp: true)).load
13
+ end
14
+
15
+ def initialize(lines)
16
+ @lines = lines.map(&:strip).reject(&:empty?)
17
+ end
18
+
19
+ def load
20
+ devices = Hash.new { |h, k| h[k] = {} }
21
+ matrix_params = {}
22
+ system_params = {}
23
+ reset_verbs = []
24
+
25
+ @lines.each do |line|
26
+ next if line.match?(TEMPLATE_RE)
27
+
28
+ if (m = RESET_RE.match(line))
29
+ reset_verbs << m[1].to_sym
30
+ next
31
+ end
32
+
33
+ if (m = SYSTEM_RE.match(line))
34
+ system_params[m[1].to_sym] = Parameter.new(
35
+ pd_name: line, device_prefix: "system", short_name: m[1], kind: :system
36
+ )
37
+ next
38
+ end
39
+
40
+ if (m = /\AMTX_(\d+)_(\d+)_recv\z/.match(line))
41
+ r, c = m[1].to_i, m[2].to_i
42
+ matrix_params[[r, c]] = Parameter.new(
43
+ pd_name: line, device_prefix: "MTX", short_name: "#{r}_#{c}"
44
+ )
45
+ next
46
+ end
47
+
48
+ next unless line =~ /\A[A-Z]/
49
+
50
+ if (m = FILTER_FC_RE.match(line))
51
+ idx = m[1].to_i
52
+ name = m[2]
53
+ devices["FILTER"][:"fc_#{idx}_#{name}"] = Parameter.new(
54
+ pd_name: line, device_prefix: "FILTER",
55
+ short_name: "fc_#{idx}_#{name}", kind: :filter_fc, index: idx
56
+ )
57
+ next
58
+ end
59
+
60
+ prefix, *rest = line.sub(/_recv\z/, "").split("_")
61
+ short = rest.join("_")
62
+ next if short.empty?
63
+ devices[prefix][short] = Parameter.new(
64
+ pd_name: line, device_prefix: prefix, short_name: short
65
+ )
66
+ end
67
+
68
+ Result.new(
69
+ devices: devices, matrix_params: matrix_params,
70
+ system_params: system_params, reset_verbs: reset_verbs
71
+ )
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,4 @@
1
+ # lib/cue_dsl/errors.rb
2
+ module CueDSL
3
+ VERSION = "1.0.0"
4
+ end