@devaloop/devalang 0.0.1-alpha.1 → 0.0.1-alpha.3
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.
- package/.devalang +4 -0
- package/Cargo.toml +46 -45
- package/README.md +35 -10
- package/docs/CHANGELOG.md +58 -0
- package/docs/COMMANDS.md +29 -6
- package/docs/CONFIG.md +28 -0
- package/docs/ROADMAP.md +6 -2
- package/docs/TODO.md +21 -18
- package/examples/exported.deva +1 -1
- package/examples/index.deva +2 -1
- package/out-tsc/bin/devalang.exe +0 -0
- package/package.json +2 -3
- package/project-version.json +4 -4
- package/rust/cli/build.rs +99 -29
- package/rust/cli/check.rs +95 -103
- package/rust/cli/init.rs +77 -0
- package/rust/cli/mod.rs +174 -1
- package/rust/cli/template.rs +56 -0
- package/rust/config/loader.rs +14 -0
- package/rust/config/mod.rs +15 -0
- package/rust/core/builder/mod.rs +21 -27
- package/rust/core/debugger/lexer.rs +12 -0
- package/rust/core/debugger/mod.rs +12 -49
- package/rust/core/debugger/preprocessor.rs +23 -0
- package/rust/core/error/mod.rs +60 -0
- package/rust/core/lexer/handler/at.rs +21 -0
- package/rust/core/lexer/handler/brace.rs +41 -0
- package/rust/core/lexer/handler/colon.rs +21 -0
- package/rust/core/lexer/handler/comment.rs +30 -0
- package/rust/core/lexer/handler/dot.rs +21 -0
- package/rust/core/lexer/handler/equal.rs +32 -0
- package/rust/core/lexer/handler/identifier.rs +38 -0
- package/rust/core/lexer/handler/indent.rs +52 -0
- package/rust/core/lexer/handler/mod.rs +238 -0
- package/rust/core/lexer/handler/newline.rs +19 -0
- package/rust/core/lexer/handler/number.rs +31 -0
- package/rust/core/lexer/handler/string.rs +66 -0
- package/rust/core/lexer/mod.rs +16 -324
- package/rust/core/lexer/token.rs +55 -0
- package/rust/core/mod.rs +5 -2
- package/rust/core/parser/handler/at.rs +166 -0
- package/rust/core/parser/handler/bank.rs +38 -0
- package/rust/core/parser/handler/dot.rs +112 -0
- package/rust/core/parser/handler/identifier.rs +134 -0
- package/rust/core/parser/handler/loop_.rs +55 -0
- package/rust/core/parser/handler/mod.rs +6 -0
- package/rust/core/parser/handler/tempo.rs +47 -0
- package/rust/core/parser/mod.rs +204 -166
- package/rust/core/parser/statement.rs +91 -0
- package/rust/core/preprocessor/loader.rs +105 -0
- package/rust/core/preprocessor/mod.rs +2 -24
- package/rust/core/preprocessor/module.rs +37 -56
- package/rust/core/preprocessor/processor.rs +41 -0
- package/rust/core/preprocessor/resolver.rs +372 -0
- package/rust/core/shared/duration.rs +8 -0
- package/rust/core/shared/mod.rs +2 -0
- package/rust/core/shared/value.rs +18 -0
- package/rust/core/store/export.rs +28 -0
- package/rust/core/store/global.rs +39 -0
- package/rust/core/store/import.rs +28 -0
- package/rust/core/store/mod.rs +4 -0
- package/rust/core/store/variable.rs +28 -0
- package/rust/core/utils/mod.rs +2 -0
- package/rust/core/utils/validation.rs +35 -0
- package/rust/lib.rs +0 -1
- package/rust/main.rs +39 -30
- package/rust/utils/file.rs +35 -0
- package/rust/utils/logger.rs +69 -34
- package/rust/utils/mod.rs +3 -2
- package/rust/utils/watcher.rs +25 -0
- package/templates/minimal/.devalang +4 -0
- package/templates/minimal/src/index.deva +2 -0
- package/templates/welcome/.devalang +4 -0
- package/templates/welcome/README.md +185 -0
- package/templates/welcome/samples/kick-808.wav +0 -0
- package/templates/welcome/src/index.deva +13 -0
- package/templates/welcome/src/variables.deva +5 -0
- package/rust/audio/mod.rs +0 -1
- package/rust/cli/new.rs +0 -1
- package/rust/core/parser/at.rs +0 -142
- package/rust/core/parser/bank.rs +0 -42
- package/rust/core/parser/dot.rs +0 -107
- package/rust/core/parser/identifer.rs +0 -91
- package/rust/core/parser/loop_.rs +0 -62
- package/rust/core/parser/tempo.rs +0 -42
- package/rust/core/parser/variable.rs +0 -129
- package/rust/core/preprocessor/dependencies.rs +0 -54
- package/rust/core/preprocessor/resolver/at.rs +0 -24
- package/rust/core/preprocessor/resolver/bank.rs +0 -59
- package/rust/core/preprocessor/resolver/loop_.rs +0 -82
- package/rust/core/preprocessor/resolver/mod.rs +0 -113
- package/rust/core/preprocessor/resolver/tempo.rs +0 -70
- package/rust/core/preprocessor/resolver/trigger.rs +0 -176
- package/rust/core/types/cli.rs +0 -160
- package/rust/core/types/mod.rs +0 -7
- package/rust/core/types/module.rs +0 -41
- package/rust/core/types/parser.rs +0 -73
- package/rust/core/types/statement.rs +0 -105
- package/rust/core/types/store.rs +0 -116
- package/rust/core/types/token.rs +0 -83
- package/rust/core/types/variable.rs +0 -32
- package/rust/runner/executer.rs +0 -44
- package/rust/runner/mod.rs +0 -1
- /package/rust/{utils → core/utils}/path.rs +0 -0
- /package/rust/utils/{loader.rs → spinner.rs} +0 -0
package/rust/cli/check.rs
CHANGED
|
@@ -1,124 +1,116 @@
|
|
|
1
|
-
use std::{ thread, time::Duration };
|
|
2
|
-
|
|
3
1
|
use crate::{
|
|
2
|
+
config::Config,
|
|
4
3
|
core::{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
statement::{
|
|
9
|
-
Statement,
|
|
10
|
-
StatementIterator,
|
|
11
|
-
StatementKind,
|
|
12
|
-
StatementResolved,
|
|
13
|
-
StatementResolvedValue,
|
|
14
|
-
},
|
|
15
|
-
variable::VariableValue,
|
|
16
|
-
},
|
|
4
|
+
preprocessor::loader::ModuleLoader,
|
|
5
|
+
store::global::GlobalStore,
|
|
6
|
+
utils::path::{ find_entry_file, normalize_path },
|
|
17
7
|
},
|
|
18
|
-
|
|
19
|
-
utils::{ loader::with_spinner, logger::log_message, path::{ find_entry_file, normalize_path } },
|
|
8
|
+
utils::{ logger::{ LogLevel, Logger }, spinner::with_spinner, watcher::watch_directory },
|
|
20
9
|
};
|
|
10
|
+
use std::{ thread, time::Duration };
|
|
21
11
|
|
|
22
|
-
pub fn handle_check_command(
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
pub fn handle_check_command(
|
|
13
|
+
config: Option<Config>,
|
|
14
|
+
entry: Option<String>,
|
|
15
|
+
output: Option<String>,
|
|
16
|
+
watch: bool
|
|
17
|
+
) {
|
|
18
|
+
let fetched_entry = if entry.is_none() {
|
|
19
|
+
config
|
|
20
|
+
.as_ref()
|
|
21
|
+
.and_then(|c| c.defaults.entry.clone())
|
|
22
|
+
.unwrap_or_else(|| "".to_string())
|
|
23
|
+
} else {
|
|
24
|
+
entry.clone().unwrap_or_else(|| "".to_string())
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let fetched_output = if output.is_none() {
|
|
28
|
+
config
|
|
29
|
+
.as_ref()
|
|
30
|
+
.and_then(|c| c.defaults.output.clone())
|
|
31
|
+
.unwrap_or_else(|| "".to_string())
|
|
32
|
+
} else {
|
|
33
|
+
output.clone().unwrap_or_else(|| "".to_string())
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let fetched_watch = if watch {
|
|
37
|
+
watch
|
|
38
|
+
} else {
|
|
39
|
+
config
|
|
40
|
+
.as_ref()
|
|
41
|
+
.and_then(|c| c.defaults.watch)
|
|
42
|
+
.unwrap_or(false)
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
let logger = Logger::new();
|
|
46
|
+
|
|
47
|
+
if fetched_entry.is_empty() {
|
|
48
|
+
logger.log_message(
|
|
49
|
+
LogLevel::Error,
|
|
50
|
+
"Entry path is not specified. Please provide a valid entry path."
|
|
51
|
+
);
|
|
25
52
|
std::process::exit(1);
|
|
26
|
-
}
|
|
53
|
+
}
|
|
54
|
+
if fetched_output.is_empty() {
|
|
55
|
+
logger.log_message(
|
|
56
|
+
LogLevel::Error,
|
|
57
|
+
"Output directory is not specified. Please provide a valid output directory."
|
|
58
|
+
);
|
|
59
|
+
std::process::exit(1);
|
|
60
|
+
}
|
|
27
61
|
|
|
28
|
-
let
|
|
29
|
-
|
|
62
|
+
let entry_file = find_entry_file(&fetched_entry).unwrap_or_else(|| {
|
|
63
|
+
logger.log_message(
|
|
64
|
+
LogLevel::Error,
|
|
65
|
+
&format!("❌ index.deva not found in directory: {}", fetched_entry)
|
|
66
|
+
);
|
|
67
|
+
std::process::exit(1);
|
|
30
68
|
});
|
|
31
69
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
let normalized_output_dir = normalize_path(&output);
|
|
36
|
-
|
|
37
|
-
let global_store = load_all_modules(&normalized_entry_file);
|
|
70
|
+
// SECTION Begin check
|
|
71
|
+
if fetched_watch {
|
|
72
|
+
begin_check(entry_file.clone(), fetched_output.clone());
|
|
38
73
|
|
|
39
|
-
|
|
40
|
-
|
|
74
|
+
logger.log_message(
|
|
75
|
+
LogLevel::Watcher,
|
|
76
|
+
&format!("Watching for changes in '{}'...", fetched_entry)
|
|
77
|
+
);
|
|
41
78
|
|
|
42
|
-
|
|
79
|
+
watch_directory(entry_file.clone(), move || {
|
|
80
|
+
logger.log_message(LogLevel::Watcher, "Detected changes, re-checking...");
|
|
43
81
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
82
|
+
begin_check(entry_file.clone(), fetched_output.clone());
|
|
83
|
+
}).unwrap();
|
|
84
|
+
} else {
|
|
85
|
+
begin_check(entry_file.clone(), fetched_output.clone());
|
|
86
|
+
}
|
|
87
|
+
}
|
|
47
88
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
89
|
+
fn begin_check(entry: String, output: String) {
|
|
90
|
+
let spinner = with_spinner("Checking...", || {
|
|
91
|
+
thread::sleep(Duration::from_millis(800));
|
|
92
|
+
});
|
|
51
93
|
|
|
52
|
-
|
|
53
|
-
let warning_message = format!(
|
|
54
|
-
"Check completed with errors in {:.2?}. Output files written to: '{}'",
|
|
55
|
-
duration.elapsed(),
|
|
56
|
-
normalized_output_dir
|
|
57
|
-
);
|
|
94
|
+
let duration = std::time::Instant::now();
|
|
58
95
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let success_message = format!(
|
|
62
|
-
"Check completed successfully in {:.2?}. Output files written to: '{}'",
|
|
63
|
-
duration.elapsed(),
|
|
64
|
-
normalized_output_dir
|
|
65
|
-
);
|
|
96
|
+
let normalized_entry_file = normalize_path(&entry);
|
|
97
|
+
let normalized_output_dir = normalize_path(&output);
|
|
66
98
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
}
|
|
99
|
+
let mut global_store = GlobalStore::new();
|
|
100
|
+
let module_loader = ModuleLoader::new(&normalized_entry_file, &normalized_output_dir);
|
|
71
101
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
StatementResolvedValue::Map(map) => {
|
|
77
|
-
for (key, value) in map {
|
|
78
|
-
if match_error_recursively_resolved_value(&value) {
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
StatementResolvedValue::Array(array) => {
|
|
85
|
-
for item in array {
|
|
86
|
-
if match_error_recursively_resolved(&item) {
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
_ => {
|
|
93
|
-
if let StatementKind::Error = stmt.kind {
|
|
94
|
-
eprintln!("❌ Error found in statement: {:?}", stmt);
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
102
|
+
// SECTION Load
|
|
103
|
+
// NOTE: We don't use modules in the check command, but we still need to load them
|
|
104
|
+
let modules = module_loader.load_all(&mut global_store);
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
}
|
|
106
|
+
// TODO: Implement debugging
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
StatementResolvedValue::Array(array) => {
|
|
114
|
-
for item in array {
|
|
115
|
-
if match_error_recursively_resolved(item) {
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
_ => {}
|
|
121
|
-
}
|
|
108
|
+
let success_message = format!(
|
|
109
|
+
"Check completed successfully in {:.2?}. Output files written to: '{}'",
|
|
110
|
+
duration.elapsed(),
|
|
111
|
+
normalized_output_dir
|
|
112
|
+
);
|
|
122
113
|
|
|
123
|
-
|
|
114
|
+
let logger = Logger::new();
|
|
115
|
+
logger.log_message(LogLevel::Success, &success_message);
|
|
124
116
|
}
|
package/rust/cli/init.rs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
use std::{ fs, path::Path };
|
|
2
|
+
use include_dir::{ include_dir, Dir };
|
|
3
|
+
use inquire::{ Select, Confirm };
|
|
4
|
+
|
|
5
|
+
use crate::{ cli::template::get_available_templates, utils::file::copy_dir_recursive };
|
|
6
|
+
|
|
7
|
+
static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates");
|
|
8
|
+
|
|
9
|
+
pub fn handle_init_command(name: Option<String>, template: Option<String>) {
|
|
10
|
+
let current_dir = std::env::current_dir().unwrap();
|
|
11
|
+
let project_name = name
|
|
12
|
+
.clone()
|
|
13
|
+
.unwrap_or_else(|| {
|
|
14
|
+
current_dir.file_name().unwrap_or_default().to_string_lossy().to_string()
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Select a template if not provided
|
|
18
|
+
let selected_template = template.unwrap_or_else(|| {
|
|
19
|
+
Select::new("Select a template for your project:", get_available_templates())
|
|
20
|
+
.prompt()
|
|
21
|
+
.unwrap_or_else(|_| {
|
|
22
|
+
eprintln!("No template selected. Exiting...");
|
|
23
|
+
std::process::exit(1);
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if selected_template.is_empty() {
|
|
28
|
+
eprintln!("Template cannot be empty.");
|
|
29
|
+
std::process::exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if name.is_none() {
|
|
33
|
+
// Case of initialization in the current directory
|
|
34
|
+
if fs::read_dir(¤t_dir).unwrap().next().is_some() {
|
|
35
|
+
let confirm = Confirm::new(
|
|
36
|
+
"The current directory is not empty. Do you want to continue?"
|
|
37
|
+
)
|
|
38
|
+
.with_default(false)
|
|
39
|
+
.prompt()
|
|
40
|
+
.unwrap_or(false);
|
|
41
|
+
|
|
42
|
+
if !confirm {
|
|
43
|
+
eprintln!("Operation cancelled by the user.");
|
|
44
|
+
std::process::exit(0);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
scaffold_project_current_dir(current_dir.as_path(), selected_template);
|
|
49
|
+
println!(
|
|
50
|
+
"✅ Initialized '{}' project in current directory: {}",
|
|
51
|
+
project_name,
|
|
52
|
+
current_dir.display()
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
// Case of initialization in a new directory
|
|
56
|
+
let target_path = current_dir.join(&project_name);
|
|
57
|
+
|
|
58
|
+
if target_path.exists() {
|
|
59
|
+
eprintln!("❌ A folder named '{}' already exists.", project_name);
|
|
60
|
+
std::process::exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fs::create_dir_all(&target_path).expect("Error creating project directory");
|
|
64
|
+
|
|
65
|
+
scaffold_project_current_dir(&target_path, selected_template);
|
|
66
|
+
println!("✅ Initialized '{}' project in: {}", project_name, target_path.display());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fn scaffold_project_current_dir(path: &Path, template: String) {
|
|
71
|
+
let template_dir = TEMPLATES_DIR.get_dir(&template).unwrap_or_else(|| {
|
|
72
|
+
eprintln!("❌ The template '{}' doesn't exist.", template);
|
|
73
|
+
std::process::exit(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
copy_dir_recursive(template_dir, path, &template_dir.path());
|
|
77
|
+
}
|
package/rust/cli/mod.rs
CHANGED
|
@@ -1,3 +1,176 @@
|
|
|
1
1
|
pub mod check;
|
|
2
|
-
pub mod new;
|
|
3
2
|
pub mod build;
|
|
3
|
+
pub mod init;
|
|
4
|
+
pub mod template;
|
|
5
|
+
|
|
6
|
+
use clap::{ Parser, Subcommand };
|
|
7
|
+
use crate::utils::version::get_version;
|
|
8
|
+
|
|
9
|
+
#[derive(Parser)]
|
|
10
|
+
#[command(name = "devalang")]
|
|
11
|
+
#[command(author = "Devaloop")]
|
|
12
|
+
#[command(version = get_version())]
|
|
13
|
+
#[command(about = "🦊 Devalang – A programming language for music and sound.")]
|
|
14
|
+
pub struct Cli {
|
|
15
|
+
#[arg(long, global = true)]
|
|
16
|
+
/// Skips loading the configuration file.
|
|
17
|
+
pub no_config: bool,
|
|
18
|
+
|
|
19
|
+
#[command(subcommand)]
|
|
20
|
+
pub command: Commands,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[derive(Subcommand)]
|
|
24
|
+
pub enum TemplateCommand {
|
|
25
|
+
/// Lists all available templates for Devalang projects.
|
|
26
|
+
List,
|
|
27
|
+
/// Displays information about a specific template.
|
|
28
|
+
Info {
|
|
29
|
+
name: String,
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[derive(Subcommand)]
|
|
34
|
+
pub enum Commands {
|
|
35
|
+
/// Create a new Devalang project.
|
|
36
|
+
///
|
|
37
|
+
/// ### Arguments
|
|
38
|
+
/// - `name` - The name of the project to create.
|
|
39
|
+
/// - `template` - The template to use for the project. Defaults to "default".
|
|
40
|
+
///
|
|
41
|
+
/// ### Example
|
|
42
|
+
/// ```bash
|
|
43
|
+
/// devalang init --name my_project --template default
|
|
44
|
+
///
|
|
45
|
+
Init {
|
|
46
|
+
#[arg(short, long)]
|
|
47
|
+
/// The optional name (directory) of the project to create.
|
|
48
|
+
name: Option<String>,
|
|
49
|
+
|
|
50
|
+
#[arg(short, long)]
|
|
51
|
+
/// The template to use for the project.
|
|
52
|
+
///
|
|
53
|
+
/// ### Default value
|
|
54
|
+
/// - `default`
|
|
55
|
+
///
|
|
56
|
+
template: Option<String>,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
Template {
|
|
60
|
+
#[command(subcommand)]
|
|
61
|
+
/// The template command to execute.
|
|
62
|
+
command: TemplateCommand,
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/// Build the program and generate output files.
|
|
66
|
+
///
|
|
67
|
+
/// ### Arguments
|
|
68
|
+
/// - `entry` - The entry point of the program to build. Defaults to "./src".
|
|
69
|
+
/// - `output` - The directory where the output files will be generated. Defaults to "./output".
|
|
70
|
+
/// - `watch` - Whether to watch for changes and rebuild. Defaults to "true".
|
|
71
|
+
///
|
|
72
|
+
/// ### Example
|
|
73
|
+
/// ```bash
|
|
74
|
+
/// devalang build --entry ./src --output ./output --watch true
|
|
75
|
+
/// ```
|
|
76
|
+
///
|
|
77
|
+
Build {
|
|
78
|
+
#[arg(short, long)]
|
|
79
|
+
/// The entry point of the program to build.
|
|
80
|
+
///
|
|
81
|
+
entry: Option<String>,
|
|
82
|
+
|
|
83
|
+
#[arg(short, long)]
|
|
84
|
+
/// The directory where the output files will be generated.
|
|
85
|
+
///
|
|
86
|
+
output: Option<String>,
|
|
87
|
+
|
|
88
|
+
#[arg(long, default_value_t = false)]
|
|
89
|
+
/// Whether to watch for changes and rebuild.
|
|
90
|
+
///
|
|
91
|
+
/// ### Default value
|
|
92
|
+
/// - `false`
|
|
93
|
+
///
|
|
94
|
+
watch: bool,
|
|
95
|
+
|
|
96
|
+
#[arg(long, default_value = "real-time")]
|
|
97
|
+
/// The mode of compilation.
|
|
98
|
+
///
|
|
99
|
+
/// ### Default value
|
|
100
|
+
/// - `real-time`
|
|
101
|
+
///
|
|
102
|
+
/// ### Possible values
|
|
103
|
+
/// - `real-time` - Compiles files as soon as possible.
|
|
104
|
+
/// - `batch` - Compiles files one by one.
|
|
105
|
+
/// - `check` - Analyzes the code without compiling it.
|
|
106
|
+
///
|
|
107
|
+
compilation_mode: String,
|
|
108
|
+
|
|
109
|
+
#[arg(short, long, default_value_t = false)]
|
|
110
|
+
/// Whether to print debug information.
|
|
111
|
+
///
|
|
112
|
+
/// ### Default value
|
|
113
|
+
/// - `false`
|
|
114
|
+
///
|
|
115
|
+
debug: bool,
|
|
116
|
+
|
|
117
|
+
#[arg(short, long, default_value_t = false)]
|
|
118
|
+
/// Whether to compress the output files.
|
|
119
|
+
///
|
|
120
|
+
/// ### Default value
|
|
121
|
+
/// - `false`
|
|
122
|
+
///
|
|
123
|
+
compress: bool,
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
/// Analyze the program for errors and warnings.
|
|
127
|
+
///
|
|
128
|
+
/// ### Arguments
|
|
129
|
+
/// - `entry` - The entry point of the program to analyze. Defaults to "./src".
|
|
130
|
+
/// - `watch` - Whether to watch for changes and re-analyze. Defaults to "true".
|
|
131
|
+
///
|
|
132
|
+
/// ### Example
|
|
133
|
+
/// ```bash
|
|
134
|
+
/// devalang check --entry ./src --watch true --compilation-mode real-time
|
|
135
|
+
/// ```
|
|
136
|
+
Check {
|
|
137
|
+
#[arg(short, long)]
|
|
138
|
+
/// The entry point of the program to analyze.
|
|
139
|
+
///
|
|
140
|
+
entry: Option<String>,
|
|
141
|
+
|
|
142
|
+
#[arg(short, long)]
|
|
143
|
+
/// The directory where the output files will be generated.
|
|
144
|
+
///
|
|
145
|
+
output: Option<String>,
|
|
146
|
+
|
|
147
|
+
#[arg(long, default_value_t = false)]
|
|
148
|
+
/// Whether to watch for changes and re-analyze.
|
|
149
|
+
///
|
|
150
|
+
/// ### Default value
|
|
151
|
+
/// - `false`
|
|
152
|
+
///
|
|
153
|
+
watch: bool,
|
|
154
|
+
|
|
155
|
+
#[arg(short, long, default_value = "real-time")]
|
|
156
|
+
/// The mode of compilation.
|
|
157
|
+
///
|
|
158
|
+
/// ### Default value
|
|
159
|
+
/// - `real-time`
|
|
160
|
+
///
|
|
161
|
+
/// ### Possible values
|
|
162
|
+
/// - `real-time` - Analyzes files as soon as possible.
|
|
163
|
+
/// - `batch` - Analyzes files one by one.
|
|
164
|
+
/// - `check` - Analyzes the code without compiling it.
|
|
165
|
+
///
|
|
166
|
+
compilation_mode: String,
|
|
167
|
+
|
|
168
|
+
#[arg(short, long, default_value_t = false)]
|
|
169
|
+
/// Whether to print debug information.
|
|
170
|
+
///
|
|
171
|
+
/// ### Default value
|
|
172
|
+
/// - `false`
|
|
173
|
+
///
|
|
174
|
+
debug: bool,
|
|
175
|
+
},
|
|
176
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
use include_dir::{ include_dir, Dir, DirEntry };
|
|
2
|
+
|
|
3
|
+
use crate::utils::file::format_file_size;
|
|
4
|
+
|
|
5
|
+
static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates");
|
|
6
|
+
|
|
7
|
+
pub fn handle_template_list_command() {
|
|
8
|
+
let available_templates = get_available_templates();
|
|
9
|
+
|
|
10
|
+
println!("📦 Available templates ({}) :\n", available_templates.len());
|
|
11
|
+
|
|
12
|
+
for dir in available_templates {
|
|
13
|
+
println!("• {}", dir);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
println!("\nUsage : devalang init --name <project-name> --template <template-name>");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
pub fn handle_template_info_command(name: String) {
|
|
20
|
+
let template_dir = TEMPLATES_DIR.get_dir(name.clone()).unwrap_or_else(|| {
|
|
21
|
+
println!("❌ The template '{}' is not found.", name);
|
|
22
|
+
|
|
23
|
+
std::process::exit(1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
let mut file_count = 0;
|
|
27
|
+
let mut dir_count = 0;
|
|
28
|
+
let mut total_size: u64 = 0;
|
|
29
|
+
|
|
30
|
+
fn walk(dir: &Dir, file_count: &mut u32, dir_count: &mut u32, total_size: &mut u64) {
|
|
31
|
+
for entry in dir.entries() {
|
|
32
|
+
match entry {
|
|
33
|
+
DirEntry::File(file) => {
|
|
34
|
+
*file_count += 1;
|
|
35
|
+
*total_size += file.contents().len() as u64;
|
|
36
|
+
}
|
|
37
|
+
DirEntry::Dir(subdir) => {
|
|
38
|
+
*dir_count += 1;
|
|
39
|
+
walk(subdir, file_count, dir_count, total_size);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
walk(template_dir, &mut file_count, &mut dir_count, &mut total_size);
|
|
46
|
+
|
|
47
|
+
println!("📦 Template : {}", name);
|
|
48
|
+
println!("📂 Content : {file_count} file(s), {dir_count} folder(s)");
|
|
49
|
+
println!("💾 Size : {}", format_file_size(total_size));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn get_available_templates() -> Vec<String> {
|
|
53
|
+
TEMPLATES_DIR.dirs()
|
|
54
|
+
.map(|dir| dir.path().file_name().unwrap().to_string_lossy().to_string())
|
|
55
|
+
.collect()
|
|
56
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
use std::{ fs, path::Path };
|
|
2
|
+
|
|
3
|
+
use crate::config::Config;
|
|
4
|
+
|
|
5
|
+
pub fn load_config(path: Option<&Path>) -> Option<Config> {
|
|
6
|
+
let config_path = path.unwrap_or_else(|| Path::new(".devalang"));
|
|
7
|
+
|
|
8
|
+
if config_path.exists() {
|
|
9
|
+
let content = fs::read_to_string(config_path).ok()?;
|
|
10
|
+
toml::from_str(&content).ok()
|
|
11
|
+
} else {
|
|
12
|
+
None
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
pub mod loader;
|
|
2
|
+
|
|
3
|
+
use serde::Deserialize;
|
|
4
|
+
|
|
5
|
+
#[derive(Debug, Deserialize)]
|
|
6
|
+
pub struct Config {
|
|
7
|
+
pub defaults: ConfigDefaults,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
#[derive(Debug, Deserialize)]
|
|
11
|
+
pub struct ConfigDefaults {
|
|
12
|
+
pub entry: Option<String>,
|
|
13
|
+
pub output: Option<String>,
|
|
14
|
+
pub watch: Option<bool>,
|
|
15
|
+
}
|
package/rust/core/builder/mod.rs
CHANGED
|
@@ -1,37 +1,31 @@
|
|
|
1
|
-
use crate::core::
|
|
2
|
-
use std::fs::
|
|
1
|
+
use crate::core::parser::statement::Statement;
|
|
2
|
+
use std::{ collections::HashMap, fs::create_dir_all };
|
|
3
3
|
use std::io::Write;
|
|
4
4
|
|
|
5
|
-
pub
|
|
6
|
-
let mut ast_string = String::new();
|
|
5
|
+
pub struct Builder {}
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
})
|
|
13
|
-
.unwrap_or_else(|err| {
|
|
14
|
-
eprintln!("Error serializing AST: {}", err);
|
|
15
|
-
std::process::exit(1);
|
|
16
|
-
});
|
|
7
|
+
impl Builder {
|
|
8
|
+
pub fn new() -> Self {
|
|
9
|
+
Builder {}
|
|
10
|
+
}
|
|
17
11
|
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
pub fn build_ast(&self, modules: &HashMap<String, Vec<Statement>>) {
|
|
13
|
+
let output_path = "./output";
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
for (name, statements) in modules {
|
|
16
|
+
let formatted_name = name.split("/").last().unwrap_or(name);
|
|
17
|
+
let formatted_name = formatted_name.replace(".deva", "");
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
create_dir_all(format!("{}/ast", output_path)).expect("Failed to create AST directory");
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
21
|
+
let file_path = format!("{}/ast/{}.json", output_path, formatted_name);
|
|
22
|
+
let mut file = std::fs::File::create(file_path).expect("Failed to create AST file");
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
let content = serde_json
|
|
25
|
+
::to_string_pretty(&statements)
|
|
26
|
+
.expect("Failed to serialize AST");
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
file.write_all(content.as_bytes()).expect("Failed to write AST to file");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
37
31
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
use crate::core::{ debugger::Debugger, lexer::token::Token };
|
|
2
|
+
|
|
3
|
+
pub fn write_lexer_log_file(output_dir: &str, file_name: &str, tokens: Vec<Token>) {
|
|
4
|
+
let debugger = Debugger::new();
|
|
5
|
+
let mut content = String::new();
|
|
6
|
+
|
|
7
|
+
for token in tokens {
|
|
8
|
+
content.push_str(&format!("{:?}\n", token));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
debugger.write_log_file(output_dir, file_name, &content);
|
|
12
|
+
}
|