@devaloop/devalang 0.0.1-alpha.1 โ†’ 0.0.1-alpha.2

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.
Files changed (49) hide show
  1. package/.devalang +4 -0
  2. package/Cargo.toml +3 -2
  3. package/README.md +30 -6
  4. package/docs/CHANGELOG.md +53 -0
  5. package/docs/COMMANDS.md +29 -6
  6. package/docs/CONFIG.md +28 -0
  7. package/docs/ROADMAP.md +1 -1
  8. package/docs/TODO.md +43 -15
  9. package/examples/index.deva +1 -1
  10. package/out-tsc/bin/devalang.exe +0 -0
  11. package/package.json +2 -2
  12. package/project-version.json +3 -3
  13. package/rust/cli/build.rs +58 -5
  14. package/rust/cli/check.rs +73 -17
  15. package/rust/cli/init.rs +77 -0
  16. package/rust/cli/mod.rs +2 -1
  17. package/rust/cli/template.rs +56 -0
  18. package/rust/core/lexer/at.rs +21 -0
  19. package/rust/core/lexer/brace.rs +41 -0
  20. package/rust/core/lexer/bracket.rs +41 -0
  21. package/rust/core/lexer/colon.rs +21 -0
  22. package/rust/core/lexer/comment.rs +30 -0
  23. package/rust/core/lexer/dot.rs +21 -0
  24. package/rust/core/lexer/driver.rs +286 -0
  25. package/rust/core/lexer/equal.rs +32 -0
  26. package/rust/core/lexer/identifier.rs +38 -0
  27. package/rust/core/lexer/indent.rs +47 -0
  28. package/rust/core/lexer/mod.rs +14 -333
  29. package/rust/core/lexer/newline.rs +23 -0
  30. package/rust/core/lexer/number.rs +31 -0
  31. package/rust/core/lexer/quote.rs +61 -0
  32. package/rust/core/parser/dot.rs +48 -18
  33. package/rust/core/preprocessor/module.rs +1 -1
  34. package/rust/core/types/cli.rs +53 -31
  35. package/rust/core/types/config.rs +15 -0
  36. package/rust/core/types/mod.rs +2 -1
  37. package/rust/main.rs +30 -19
  38. package/rust/utils/config.rs +13 -0
  39. package/rust/utils/file.rs +35 -0
  40. package/rust/utils/mod.rs +4 -1
  41. package/rust/utils/watcher.rs +25 -0
  42. package/templates/minimal/.devalang +4 -0
  43. package/templates/minimal/src/index.deva +2 -0
  44. package/templates/welcome/.devalang +4 -0
  45. package/templates/welcome/README.md +185 -0
  46. package/templates/welcome/samples/kick-808.wav +0 -0
  47. package/templates/welcome/src/index.deva +13 -0
  48. package/templates/welcome/src/variables.deva +5 -0
  49. package/rust/cli/new.rs +0 -1
@@ -7,6 +7,10 @@ use crate::utils::version::get_version;
7
7
  #[command(version = get_version())]
8
8
  #[command(about = "๐ŸฆŠ Devalang โ€“ A programming language for music and sound.")]
9
9
  pub struct Cli {
10
+ #[arg(long, global = true)]
11
+ /// Skips loading the configuration file.
12
+ pub no_config: bool,
13
+
10
14
  #[command(subcommand)]
11
15
  pub command: CliCommands,
12
16
  }
@@ -34,6 +38,36 @@ pub enum CompilationMode {
34
38
 
35
39
  #[derive(Subcommand)]
36
40
  pub enum CliCommands {
41
+ /// Create a new Devalang project.
42
+ ///
43
+ /// ### Arguments
44
+ /// - `name` - The name of the project to create.
45
+ /// - `template` - The template to use for the project. Defaults to "default".
46
+ ///
47
+ /// ### Example
48
+ /// ```bash
49
+ /// devalang init --name my_project --template default
50
+ ///
51
+ Init {
52
+ #[arg(short, long)]
53
+ /// The optional name (directory) of the project to create.
54
+ name: Option<String>,
55
+
56
+ #[arg(short, long)]
57
+ /// The template to use for the project.
58
+ ///
59
+ /// ### Default value
60
+ /// - `default`
61
+ ///
62
+ template: Option<String>,
63
+ },
64
+
65
+ Template {
66
+ #[command(subcommand)]
67
+ /// The template command to execute.
68
+ command: CliTemplateCommand,
69
+ },
70
+
37
71
  /// Build the program and generate output files.
38
72
  ///
39
73
  /// ### Arguments
@@ -47,29 +81,23 @@ pub enum CliCommands {
47
81
  /// ```
48
82
  ///
49
83
  Build {
50
- #[arg(short, long, default_value = "./src")]
84
+ #[arg(short, long)]
51
85
  /// The entry point of the program to build.
52
86
  ///
53
- /// ### Default value
54
- /// - `./src`
55
- ///
56
- entry: String,
87
+ entry: Option<String>,
57
88
 
58
- #[arg(short, long, default_value = "./output")]
89
+ #[arg(short, long)]
59
90
  /// The directory where the output files will be generated.
60
91
  ///
61
- /// ### Default value
62
- /// - `./output`
63
- ///
64
- output: String,
92
+ output: Option<String>,
65
93
 
66
- #[arg(short, long, default_value = "true")]
94
+ #[arg(long, default_value_t = false)]
67
95
  /// Whether to watch for changes and rebuild.
68
96
  ///
69
97
  /// ### Default value
70
- /// - `true`
98
+ /// - `false`
71
99
  ///
72
- watch: String,
100
+ watch: bool,
73
101
 
74
102
  #[arg(long, default_value = "real-time")]
75
103
  /// The mode of compilation.
@@ -84,21 +112,21 @@ pub enum CliCommands {
84
112
  ///
85
113
  compilation_mode: String,
86
114
 
87
- #[arg(short, long, default_value = "false")]
115
+ #[arg(short, long, default_value_t = false)]
88
116
  /// Whether to print debug information.
89
117
  ///
90
118
  /// ### Default value
91
119
  /// - `false`
92
120
  ///
93
- debug: String,
121
+ debug: bool,
94
122
 
95
- #[arg(short, long, default_value = "false")]
123
+ #[arg(short, long, default_value_t = false)]
96
124
  /// Whether to compress the output files.
97
125
  ///
98
126
  /// ### Default value
99
127
  /// - `false`
100
128
  ///
101
- compress: String,
129
+ compress: bool,
102
130
  },
103
131
 
104
132
  /// Analyze the program for errors and warnings.
@@ -112,29 +140,23 @@ pub enum CliCommands {
112
140
  /// devalang check --entry ./src --watch true --compilation-mode real-time
113
141
  /// ```
114
142
  Check {
115
- #[arg(short, long, default_value = "./src")]
143
+ #[arg(short, long)]
116
144
  /// The entry point of the program to analyze.
117
145
  ///
118
- /// ### Default value
119
- /// - `./src`
120
- ///
121
- entry: String,
146
+ entry: Option<String>,
122
147
 
123
- #[arg(short, long, default_value = "./output")]
148
+ #[arg(short, long)]
124
149
  /// The directory where the output files will be generated.
125
150
  ///
126
- /// ### Default value
127
- /// - `./output`
128
- ///
129
- output: String,
151
+ output: Option<String>,
130
152
 
131
- #[arg(short, long, default_value = "false")]
153
+ #[arg(long, default_value_t = false)]
132
154
  /// Whether to watch for changes and re-analyze.
133
155
  ///
134
156
  /// ### Default value
135
157
  /// - `false`
136
158
  ///
137
- watch: String,
159
+ watch: bool,
138
160
 
139
161
  #[arg(short, long, default_value = "real-time")]
140
162
  /// The mode of compilation.
@@ -149,12 +171,12 @@ pub enum CliCommands {
149
171
  ///
150
172
  compilation_mode: String,
151
173
 
152
- #[arg(short, long, default_value = "false")]
174
+ #[arg(short, long, default_value_t = false)]
153
175
  /// Whether to print debug information.
154
176
  ///
155
177
  /// ### Default value
156
178
  /// - `false`
157
179
  ///
158
- debug: String,
180
+ debug: bool,
159
181
  },
160
182
  }
@@ -0,0 +1,15 @@
1
+ use serde::Deserialize;
2
+
3
+ #[derive(Debug, Deserialize)]
4
+ pub struct DevalangConfig {
5
+ pub defaults: Defaults,
6
+ }
7
+
8
+ #[derive(Debug, Deserialize)]
9
+ pub struct Defaults {
10
+ pub entry: Option<String>,
11
+
12
+ pub output: Option<String>,
13
+
14
+ pub watch: Option<bool>,
15
+ }
@@ -4,4 +4,5 @@ pub mod variable;
4
4
  pub mod store;
5
5
  pub mod parser;
6
6
  pub mod module;
7
- pub mod cli;
7
+ pub mod cli;
8
+ pub mod config;
package/rust/main.rs CHANGED
@@ -7,36 +7,47 @@ pub mod utils;
7
7
  use std::{ io };
8
8
  use clap::Parser;
9
9
  use crate::{
10
- cli::{ build::handle_build_command, check::handle_check_command },
11
- core::types::cli::{ Cli, CliCommands },
10
+ cli::{
11
+ build::handle_build_command,
12
+ check::handle_check_command,
13
+ init::handle_init_command,
14
+ template::{ handle_template_info_command, handle_template_list_command },
15
+ },
16
+ core::types::{ cli::{ Cli, CliCommands, CliTemplateCommand }, config::DevalangConfig },
17
+ utils::{ config::load_config, logger::log_message },
12
18
  };
13
19
 
14
20
  fn main() -> io::Result<()> {
15
- let cli = Cli::parse();
21
+ let cli: Cli = Cli::parse();
22
+ let mut config: Option<DevalangConfig> = None;
23
+
24
+ if !cli.no_config {
25
+ config = load_config(None);
26
+ } else {
27
+ log_message("Configuration file loading is skipped.", "WARNING");
28
+ }
16
29
 
17
30
  match cli.command {
18
- // TODO - Implement the new command
19
- // CliCommands::New { name, template } => {
20
- // log_message("Command 'new project' is not implemented yet.", "WARNING");
21
- // }
31
+ CliCommands::Init { name, template } => {
32
+ handle_init_command(name, template);
33
+ }
22
34
 
23
- // TODO - Implement the template command
24
- // CliCommands::Template { command } =>
25
- // match command {
26
- // CliTemplateCommand::List => {
27
- // log_message("Command 'template list' is not implemented yet.", "WARNING");
28
- // }
29
- // CliTemplateCommand::Info { name } => {
30
- // log_message("Command 'template info' is not implemented yet.", "WARNING");
31
- // }
32
- // }
35
+ CliCommands::Template { command } =>
36
+ match command {
37
+ CliTemplateCommand::List => {
38
+ handle_template_list_command();
39
+ }
40
+ CliTemplateCommand::Info { name } => {
41
+ handle_template_info_command(name);
42
+ }
43
+ }
33
44
 
34
45
  CliCommands::Build { entry, output, watch, compilation_mode, debug, compress } => {
35
- handle_build_command(entry, output);
46
+ handle_build_command(config, entry, output, watch);
36
47
  }
37
48
 
38
49
  CliCommands::Check { entry, output, watch, compilation_mode, debug } => {
39
- handle_check_command(entry, output);
50
+ handle_check_command(config, entry, output, watch);
40
51
  }
41
52
 
42
53
  // TODO - Implement the play command
@@ -0,0 +1,13 @@
1
+ use std::{ fs, path::Path };
2
+ use crate::core::types::config::DevalangConfig;
3
+
4
+ pub fn load_config(path: Option<&Path>) -> Option<DevalangConfig> {
5
+ let config_path = path.unwrap_or_else(|| Path::new(".devalang"));
6
+
7
+ if config_path.exists() {
8
+ let content = fs::read_to_string(config_path).ok()?;
9
+ toml::from_str(&content).ok()
10
+ } else {
11
+ None
12
+ }
13
+ }
@@ -0,0 +1,35 @@
1
+ use std::{ fs::{ self }, path::Path };
2
+ use include_dir::{ Dir, DirEntry };
3
+
4
+ pub fn copy_dir_recursive(dir: &Dir, target_root: &Path, base_path: &Path) {
5
+ for entry in dir.entries() {
6
+ match entry {
7
+ DirEntry::Dir(subdir) => {
8
+ copy_dir_recursive(subdir, target_root, base_path);
9
+ }
10
+ DirEntry::File(file) => {
11
+ let rel_path = file.path().strip_prefix(base_path).unwrap();
12
+ let dest_path = target_root.join(rel_path);
13
+
14
+ if let Some(parent) = dest_path.parent() {
15
+ fs::create_dir_all(parent).unwrap();
16
+ }
17
+
18
+ fs::write(&dest_path, file.contents()).expect("Error writing file");
19
+ }
20
+ }
21
+ }
22
+ }
23
+
24
+ pub fn format_file_size(bytes: u64) -> String {
25
+ const KB: u64 = 1024;
26
+ const MB: u64 = 1024 * 1024;
27
+
28
+ if bytes >= MB {
29
+ format!("{:.2} Mb", (bytes as f64) / (MB as f64))
30
+ } else if bytes >= KB {
31
+ format!("{:.2} Kb", (bytes as f64) / (KB as f64))
32
+ } else {
33
+ format!("{} bytes", bytes)
34
+ }
35
+ }
package/rust/utils/mod.rs CHANGED
@@ -2,4 +2,7 @@ pub mod version;
2
2
  pub mod signature;
3
3
  pub mod path;
4
4
  pub mod loader;
5
- pub mod logger;
5
+ pub mod logger;
6
+ pub mod file;
7
+ pub mod watcher;
8
+ pub mod config;
@@ -0,0 +1,25 @@
1
+ use notify::{ Watcher, RecursiveMode, Config, RecommendedWatcher };
2
+ use std::sync::mpsc::channel;
3
+
4
+ pub fn watch_directory<F>(entry: String, callback: F) -> notify::Result<()>
5
+ where F: Fn() + Send + 'static
6
+ {
7
+ let (tx, rx) = channel();
8
+
9
+ let mut watcher: RecommendedWatcher = Watcher::new(tx, Config::default())?;
10
+ watcher.watch(&entry.as_ref(), RecursiveMode::Recursive)?;
11
+
12
+ loop {
13
+ match rx.recv() {
14
+ Ok(_) => {
15
+ callback();
16
+ }
17
+ Err(e) => {
18
+ eprintln!("Channel error : {:?}", e);
19
+ break;
20
+ }
21
+ }
22
+ }
23
+
24
+ Ok(())
25
+ }
@@ -0,0 +1,4 @@
1
+ [defaults]
2
+ entry = "./src"
3
+ output = "./output"
4
+ watch = true
@@ -0,0 +1,2 @@
1
+ # Write your Devalang code here
2
+ # ...
@@ -0,0 +1,4 @@
1
+ [defaults]
2
+ entry = "./src"
3
+ output = "./output"
4
+ watch = true
@@ -0,0 +1,185 @@
1
+ <div align="center">
2
+ <img src="https://firebasestorage.googleapis.com/v0/b/devaloop-labs.firebasestorage.app/o/devalang-teal-logo.svg?alt=media&token=d2a5705a-1eba-4b49-88e6-895a761fb7f7" alt="Devalang Logo">
3
+ </div>
4
+
5
+ ![Rust](https://img.shields.io/badge/Made%20with-Rust-orange?logo=rust)
6
+ ![TypeScript](https://img.shields.io/badge/Built%20with-TypeScript-blue?logo=typescript)
7
+ ![Node.js](https://img.shields.io/badge/Node.js-18%2B-brightgreen?logo=node.js)
8
+
9
+ ![Project Status](https://img.shields.io/badge/status-alpha-red)
10
+ ![Version](https://img.shields.io/badge/version-0.0.1-blue)
11
+ ![License: MIT](https://img.shields.io/badge/license-MIT-green)
12
+ ![Platform](https://img.shields.io/badge/platform-Windows-blue)
13
+
14
+ ![npm](https://img.shields.io/npm/dm/@devaloop/devalang)
15
+
16
+ ## ๐ŸŽผ Devalang, by **Devaloop Labs**
17
+
18
+ ๐ŸŽถ Compose music with code โ€” simple, structured, sonic.
19
+
20
+ Devalang is a tiny domain-specific language (DSL) for music makers, sound designers, and audio hackers.
21
+ Compose loops, control samples, and automate parameters โ€” all in clean, readable text.
22
+
23
+ ๐ŸฆŠ Whether you're building a track, shaping textures, or performing live, Devalang helps you think in rhythms. Itโ€™s designed to be simple, expressive, and fast โ€” because your ideas shouldnโ€™t wait.
24
+
25
+ From studio sketches to live sets, Devalang gives you rhythmic control โ€” with the elegance of code.
26
+
27
+ > ๐Ÿšง **v0.0.1-alpha.1 Notice** ๐Ÿšง
28
+ >
29
+ > Devalang is still in early development. This version does not yet include **sound rendering**.
30
+ >
31
+ > You can parse code, generate the AST, and validate syntax โ€” all essential building blocks for the upcoming audio engine.
32
+ >
33
+ > Currently, only `.kick` is included as a built-in trigger.
34
+ > Custom instruments can be defined with `@load`, allowing any sound sample to be triggered with the same syntax.
35
+ >
36
+ > Currently, Devalang CLI is only available for **Windows**.
37
+ > Linux and macOS binaries will be added in future releases via cross-platform builds.
38
+
39
+ ## ๐Ÿš€ Features
40
+
41
+ - ๐Ÿงฉ Module system for importing and exporting variables between files (`@import`, `@export`)
42
+ - ๐Ÿ“œ Structured AST generation for debugging and future compilation
43
+ - ๐Ÿ”ข Basic data types: strings, numbers, booleans, maps, arrays
44
+ - ๐Ÿ‘๏ธ Watch mode for `build` and `check` commands
45
+ - โฑ๏ธ `bpm` assignment for setting tempo
46
+ - ๐Ÿงฑ `bank` declaration to define the instrument set
47
+ - ๐Ÿ” Looping system with fixed repetitions (`loop 4:`)
48
+ - ๐Ÿงช Instruction calls with parameters (e.g. `.kick auto {reverb:10, decay:20}`) for testing pattern syntax
49
+ - ๐Ÿ“„ `let` assignments for storing reusable values
50
+ - ๐Ÿ”„ `@load` assignment to load a sample (.mp3, .wav) to use it as a value
51
+ - ๐Ÿ› ๏ธ CLI tools for syntax checking (`check`), AST output (`build`)
52
+
53
+ ## ๐Ÿ“† Installation
54
+
55
+ ### For users
56
+
57
+ > - โš ๏ธ Requires [Node.js 18+](https://nodejs.org/en/download)
58
+
59
+ Install the package globally (NPM)
60
+
61
+ ```bash
62
+ npm install -g @devaloop/devalang
63
+ ```
64
+
65
+ Usage without install (NPX)
66
+
67
+ ```bash
68
+ npx @devaloop/devalang <command>
69
+ ```
70
+
71
+ ### For contributors
72
+
73
+ > - โš ๏ธ Requires [Node.js 18+](https://nodejs.org/en/download)
74
+ > - โš ๏ธ Requires [Rust 1.70+](https://www.rust-lang.org/learn/get-started#installing-rust)
75
+
76
+ ```bash
77
+ > git clone https://github.com/devaloop-labs/devalang.git
78
+ > cd devalang
79
+ > npm install
80
+ > cargo install --path .
81
+ ```
82
+
83
+ Usage for development (feel free to change arguments in package.json)
84
+
85
+ ```bash
86
+ # For syntax checking test
87
+ npm run rust:dev <command>
88
+ ```
89
+
90
+ ## โ” Usage
91
+
92
+ For more examples, see [docs/COMMANDS.md](./docs/COMMANDS.md)
93
+
94
+ ### Initialize a new project
95
+
96
+ In the current directory
97
+
98
+ ```bash
99
+ devalang init
100
+ ```
101
+
102
+ Or use optional arguments to specify a directory name and a template
103
+
104
+ ```bash
105
+ devalang init --name <project-name> --template <template-name>
106
+ ```
107
+
108
+ ### Checking syntax only and output debug files
109
+
110
+ ```bash
111
+ devalang check --entry <entry-directory> --output <output-directory> --watch
112
+ ```
113
+
114
+ ### Building output file(s) (AST generation for the moment)
115
+
116
+ ```bash
117
+ devalang build --entry <entry-directory> --output <output-directory> --watch
118
+ ```
119
+
120
+ ## โš™๏ธ Configuration
121
+
122
+ You can use a configuration file to set default values for various settings, making it easier to manage your Devalang project.
123
+
124
+ To do this, create a `.devalang` file in the root of your project directory.
125
+
126
+ See [docs/CONFIG.md](./docs/CONFIG.md) for more information.
127
+
128
+ ## ๐Ÿ“„ Syntax example
129
+
130
+ For more examples, see [docs/SYNTAX.md](./docs/SYNTAX.md)
131
+
132
+ ```deva
133
+ # index.deva
134
+
135
+ @import { globalBpm, globalBank, kickDuration } from "global.deva"
136
+
137
+ bpm globalBpm
138
+ # Will declare the tempo at the globalBpm variable beats per minute
139
+
140
+ bank globalBank
141
+ # Will declare a custom instrument bank using the globalBank variable
142
+
143
+ loop 5:
144
+ .kick kickDuration {reverb=50, drive=25}
145
+ # Will play 5 times a kick for the duration of the kickDuration variable with reverb and drive effects
146
+ ```
147
+
148
+ ```deva
149
+ # global.deva
150
+
151
+ let globalBpm = 120
152
+ let globalBank = 808
153
+ let kickDuration = 500
154
+
155
+ @export { globalBpm, globalBank, kickDuration }
156
+ ```
157
+
158
+ ## ๐Ÿงฏ Known issues
159
+
160
+ - No support yet for Audio Engine
161
+ - No support yet for `if`, `else`, `else if` statements
162
+ - No support yet for `@group`, `@pattern`, `@function` statements
163
+ - Nested loops and conditions may not be fully tested
164
+
165
+ ## ๐Ÿงช Roadmap Highlights
166
+
167
+ For more info, see [docs/ROADMAP.md](./docs/ROADMAP.md)
168
+
169
+ - โณ Audio engine integration
170
+ - โณ Other statements (e.g `if`, `@group`, ...)
171
+ - โณ Cross-platform support (Linux, macOS)
172
+ - โณ More built-in instruments (e.g. snare, hi-hat, etc.)
173
+
174
+ ## ๐Ÿ›ก๏ธ License
175
+
176
+ MIT โ€” see [LICENSE](./LICENSE)
177
+
178
+ ## ๐Ÿค Contributing
179
+
180
+ Contributions, bug reports and suggestions are welcome !
181
+ Feel free to open an issue or submit a pull request.
182
+
183
+ ## ๐Ÿ“ข Contact
184
+
185
+ ๐Ÿ“ง [contact@devaloop.com](mailto:contact@devaloop.com)
@@ -0,0 +1,13 @@
1
+ # ๐ŸฆŠ Welcome to Devalang !
2
+ # This is your first Devalang program ?
3
+ # Let's start with a simple example that plays a sample in a loop.
4
+
5
+ @import { bpmVar, bankVar, loopVar } from './variables.deva'
6
+ @load "../samples/kick-808.wav" as sample
7
+
8
+ bpm bpmVar
9
+
10
+ bank bankVar
11
+
12
+ loop loopVar:
13
+ .sample auto
@@ -0,0 +1,5 @@
1
+ let bpmVar = 120
2
+ let bankVar = 808
3
+ let loopVar = 10
4
+
5
+ @export { bpmVar, bankVar, loopVar }
package/rust/cli/new.rs DELETED
@@ -1 +0,0 @@
1
- // TODO Implement the new command to create a new project with a template