From cc310dcab960c4754cc2b47024cb660318c2f840 Mon Sep 17 00:00:00 2001 From: Ronaldson Bellande <47253433+RonaldsonBellande@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:37:18 -0400 Subject: [PATCH 1/5] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index b751661..f6d320f 100644 --- a/README.md +++ b/README.md @@ -46,5 +46,13 @@ projects: team_size: 3 ``` + +## Python Installation +- https://github.com/RonaldsonBellande/bellande_format/tree/main/Package/Python + +## JavaScript Installation +- https://github.com/RonaldsonBellande/bellande_format/tree/main/Package/JavaScript + + ## License This Algorithm or Models is distributed under the [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/), see [LICENSE](https://github.com/RonaldsonBellande/bellande_format/blob/main/LICENSE) and [NOTICE](https://github.com/RonaldsonBellande/bellande_format/blob/main/LICENSE) for more information. From 2fc556c6c77375c2ef4c624a9d8d8b012274e716 Mon Sep 17 00:00:00 2001 From: RonaldsonBellande Date: Tue, 10 Sep 2024 21:12:13 -0400 Subject: [PATCH 2/5] recal --- Package/Python/src/bellande_parser.py | 79 ++++++++++++++++++--------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/Package/Python/src/bellande_parser.py b/Package/Python/src/bellande_parser.py index 6170547..845997c 100644 --- a/Package/Python/src/bellande_parser.py +++ b/Package/Python/src/bellande_parser.py @@ -18,46 +18,55 @@ import re from typing import Dict, List, Union, Any -class bellande_format: - def parse_bellande(self, file_path: str) -> Dict: +class Bellande_Format: + def parse_bellande(self, file_path: str) -> str: with open(file_path, 'r') as file: lines = file.readlines() - return self.parse_lines(lines) + parsed_data = self.parse_lines(lines) + return self.to_string_representation(parsed_data) - def parse_lines(self, lines: List[str]) -> Dict: + def parse_lines(self, lines: List[str]) -> Union[Dict, List]: result = {} - stack = [(-1, result)] - + current_key = None + current_list = None + indent_stack = [(-1, result)] + for line in lines: stripped = line.strip() if not stripped or stripped.startswith('#'): continue - + indent = len(line) - len(line.lstrip()) - while stack and indent <= stack[-1][0]: - stack.pop() - - parent = stack[-1][1] - + + while indent_stack and indent <= indent_stack[-1][0]: + popped = indent_stack.pop() + if isinstance(popped[1], list): + current_list = None + if ':' in stripped: key, value = map(str.strip, stripped.split(':', 1)) + current_key = key if value: - parent[key] = self.parse_value(value) + result[key] = self.parse_value(value) else: - new_dict = {} - parent[key] = new_dict - stack.append((indent, new_dict)) + result[key] = [] + current_list = result[key] + indent_stack.append((indent, current_list)) elif stripped.startswith('-'): value = stripped[1:].strip() - - if isinstance(parent, list): - parent.append(self.parse_value(value)) + parsed_value = self.parse_value(value) + if current_list is not None: + current_list.append(parsed_value) else: - new_list = [self.parse_value(value)] - last_key = list(parent.keys())[-1] - parent[last_key] = new_list - stack.append((indent, new_list)) - + if not result: # If result is empty, start a root-level list + result = [parsed_value] + current_list = result + indent_stack = [(-1, result)] + else: + result[current_key] = [parsed_value] + current_list = result[current_key] + indent_stack.append((indent, current_list)) + return result def parse_value(self, value: str) -> Union[str, int, float, bool, None]: @@ -76,6 +85,24 @@ class bellande_format: else: return value + def to_string_representation(self, data: Any) -> str: + if isinstance(data, dict): + items = [f'"{k}": {self.to_string_representation(v)}' for k, v in data.items()] + return '{' + ', '.join(items) + '}' + elif isinstance(data, list): + items = [self.to_string_representation(item) for item in data] + return '[' + ', '.join(items) + ']' + elif isinstance(data, str): + return f'"{data}"' + elif isinstance(data, (int, float)): + return str(data) + elif data is None: + return 'null' + elif isinstance(data, bool): + return str(data).lower() + else: + return str(data) + def write_bellande(self, data: Any, file_path: str): with open(file_path, 'w') as file: file.write(self.to_bellande_string(data)) @@ -93,7 +120,7 @@ class bellande_format: elif isinstance(data, list): lines = [] for item in data: - if isinstance(item, (dict, list)): + if isinstance(item, dict): lines.append(f"{' ' * indent}-") lines.append(self.to_bellande_string(item, indent + 4)) else: @@ -104,7 +131,7 @@ class bellande_format: def format_value(self, value: Any) -> str: if isinstance(value, str): - if ' ' in value or ':' in value: + if ' ' in value or ':' in value or value.lower() in ['true', 'false', 'null']: return f'"{value}"' return value elif isinstance(value, bool): From 1ecf0b936d99a4383641b0ca54d694aecd39652a Mon Sep 17 00:00:00 2001 From: RonaldsonBellande Date: Wed, 11 Sep 2024 18:02:40 -0400 Subject: [PATCH 3/5] recal --- Package/Python/src/bellande_parser.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Package/Python/src/bellande_parser.py b/Package/Python/src/bellande_parser.py index 845997c..a03e54c 100644 --- a/Package/Python/src/bellande_parser.py +++ b/Package/Python/src/bellande_parser.py @@ -102,18 +102,18 @@ class Bellande_Format: return str(data).lower() else: return str(data) - + def write_bellande(self, data: Any, file_path: str): with open(file_path, 'w') as file: file.write(self.to_bellande_string(data)) - + def to_bellande_string(self, data: Any, indent: int = 0) -> str: if isinstance(data, dict): lines = [] for key, value in data.items(): if isinstance(value, (dict, list)): lines.append(f"{' ' * indent}{key}:") - lines.append(self.to_bellande_string(value, indent + 4)) + lines.append(self.to_bellande_string(value, indent + 2)) else: lines.append(f"{' ' * indent}{key}: {self.format_value(value)}") return '\n'.join(lines) @@ -121,8 +121,9 @@ class Bellande_Format: lines = [] for item in data: if isinstance(item, dict): - lines.append(f"{' ' * indent}-") - lines.append(self.to_bellande_string(item, indent + 4)) + dict_lines = self.to_bellande_string(item, indent + 2).split('\n') + lines.append(f"{' ' * indent}- {dict_lines[0]}") + lines.extend(dict_lines[1:]) else: lines.append(f"{' ' * indent}- {self.format_value(item)}") return '\n'.join(lines) From 2fde3ecc4a1f78232cd2b77163e90c1abebe4053 Mon Sep 17 00:00:00 2001 From: RonaldsonBellande Date: Thu, 12 Sep 2024 02:38:49 -0400 Subject: [PATCH 4/5] latest pushes --- Package/JavaScript/src/bellande_parser.js | 96 ++++++++--- Package/Python/.gitignore | 1 + Package/Rust/README.md | 0 Package/Rust/src/bellande_parser.rs | 196 ++++++++++++++++++++++ 4 files changed, 272 insertions(+), 21 deletions(-) create mode 100644 Package/Rust/README.md create mode 100644 Package/Rust/src/bellande_parser.rs diff --git a/Package/JavaScript/src/bellande_parser.js b/Package/JavaScript/src/bellande_parser.js index c86feb1..1adb24a 100644 --- a/Package/JavaScript/src/bellande_parser.js +++ b/Package/JavaScript/src/bellande_parser.js @@ -1,45 +1,73 @@ +// Copyright (C) 2024 Bellande Algorithm Model Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + const fs = require('fs'); class BellandeFormat { parseBellande(filePath) { const content = fs.readFileSync(filePath, 'utf8'); const lines = content.split('\n'); - return this.parseLines(lines); + const parsedData = this.parseLines(lines); + return this.toStringRepresentation(parsedData); } parseLines(lines) { const result = {}; - const stack = [[-1, result]]; + let currentKey = null; + let currentList = null; + const indentStack = [[-1, result]]; for (const line of lines) { const stripped = line.trim(); if (!stripped || stripped.startsWith('#')) continue; const indent = line.length - line.trimLeft().length; - while (stack.length && indent <= stack[stack.length - 1][0]) { - stack.pop(); - } - const parent = stack[stack.length - 1][1]; + while (indentStack.length && indent <= indentStack[indentStack.length - 1][0]) { + const popped = indentStack.pop(); + if (Array.isArray(popped[1])) { + currentList = null; + } + } if (stripped.includes(':')) { const [key, value] = stripped.split(':').map(s => s.trim()); + currentKey = key; if (value) { - parent[key] = this.parseValue(value); + result[key] = this.parseValue(value); } else { - const newDict = {}; - parent[key] = newDict; - stack.push([indent, newDict]); + result[key] = []; + currentList = result[key]; + indentStack.push([indent, currentList]); } } else if (stripped.startsWith('-')) { const value = stripped.slice(1).trim(); - if (Array.isArray(parent)) { - parent.push(this.parseValue(value)); + const parsedValue = this.parseValue(value); + if (currentList !== null) { + currentList.push(parsedValue); } else { - const newList = [this.parseValue(value)]; - const lastKey = Object.keys(parent).pop(); - parent[lastKey] = newList; - stack.push([indent, newList]); + if (Object.keys(result).length === 0) { + result = [parsedValue]; + currentList = result; + indentStack = [[-1, result]]; + } else { + result[currentKey] = [parsedValue]; + currentList = result[currentKey]; + indentStack.push([indent, currentList]); + } } } } @@ -57,9 +85,30 @@ class BellandeFormat { return value; } + toStringRepresentation(data) { + if (typeof data === 'object' && data !== null) { + if (Array.isArray(data)) { + const items = data.map(item => this.toStringRepresentation(item)); + return '[' + items.join(', ') + ']'; + } else { + const items = Object.entries(data).map(([k, v]) => `"${k}": ${this.toStringRepresentation(v)}`); + return '{' + items.join(', ') + '}'; + } + } else if (typeof data === 'string') { + return `"${data}"`; + } else if (typeof data === 'number') { + return data.toString(); + } else if (data === null) { + return 'null'; + } else if (typeof data === 'boolean') { + return data.toString().toLowerCase(); + } else { + return String(data); + } + } + writeBellande(data, filePath) { - const content = this.toBellandeString(data); - fs.writeFileSync(filePath, content); + fs.writeFileSync(filePath, this.toBellandeString(data)); } toBellandeString(data, indent = 0) { @@ -84,14 +133,19 @@ class BellandeFormat { formatValue(value) { if (typeof value === 'string') { - return value.includes(' ') || value.includes(':') ? `"${value}"` : value; + if (value.includes(' ') || value.includes(':') || ['true', 'false', 'null'].includes(value.toLowerCase())) { + return `"${value}"`; + } + return value; } if (typeof value === 'boolean') { - return value.toString(); + return value.toString().toLowerCase(); } if (value === null) { return 'null'; } - return value.toString(); + return String(value); } } + +module.exports = BellandeFormat; diff --git a/Package/Python/.gitignore b/Package/Python/.gitignore index 3e54b1f..b111f4b 100644 --- a/Package/Python/.gitignore +++ b/Package/Python/.gitignore @@ -2,3 +2,4 @@ publish.sh tests setup.py dist +src/bellande_format.egg-info diff --git a/Package/Rust/README.md b/Package/Rust/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Package/Rust/src/bellande_parser.rs b/Package/Rust/src/bellande_parser.rs new file mode 100644 index 0000000..c3fa077 --- /dev/null +++ b/Package/Rust/src/bellande_parser.rs @@ -0,0 +1,196 @@ +// Copyright (C) 2024 Bellande Algorithm Model Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::collections::HashMap; +use std::fs; +use std::path::Path; + +#[derive(Debug, Clone)] +enum BellandeValue { + String(String), + Integer(i64), + Float(f64), + Boolean(bool), + Null, + List(Vec), + Map(HashMap), +} + +struct BellandeFormat; + +impl BellandeFormat { + pub fn parse_bellande>( + &self, + file_path: P, + ) -> Result { + let content = fs::read_to_string(file_path)?; + let lines: Vec<&str> = content.lines().collect(); + let parsed_data = self.parse_lines(&lines); + Ok(parsed_data) + } + + fn parse_lines(&self, lines: &[&str]) -> BellandeValue { + let mut result = HashMap::new(); + let mut current_key = String::new(); + let mut current_list: Option> = None; + let mut indent_stack = vec![(0, &mut result)]; + + for line in lines { + let stripped = line.trim(); + if stripped.is_empty() || stripped.starts_with('#') { + continue; + } + + let indent = line.len() - line.trim_start().len(); + + while indent_stack.last().map_or(false, |&(i, _)| indent <= i) { + indent_stack.pop(); + current_list = None; + } + + if let Some(colon_pos) = stripped.find(':') { + let (key, value) = stripped.split_at(colon_pos); + let key = key.trim().to_string(); + let value = value[1..].trim(); + + current_key = key.clone(); + if !value.is_empty() { + let parsed_value = self.parse_value(value); + indent_stack.last_mut().unwrap().1.insert(key, parsed_value); + } else { + let new_list = Vec::new(); + indent_stack + .last_mut() + .unwrap() + .1 + .insert(key, BellandeValue::List(new_list)); + if let BellandeValue::List(list) = indent_stack + .last_mut() + .unwrap() + .1 + .get_mut(¤t_key) + .unwrap() + { + current_list = Some(list); + indent_stack.push((indent, list)); + } + } + } else if stripped.starts_with('-') { + let value = stripped[1..].trim(); + let parsed_value = self.parse_value(value); + if let Some(list) = &mut current_list { + list.push(parsed_value); + } else { + let mut new_list = Vec::new(); + new_list.push(parsed_value); + indent_stack + .last_mut() + .unwrap() + .1 + .insert(current_key.clone(), BellandeValue::List(new_list)); + if let BellandeValue::List(list) = indent_stack + .last_mut() + .unwrap() + .1 + .get_mut(¤t_key) + .unwrap() + { + current_list = Some(list); + indent_stack.push((indent, list)); + } + } + } + } + + BellandeValue::Map(result) + } + + fn parse_value(&self, value: &str) -> BellandeValue { + if value.eq_ignore_ascii_case("true") { + BellandeValue::Boolean(true) + } else if value.eq_ignore_ascii_case("false") { + BellandeValue::Boolean(false) + } else if value.eq_ignore_ascii_case("null") { + BellandeValue::Null + } else if value.starts_with('"') && value.ends_with('"') { + BellandeValue::String(value[1..value.len() - 1].to_string()) + } else if let Ok(int_value) = value.parse::() { + BellandeValue::Integer(int_value) + } else if let Ok(float_value) = value.parse::() { + BellandeValue::Float(float_value) + } else { + BellandeValue::String(value.to_string()) + } + } + + pub fn write_bellande>( + &self, + data: &BellandeValue, + file_path: P, + ) -> Result<(), std::io::Error> { + let content = self.to_bellande_string(data, 0); + fs::write(file_path, content) + } + + fn to_bellande_string(&self, data: &BellandeValue, indent: usize) -> String { + match data { + BellandeValue::Map(map) => map + .iter() + .map(|(key, value)| { + let value_str = match value { + BellandeValue::Map(_) | BellandeValue::List(_) => { + format!("\n{}", self.to_bellande_string(value, indent + 2)) + } + _ => format!(" {}", self.format_value(value)), + }; + format!("{}{}: {}", " ".repeat(indent), key, value_str) + }) + .collect::>() + .join("\n"), + BellandeValue::List(list) => list + .iter() + .map(|item| { + format!( + "{}- {}", + " ".repeat(indent), + self.to_bellande_string(item, indent + 2) + ) + }) + .collect::>() + .join("\n"), + _ => self.format_value(data), + } + } + + fn format_value(&self, value: &BellandeValue) -> String { + match value { + BellandeValue::String(s) => { + if s.contains(' ') + || s.contains(':') + || ["true", "false", "null"].contains(&s.to_lowercase().as_str()) + { + format!("\"{}\"", s) + } else { + s.clone() + } + } + BellandeValue::Integer(i) => i.to_string(), + BellandeValue::Float(f) => f.to_string(), + BellandeValue::Boolean(b) => b.to_string().to_lowercase(), + BellandeValue::Null => "null".to_string(), + BellandeValue::List(_) | BellandeValue::Map(_) => unreachable!(), + } + } +} From 82bff582c6c0d0f722225f7378c5bebca84a381c Mon Sep 17 00:00:00 2001 From: RonaldsonBellande Date: Thu, 12 Sep 2024 02:49:34 -0400 Subject: [PATCH 5/5] Reset repo to respect .gitignore --- Package/Rust/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Package/Rust/.gitignore diff --git a/Package/Rust/.gitignore b/Package/Rust/.gitignore new file mode 100644 index 0000000..6518f05 --- /dev/null +++ b/Package/Rust/.gitignore @@ -0,0 +1,2 @@ +publish.sh +src/Cargo.toml