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/Python/dist/bellande_format-0.1.1.tar.gz b/Package/Python/dist/bellande_format-0.1.1.tar.gz new file mode 100644 index 0000000..257b0c8 Binary files /dev/null and b/Package/Python/dist/bellande_format-0.1.1.tar.gz differ diff --git a/Package/Python/publish.sh b/Package/Python/publish.sh new file mode 100755 index 0000000..48934a9 --- /dev/null +++ b/Package/Python/publish.sh @@ -0,0 +1,2 @@ +python setup.py sdist +twine upload dist/* diff --git a/Package/Python/setup.py b/Package/Python/setup.py new file mode 100644 index 0000000..c15691b --- /dev/null +++ b/Package/Python/setup.py @@ -0,0 +1,40 @@ +from setuptools import setup, find_packages + +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setup( + name="bellande_format", + version="0.1.1", + description="Bellande Format is a file format type", + long_description=long_description, + long_description_content_type="text/markdown", + author="RonaldsonBellande", + author_email="ronaldsonbellande@gmail.com", + packages=find_packages(where="src"), + package_dir={"": "src"}, + include_package_data=True, + install_requires=[ + "numpy", + ], + classifiers=[ + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Programming Language :: Python", + ], + keywords=["package", "setuptools"], + python_requires=">=3.0", + extras_require={ + "dev": ["pytest", "pytest-cov[all]", "mypy", "black"], + }, + entry_points={ + 'console_scripts': [ + 'Bellande_Format = bellande_parser:Bellande_Format', + ], + }, + project_urls={ + "Home": "https://github.com/RonaldsonBellande/bellande_format", + "Homepage": "https://github.com/RonaldsonBellande/bellande_format", + "documentation": "https://github.com/RonaldsonBellande/bellande_format", + "repository": "https://github.com/RonaldsonBellande/bellande_format", + }, +) diff --git a/Package/Rust/README.md b/Package/Rust/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Package/Rust/publish.sh b/Package/Rust/publish.sh new file mode 100755 index 0000000..f23334d --- /dev/null +++ b/Package/Rust/publish.sh @@ -0,0 +1,2 @@ +cargo package +cargo publish diff --git a/Package/Rust/src/Cargo.toml b/Package/Rust/src/Cargo.toml new file mode 100644 index 0000000..30b64e9 --- /dev/null +++ b/Package/Rust/src/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "bellande_parser" +version = "0.0.1" +edition = "2021" +authors = ["Ronaldson Bellande ronaldsonbellande@gmail.com"] +description = "Bellande File Format" +license = "GNU" + +[lib] +path = "bellande_parser.rs" + +[dependencies] +std = { version = "1.65", features = ["collections", "fs", "path"] } 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!(), + } + } +} diff --git a/git_scripts/fix_errors.sh b/git_scripts/fix_errors.sh new file mode 100755 index 0000000..2ebdbee --- /dev/null +++ b/git_scripts/fix_errors.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Get the URL from .git/config +git_url=$(git config --get remote.origin.url) + +# Check if a URL is found +if [ -z "$git_url" ]; then + echo "No remote URL found in .git/config." + exit 1 +fi + +# Clone the repository into a temporary folder +git clone "$git_url" tmp_clone + +# Check if the clone was successful +if [ $? -eq 0 ]; then + # Remove the existing .git directory if it exists + if [ -d ".git" ]; then + rm -rf .git + fi + + # Copy the .git directory from the clone to the current repository + cp -r tmp_clone/.git . + + # Remove the clone directory + rm -rf tmp_clone + + echo "Repository cloned and .git directory copied successfully." +else + echo "Failed to clone the repository." +fi diff --git a/git_scripts/push.sh b/git_scripts/push.sh new file mode 100755 index 0000000..0b793ae --- /dev/null +++ b/git_scripts/push.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Git push what is already in the repository +git pull --no-edit; git fetch; + +# Exclude specific files and directories +EXCLUDES=(".git" ".gitignore" "executable") + +# Find all non-hidden files and directories, excluding any hidden files and directories +find . -type f ! -path '*/.*' -print0 | while IFS= read -r -d '' file; do + # Check if the file is in the exclude list + should_exclude=false + for exclude in "${EXCLUDES[@]}"; do + if [[ "$(basename "$file")" == "$exclude" ]]; then + should_exclude=true + break + fi + done + + # Add file to staging area if it's not excluded + if [ "$should_exclude" = false ]; then + git add -f "$file" + fi +done +git commit -am "latest pushes"; git push diff --git a/git_scripts/repository_recal.sh b/git_scripts/repository_recal.sh new file mode 100755 index 0000000..4c7fd4f --- /dev/null +++ b/git_scripts/repository_recal.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +# Git push what is already in the repository +git pull --no-edit; git fetch; git add .; git commit -am "latest pushes"; git push + +# Get the current directory +current_dir=$(pwd) + +# Read the remote repository URL from .git/config +remote_repo_url=$(git -C "$current_dir" config --get remote.origin.url) + +# Create a temporary directory for cloning the repository +temp_dir=$(mktemp -d) + +# Clone the repository into the temporary directory without using local references +git clone --no-local "$current_dir" "$temp_dir" + +# Switch to the temporary directory +cd "$temp_dir" + +# Create a temporary file to store the file list +tmp_file=$(mktemp) +# Create a temporary file to store the processed commits +processed_commits_file=$(mktemp) + +# Function to check if a commit has already been processed +is_commit_processed() { + local commit="$1" + + # Check if the commit is already processed + grep -Fxq "$commit" "$processed_commits_file" +} + +# Function to mark a commit as processed +mark_commit_processed() { + local commit="$1" + + # Mark the commit as processed + echo "$commit" >> "$processed_commits_file" +} + +# Function to check if a file or folder exists in the repository +file_exists_in_repo() { + local file_path="$1" + + # Check if the file or folder exists in the repository + git ls-tree --name-only -r HEAD | grep -Fxq "$file_path" +} + +# Function to process the files and folders in each commit +process_commit_files() { + local commit="$1" + + # Check if the commit has already been processed + if is_commit_processed "$commit"; then + echo "Commit $commit already processed. Skipping..." + return + fi + + # Get the list of files and folders in the commit (including subfolders) + git ls-tree --name-only -r "$commit" >> "$tmp_file" + + # Process each file or folder in the commit + while IFS= read -r line + do + # Check if the file or folder exists in the current push + if file_exists_in_repo "$line"; then + echo "Keeping: $line" + else + echo "Deleting: $line" + git filter-repo --path "$line" --invert-paths + fi + done < "$tmp_file" + + # Mark the commit as processed + mark_commit_processed "$commit" + + # Clear the temporary file + > "$tmp_file" +} + +# Iterate over each commit in the repository +git rev-list --all | while IFS= read -r commit +do + process_commit_files "$commit" +done + +# Push the filtered changes to the original repository +git remote add origin "$remote_repo_url" +git push --force origin main + +# Perform a history rewrite to remove the filtered files +git filter-repo --force + +# Fetch the changes from the remote repository +git -C "$current_dir" fetch origin + +# Merge the remote changes into the local repository +git -C "$current_dir" merge origin/main --no-edit + +# Update the local repository and reduce the size of .git if needed +git -C "$current_dir" gc --prune=now +git -C "$current_dir" reflog expire --expire=now --all +git -C "$current_dir" repack -ad + +# Clean up temporary files and directories +cd "$current_dir" +rm -rf "$temp_dir" +rm "$tmp_file" +rm "$processed_commits_file"