@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.
Files changed (105) hide show
  1. package/.devalang +4 -0
  2. package/Cargo.toml +46 -45
  3. package/README.md +35 -10
  4. package/docs/CHANGELOG.md +58 -0
  5. package/docs/COMMANDS.md +29 -6
  6. package/docs/CONFIG.md +28 -0
  7. package/docs/ROADMAP.md +6 -2
  8. package/docs/TODO.md +21 -18
  9. package/examples/exported.deva +1 -1
  10. package/examples/index.deva +2 -1
  11. package/out-tsc/bin/devalang.exe +0 -0
  12. package/package.json +2 -3
  13. package/project-version.json +4 -4
  14. package/rust/cli/build.rs +99 -29
  15. package/rust/cli/check.rs +95 -103
  16. package/rust/cli/init.rs +77 -0
  17. package/rust/cli/mod.rs +174 -1
  18. package/rust/cli/template.rs +56 -0
  19. package/rust/config/loader.rs +14 -0
  20. package/rust/config/mod.rs +15 -0
  21. package/rust/core/builder/mod.rs +21 -27
  22. package/rust/core/debugger/lexer.rs +12 -0
  23. package/rust/core/debugger/mod.rs +12 -49
  24. package/rust/core/debugger/preprocessor.rs +23 -0
  25. package/rust/core/error/mod.rs +60 -0
  26. package/rust/core/lexer/handler/at.rs +21 -0
  27. package/rust/core/lexer/handler/brace.rs +41 -0
  28. package/rust/core/lexer/handler/colon.rs +21 -0
  29. package/rust/core/lexer/handler/comment.rs +30 -0
  30. package/rust/core/lexer/handler/dot.rs +21 -0
  31. package/rust/core/lexer/handler/equal.rs +32 -0
  32. package/rust/core/lexer/handler/identifier.rs +38 -0
  33. package/rust/core/lexer/handler/indent.rs +52 -0
  34. package/rust/core/lexer/handler/mod.rs +238 -0
  35. package/rust/core/lexer/handler/newline.rs +19 -0
  36. package/rust/core/lexer/handler/number.rs +31 -0
  37. package/rust/core/lexer/handler/string.rs +66 -0
  38. package/rust/core/lexer/mod.rs +16 -324
  39. package/rust/core/lexer/token.rs +55 -0
  40. package/rust/core/mod.rs +5 -2
  41. package/rust/core/parser/handler/at.rs +166 -0
  42. package/rust/core/parser/handler/bank.rs +38 -0
  43. package/rust/core/parser/handler/dot.rs +112 -0
  44. package/rust/core/parser/handler/identifier.rs +134 -0
  45. package/rust/core/parser/handler/loop_.rs +55 -0
  46. package/rust/core/parser/handler/mod.rs +6 -0
  47. package/rust/core/parser/handler/tempo.rs +47 -0
  48. package/rust/core/parser/mod.rs +204 -166
  49. package/rust/core/parser/statement.rs +91 -0
  50. package/rust/core/preprocessor/loader.rs +105 -0
  51. package/rust/core/preprocessor/mod.rs +2 -24
  52. package/rust/core/preprocessor/module.rs +37 -56
  53. package/rust/core/preprocessor/processor.rs +41 -0
  54. package/rust/core/preprocessor/resolver.rs +372 -0
  55. package/rust/core/shared/duration.rs +8 -0
  56. package/rust/core/shared/mod.rs +2 -0
  57. package/rust/core/shared/value.rs +18 -0
  58. package/rust/core/store/export.rs +28 -0
  59. package/rust/core/store/global.rs +39 -0
  60. package/rust/core/store/import.rs +28 -0
  61. package/rust/core/store/mod.rs +4 -0
  62. package/rust/core/store/variable.rs +28 -0
  63. package/rust/core/utils/mod.rs +2 -0
  64. package/rust/core/utils/validation.rs +35 -0
  65. package/rust/lib.rs +0 -1
  66. package/rust/main.rs +39 -30
  67. package/rust/utils/file.rs +35 -0
  68. package/rust/utils/logger.rs +69 -34
  69. package/rust/utils/mod.rs +3 -2
  70. package/rust/utils/watcher.rs +25 -0
  71. package/templates/minimal/.devalang +4 -0
  72. package/templates/minimal/src/index.deva +2 -0
  73. package/templates/welcome/.devalang +4 -0
  74. package/templates/welcome/README.md +185 -0
  75. package/templates/welcome/samples/kick-808.wav +0 -0
  76. package/templates/welcome/src/index.deva +13 -0
  77. package/templates/welcome/src/variables.deva +5 -0
  78. package/rust/audio/mod.rs +0 -1
  79. package/rust/cli/new.rs +0 -1
  80. package/rust/core/parser/at.rs +0 -142
  81. package/rust/core/parser/bank.rs +0 -42
  82. package/rust/core/parser/dot.rs +0 -107
  83. package/rust/core/parser/identifer.rs +0 -91
  84. package/rust/core/parser/loop_.rs +0 -62
  85. package/rust/core/parser/tempo.rs +0 -42
  86. package/rust/core/parser/variable.rs +0 -129
  87. package/rust/core/preprocessor/dependencies.rs +0 -54
  88. package/rust/core/preprocessor/resolver/at.rs +0 -24
  89. package/rust/core/preprocessor/resolver/bank.rs +0 -59
  90. package/rust/core/preprocessor/resolver/loop_.rs +0 -82
  91. package/rust/core/preprocessor/resolver/mod.rs +0 -113
  92. package/rust/core/preprocessor/resolver/tempo.rs +0 -70
  93. package/rust/core/preprocessor/resolver/trigger.rs +0 -176
  94. package/rust/core/types/cli.rs +0 -160
  95. package/rust/core/types/mod.rs +0 -7
  96. package/rust/core/types/module.rs +0 -41
  97. package/rust/core/types/parser.rs +0 -73
  98. package/rust/core/types/statement.rs +0 -105
  99. package/rust/core/types/store.rs +0 -116
  100. package/rust/core/types/token.rs +0 -83
  101. package/rust/core/types/variable.rs +0 -32
  102. package/rust/runner/executer.rs +0 -44
  103. package/rust/runner/mod.rs +0 -1
  104. /package/rust/{utils → core/utils}/path.rs +0 -0
  105. /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
- debugger::Debugger,
6
- preprocessor::module::load_all_modules,
7
- types::{
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
- runner::executer::execute_statements,
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(entry: String, output: String) -> () {
23
- let entry_file = find_entry_file(&entry).unwrap_or_else(|| {
24
- eprintln!("❌ index.deva not found in directory: {}", entry);
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 spinner = with_spinner("Checking...", || {
29
- thread::sleep(Duration::from_millis(800));
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
- let duration = std::time::Instant::now();
33
-
34
- let normalized_entry_file = normalize_path(&entry_file);
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
- if let Some(module) = global_store.modules.get(&normalized_entry_file) {
40
- let mut module_clone = module.clone();
74
+ logger.log_message(
75
+ LogLevel::Watcher,
76
+ &format!("Watching for changes in '{}'...", fetched_entry)
77
+ );
41
78
 
42
- let resolved_statements = execute_statements(&mut module_clone);
79
+ watch_directory(entry_file.clone(), move || {
80
+ logger.log_message(LogLevel::Watcher, "Detected changes, re-checking...");
43
81
 
44
- let debugger = Debugger::new(&module_clone);
45
- let debug_dir = format!("{}/debug/", normalized_output_dir.clone());
46
- debugger.write_files(debug_dir.as_str(), resolved_statements.clone());
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
- let has_errors = resolved_statements.iter().any(|stmt| {
49
- match_error_recursively_resolved(&stmt.clone())
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
- if has_errors {
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
- log_message(&warning_message, "WARNING");
60
- } else {
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
- log_message(&success_message, "SUCCESS");
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
- fn match_error_recursively_resolved(stmt: &StatementResolved) -> bool {
73
- match stmt.value.clone() {
74
- // TODO Other statement value types here
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
- false
101
- }
106
+ // TODO: Implement debugging
102
107
 
103
- fn match_error_recursively_resolved_value(value: &StatementResolvedValue) -> bool {
104
- match value {
105
- StatementResolvedValue::Map(map) => {
106
- for (_, v) in map {
107
- if match_error_recursively_resolved_value(v) {
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
- false
114
+ let logger = Logger::new();
115
+ logger.log_message(LogLevel::Success, &success_message);
124
116
  }
@@ -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(&current_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
+ }
@@ -1,37 +1,31 @@
1
- use crate::core::types::statement::{ StatementResolved };
2
- use std::fs::File;
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 fn build_ast(statements: &Vec<StatementResolved>) -> String {
6
- let mut ast_string = String::new();
5
+ pub struct Builder {}
7
6
 
8
- serde_json
9
- ::to_string_pretty(statements)
10
- .map(|json| {
11
- ast_string.push_str(&json);
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
- ast_string
19
- }
12
+ pub fn build_ast(&self, modules: &HashMap<String, Vec<Statement>>) {
13
+ let output_path = "./output";
20
14
 
21
- pub fn write_ast_to_file(ast: &str, file_path: &str) {
22
- clear_json_directory(&file_path);
23
- create_json_directory(&file_path);
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
- let file_path = format!("{}/ast.json", file_path);
19
+ create_dir_all(format!("{}/ast", output_path)).expect("Failed to create AST directory");
26
20
 
27
- let mut file = File::create(&file_path).expect("Unable to create AST file");
28
- file.write_all(ast.as_bytes()).expect("Unable to write AST to file");
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
- fn clear_json_directory(path: &str) {
32
- std::fs::remove_dir_all(path);
33
- }
24
+ let content = serde_json
25
+ ::to_string_pretty(&statements)
26
+ .expect("Failed to serialize AST");
34
27
 
35
- fn create_json_directory(path: &str) {
36
- std::fs::create_dir_all(path);
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
+ }