frausto 0.2.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6175ca1a7aaec33d9d762997e3016e76be2892da7ca7b65585d7cdd8c05b6423
4
+ data.tar.gz: b0e41102e41bbb150a7d6ad48b841054577715b1ca1d2507b73b6ff2793c549f
5
+ SHA512:
6
+ metadata.gz: 2dc4fe2223f141e4283eab56f517dc1611844436fe9a8ef0d60f7924a7e52c6276cc15d385c16873c96c697398e0dd03c949c1fe0a00fbef69efceedbfd6ea5f
7
+ data.tar.gz: 07ef3e6827676dd054b80895b8beb89a4d7394ca5c4e658494fbf3fed320b564b89749d877e8cfa54621aad4f49112bca0f76713874480d6fc597ff313f128c8
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Frausto
2
+
3
+ A Ruby toolkit for Faust DSP: generate Faust code from Ruby, or convert Faust to Ruby.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ gem install frausto
9
+ ```
10
+
11
+ Or add to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'frausto'
15
+ ```
16
+
17
+ ## Tools
18
+
19
+ - **[ruby2faust](ruby2faust.md)** - Ruby DSL that generates Faust DSP code
20
+ - **[faust2ruby](faust2ruby.md)** - Convert Faust DSP code to Ruby DSL
21
+
22
+ ## Quick Example
23
+
24
+ ```ruby
25
+ require 'ruby2faust'
26
+
27
+ code = Ruby2Faust.generate do
28
+ osc(440) >> lp(800) >> gain(0.3)
29
+ end
30
+
31
+ puts code
32
+ # => import("stdfaust.lib");
33
+ # process = (os.osc(440) : fi.lowpass(1, 800) : *(0.3));
34
+ ```
35
+
36
+ ```ruby
37
+ require 'faust2ruby'
38
+
39
+ ruby_code = Faust2Ruby.to_ruby('process = os.osc(440) : *(0.5);')
40
+ # => "osc(440) >> gain(0.5)"
41
+ ```
42
+
43
+ ## License
44
+
45
+ MIT
data/bin/faust2ruby ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "faust2ruby"
8
+
9
+ options = {
10
+ expression_only: false,
11
+ verbose: false
12
+ }
13
+
14
+ parser = OptionParser.new do |opts|
15
+ opts.banner = "Usage: faust2ruby [options] <input.dsp>"
16
+ opts.separator ""
17
+ opts.separator "Convert Faust DSP code to Ruby DSL code compatible with ruby2faust."
18
+ opts.separator ""
19
+ opts.separator "Options:"
20
+
21
+ opts.on("-o", "--output FILE", "Output Ruby file (default: stdout)") do |file|
22
+ options[:output] = file
23
+ end
24
+
25
+ opts.on("-e", "--expression", "Output only process expression (no boilerplate)") do
26
+ options[:expression_only] = true
27
+ end
28
+
29
+ opts.on("-v", "--verbose", "Verbose output (show parsing info)") do
30
+ options[:verbose] = true
31
+ end
32
+
33
+ opts.on("-t", "--tokens", "Show lexer tokens (debug mode)") do
34
+ options[:tokens] = true
35
+ end
36
+
37
+ opts.on("-a", "--ast", "Show AST (debug mode)") do
38
+ options[:ast] = true
39
+ end
40
+
41
+ opts.on("--version", "Show version") do
42
+ puts "faust2ruby #{Faust2Ruby::VERSION}"
43
+ exit
44
+ end
45
+
46
+ opts.on("-h", "--help", "Show this help") do
47
+ puts opts
48
+ exit
49
+ end
50
+ end
51
+
52
+ begin
53
+ parser.parse!
54
+ rescue OptionParser::InvalidOption => e
55
+ warn e.message
56
+ warn parser
57
+ exit 1
58
+ end
59
+
60
+ # Read input
61
+ if ARGV.empty?
62
+ if $stdin.tty?
63
+ warn "Error: No input file specified"
64
+ warn parser
65
+ exit 1
66
+ end
67
+ source = $stdin.read
68
+ input_name = "<stdin>"
69
+ else
70
+ input_path = ARGV[0]
71
+ unless File.exist?(input_path)
72
+ warn "Error: File not found: #{input_path}"
73
+ exit 1
74
+ end
75
+ source = File.read(input_path)
76
+ input_name = input_path
77
+ end
78
+
79
+ begin
80
+ if options[:verbose]
81
+ warn "Parsing #{input_name}..."
82
+ end
83
+
84
+ # Debug: show tokens
85
+ if options[:tokens]
86
+ tokens = Faust2Ruby.tokenize(source)
87
+ puts "Tokens:"
88
+ tokens.each do |token|
89
+ puts " #{token.type.to_s.ljust(12)} #{token.value.inspect.ljust(20)} line #{token.line}, col #{token.column}"
90
+ end
91
+ puts
92
+ end
93
+
94
+ # Debug: show AST
95
+ if options[:ast]
96
+ program = Faust2Ruby.parse(source)
97
+ puts "AST:"
98
+ program.statements.each do |stmt|
99
+ puts " #{stmt.class.name.split('::').last}: #{stmt.inspect[0..100]}..."
100
+ end
101
+ puts
102
+ end
103
+
104
+ # Generate Ruby code
105
+ ruby_code = Faust2Ruby.to_ruby(source, expression_only: options[:expression_only])
106
+
107
+ if options[:output]
108
+ File.write(options[:output], ruby_code)
109
+ warn "Wrote #{options[:output]}" if options[:verbose]
110
+ else
111
+ puts ruby_code
112
+ end
113
+
114
+ rescue Faust2Ruby::ParseError => e
115
+ warn "Parse error: #{e.message}"
116
+ exit 1
117
+ rescue Faust2Ruby::Error => e
118
+ warn "Error: #{e.message}"
119
+ exit 1
120
+ rescue StandardError => e
121
+ warn "Unexpected error: #{e.message}"
122
+ warn e.backtrace.first(5).join("\n") if options[:verbose]
123
+ exit 1
124
+ end
data/bin/ruby2faust ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+ require_relative "../lib/ruby2faust"
6
+
7
+ module Ruby2Faust
8
+ class CLI
9
+ def initialize(args)
10
+ @args = args
11
+ @options = { target: :cpp }
12
+ end
13
+
14
+ def run
15
+ command = @args.shift
16
+
17
+ case command
18
+ when "compile", "c"
19
+ compile_command
20
+ when "run", "r"
21
+ run_command
22
+ when "version", "-v", "--version"
23
+ puts "ruby2faust #{VERSION}"
24
+ when "help", "-h", "--help", nil
25
+ print_help
26
+ else
27
+ puts "Unknown command: #{command}"
28
+ print_help
29
+ exit 1
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def compile_command
36
+ parser = OptionParser.new do |opts|
37
+ opts.banner = "Usage: ruby2faust compile [options] <file.rb>"
38
+
39
+ opts.on("-o", "--output FILE", "Output .dsp file") do |f|
40
+ @options[:output] = f
41
+ end
42
+
43
+ opts.on("-t", "--target TARGET", [:cpp, :wasm, :llvm], "Compilation target (cpp, wasm, llvm)") do |t|
44
+ @options[:target] = t
45
+ end
46
+ end
47
+ parser.parse!(@args)
48
+
49
+ input_file = @args.shift
50
+ unless input_file
51
+ puts "Error: No input file specified"
52
+ puts parser.help
53
+ exit 1
54
+ end
55
+
56
+ unless File.exist?(input_file)
57
+ puts "Error: File not found: #{input_file}"
58
+ exit 1
59
+ end
60
+
61
+ output_file = @options[:output] || input_file.sub(/\.rb$/, ".dsp")
62
+
63
+ # Load and evaluate the Ruby DSL file
64
+ dsl_code = File.read(input_file)
65
+ context = Object.new
66
+ context.extend(DSL)
67
+ process = context.instance_eval(dsl_code, input_file)
68
+
69
+ # Generate Faust code
70
+ faust_code = Emitter.program(process)
71
+ File.write(output_file, faust_code)
72
+
73
+ puts "Generated: #{output_file}"
74
+ end
75
+
76
+ def run_command
77
+ parser = OptionParser.new do |opts|
78
+ opts.banner = "Usage: ruby2faust run [options] <file.rb>"
79
+
80
+ opts.on("-t", "--target TARGET", [:cpp, :wasm], "Run target (cpp, wasm)") do |t|
81
+ @options[:target] = t
82
+ end
83
+ end
84
+ parser.parse!(@args)
85
+
86
+ input_file = @args.shift
87
+ unless input_file
88
+ puts "Error: No input file specified"
89
+ puts parser.help
90
+ exit 1
91
+ end
92
+
93
+ # First compile to .dsp
94
+ @options[:output] = nil
95
+ @args.unshift(input_file)
96
+ compile_command
97
+
98
+ # Then run Faust compiler
99
+ dsp_file = input_file.sub(/\.rb$/, ".dsp")
100
+ puts "Compiling with Faust..."
101
+ Live.faust_compile(dsp_file, target: @options[:target])
102
+ end
103
+
104
+ def print_help
105
+ puts <<~HELP
106
+ ruby2faust - A Ruby DSL for generating Faust DSP code
107
+
108
+ Usage:
109
+ ruby2faust <command> [options] [arguments]
110
+
111
+ Commands:
112
+ compile, c Compile a Ruby DSL file to Faust .dsp
113
+ run, r Compile to .dsp and run Faust compiler
114
+ version Show version
115
+ help Show this help
116
+
117
+ Examples:
118
+ ruby2faust compile synth.rb
119
+ ruby2faust compile -o output.dsp synth.rb
120
+ ruby2faust run --target wasm synth.rb
121
+
122
+ For command-specific help:
123
+ ruby2faust compile --help
124
+ HELP
125
+ end
126
+ end
127
+ end
128
+
129
+ Ruby2Faust::CLI.new(ARGV).run