From 573d2da85e21b0468e69b8d6493e430e6eed1980 Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Tue, 28 Dec 2021 00:44:16 +0100 Subject: [PATCH] First commit! --- README.md | 55 +++++++++++++++++++++++++++++ example/Example.shn | 7 ++++ example/Object.shc | 12 +++++++ example/script.sh | 16 +++++++++ oop.sh | 85 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 175 insertions(+) create mode 100644 README.md create mode 100644 example/Example.shn create mode 100644 example/Object.shc create mode 100644 example/script.sh create mode 100644 oop.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d9161b --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# BashOOP + +This idea was inspired from https://stackoverflow.com/a/40981277 in order to be adapted into making a proper OOP with constructors, properties and namespace system for bash. + +## Reference: + +- .shn: **SH**ell **N**amespace +- .shc: **SH**ell **C**lass + +## Example syntax: + +### Declaring objects. + +`script.sh`: +This is the main script which will use our objects. + +```bash +. $OOP_ROOT/oop.sh # Import library. + +importNamespace Example.shn + +Example.Object t "Test" +``` + +`Example.shn`: +This file declares our namespace and all the objects within it. + +```bash +# Namespace declaration. +namespace Example +# If namespace is set to null (no argument), then the object will be declared globally. +# Otherwise, the object will be declared within the namespace. + +# Object declaration, from class name to file name. +class Object "Object.shc" + +``` + +`Object.shc`: +This file will contain the object code. + +```bash +# Property declaration +property Object.name + +# Optional constructor. +Object.constructor() { + Object.name = $1 +} + +# Example function +Object.print() { + echo "Example OOP from $($this.name)!" +} +``` diff --git a/example/Example.shn b/example/Example.shn new file mode 100644 index 0000000..24a833c --- /dev/null +++ b/example/Example.shn @@ -0,0 +1,7 @@ +# Namespace declaration. +namespace Example +# If namespace is set to null (no argument), then the object will be declared globally. +# Otherwise, the object will be declared within the namespace. + +# Object declaration, from class name to file name. +class Object "Object.shc" diff --git a/example/Object.shc b/example/Object.shc new file mode 100644 index 0000000..f418e85 --- /dev/null +++ b/example/Object.shc @@ -0,0 +1,12 @@ +# Property declaration +property Object.name + +# Optional constructor. +Object.constructor() { + Object.name = $1 +} + +# Example function +Object.print() { + echo "Example OOP from $(this.name)!" +} diff --git a/example/script.sh b/example/script.sh new file mode 100644 index 0000000..d86c616 --- /dev/null +++ b/example/script.sh @@ -0,0 +1,16 @@ +#!/bin/bash +OOP_ROOT=.. + +. $OOP_ROOT/oop.sh # Import library. + +importNamespace Example.shn + +Example.Object t1 "Test" +Example.Object t2 "Example" + +t1.print +t2.print + +t1.name = "New name" + +t1.print diff --git a/oop.sh b/oop.sh new file mode 100644 index 0000000..47f14a4 --- /dev/null +++ b/oop.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# 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="" +_namespacePath="" + +# Namespace declaration. +# Signature: ([string namespaceName]) +namespace() { + _namespace=$1; +} + +# Imports a namespace into the current shell. +# Signature: () +importNamespace() { + namespaceFile=$1 + # Save the path in order to get the absolute path of the file. + _namespacePath=$(realpath $(dirname $namespaceFile)) + . $namespaceFile +} + +# Creates an object instance. +# Signature: (, , , [string[] constructorArguments]) +_createObject() { + type=$1 + associatedFile=$2 + varName=$3 + constructorArguments=${@:4} + + # Declare dummy constructor. + eval "$varName.constructor() { :; }" + # Create property array. + eval "declare -Ag _${varName}_properties" + # Imports the file and replace all "." with the variable name. + . <(sed s/this\\./$varName./g <(sed s/$type\\./$varName./g $associatedFile)) + # Call the constructor + eval "$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 \$@; }" +} + +# 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 \$@; }" +}