# # BashOOP - Simple OOP implementation for bash. # Copyright (C) 2021 Ad5001 # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser 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 . # # This file contains all functions required to create a namespace. # Internal variables are marked with a beginning underscore, like in most other languages. # Signatures are a list of arguments.. Those within <> are mandatory, the ones within [] are optional. # Namespace related variables. _namespace="" _namespacePath=$(realpath $(dirname ${BASH_SOURCE[0]})) # This dictionnary saves all classes for each namespace so they can be retreived and aliased. declare -Ag _namespacesClasses # This dictionnary links all files for static namespaces. declare -Ag _namespacesStaticClasses # Namespace declaration. # Signature: ([string namespaceName]) namespace() { _namespace=$1; } # Imports a namespace into the current shell. # It saves the path of the file so that relative paths can be # properly resolved. # For example, if the object Object exists within namespace Example, it # will be accessible with "Example.Object". # Signature: () importNamespace() { namespaceFile=$1 # Save the path in order to get the absolute path of the file. _namespacePath=$(realpath $(dirname $namespaceFile)) . $namespaceFile } # Aliases the classes in global namespace. # For example, if the object Object exists within namespace Example, it # will be accessible with "Example.Object" and "Object". # Signature: () using() { namespaceName=$1 # Import static classes if [ "${_namespacesStaticClasses[$namespaceName]}" != "" ]; then oldNamespace=_namespace namespace # Reset namespace # Split all staticClasses=$(echo "${_namespacesStaticClasses[$namespaceName]}" | tr ";" "\n") for cl in $staticClasses; do parts=$(echo "$cl" | tr ":" "\n") static_class ${parts[0]} ${parts[1]} done namespace $oldNamespace fi # Import static classes if [ "${_namespacesClasses[$namespaceName]}" != "" ]; then classes=$(echo "${_namespacesClasses[$namespaceName]}" | tr ";" "\n") for type in $classes; do eval "$type() { $namespaceName.$type \$@; }" done fi } # Creates a "property holder" based on a name. # A "property holder" is a dictionnary maintaining the values of properties for a specific variable. # One is created each time an object is declared, but this function can also be used for namespaces to have global # properties. # Signature: () createPropertyHolder() { name=$1 eval "declare -Ag _${name}_properties" } # Creates an object instance. # Signature: (, , , [string[] constructorArguments]) _createObject() { type=$1 associatedFile=$2 varName=$3 constructorArguments=${@:4} # Declare dummy constructor. eval "$varName.constructor() { :; }" # Declare base properties. eval "$varName.type() { echo $type; }" eval "$varName.source() { echo $associatedFile; }" # Create property array. createPropertyHolder $varName # alias the "varName" variable to itself, so that it can be used and transmitted in other variables (e.g: $varName.name would alias to varName.name) eval "$varName='$varName'" # Imports the file and replace all "." with the variable name. . <(sed s/this\\./$varName./g <(sed s/$type\\./$varName./g $associatedFile)) # Call the constructor $varName.constructor $constructorArguments } # Object creation. # Signature: (, ) class() { type=$1 # Type of the object as declared within the file. associatedFile=$2 objFullName=$type # Type of the object referenced elsewhere if [ "$_namespace" != "" ]; then objFullName="${_namespace}.$type" fi if [ ${associatedFile::1} != "/" ]; then # Relative path, we save only the absolute path associatedFile="$_namespacePath/$associatedFile" fi # Declares a new function for object initialisation. eval "$objFullName() { _createObject $type $associatedFile \$@; }" # Save the class in the dictionnary for reference. if [ "$_namespace" != "" ]; then if [ "${_namespacesClasses[$_namespace]}" == "" ]; then _namespacesClasses[$_namespace]=$type else _namespacesClasses[$_namespace]="${_namespacesClasses[$_namespace]};$type" fi fi } # Static class creation # Signature: (, ) static_class() { type=$1 # Type of the object as declared within the file. associatedFile=$2 objFullName=$type # Type of the object referenced elsewhere if [ "$_namespace" != "" ]; then objFullName="${_namespace}.$type" fi if [ ${associatedFile::1} != "/" ]; then # Relative path, we save only the absolute path associatedFile="$_namespacePath/$associatedFile" fi # Imports the file and replace all "." with the variable name. . <(sed s/this\\./$objFullName./g <(sed s/$type\\./$objFullName./g $associatedFile)) # Save the class in the dictionnary for reference. if [ "$_namespace" != "" ]; then if [ "${_namespacesStaticClasses[$_namespace]}" == "" ]; then _namespacesStaticClasses[$_namespace]="$type:$associatedFile" else _namespacesStaticClasses[$_namespace]="${_namespacesStaticClasses[$_namespace]};$type:$associatedFile" fi fi } # Associated function for properties # Signature: (, , [string operator, string value]) _accessProperty() { varName=$1 prop=$2 if [ "$3" == "=" ]; then eval "_${varName}_properties[$prop]='${@:4}'" else eval "echo \${_${varName}_properties[$prop]}" fi } # Declares a property. # Signature: () property() { propertyFullName=$1 # Split the name by ".". First element is variable name, # second is property name. propertyNames=($(echo $propertyFullName | tr "." "\n")) varName=${propertyNames[0]} prop=${propertyNames[1]} # Default value eval "_${varName}_properties[$prop]=''" # Property alias eval "$propertyFullName() { _accessProperty $varName $prop \$@; }" }