The Wayback Machine - https://web.archive.org/web/20201002150440/https://github.com/niieani/bashscript
Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

BashScript

A JavaScript/TypeScript to bash transpiler. Work in progress.

Why? Mainly because I wanted to learn how to make a transpiler.

I also wanted to experiment with bash and how far we could stretch this old, yet widely cross-platform language.

I've previously created a framework trying to make bash as usable as possible: Bash Infinity. This seemed like the natural next step.

Also, because... why not? 🤓

REPL

Not much works, but it's cool. :-)

Find it here: REPL.

Specification (WIP)

Function invocation

Function calls are transpilled as calls to bash functions/commands e.g.

input

echo('hi')

output

echo hi

if the call is used in the position of parameter or assignment: e.g.

input

const out = concat('hi', 'ho')

output

declare out=$(concat 'hi' 'ho')

Function declaration

input

function concat(a, b) {
  return `${a}${b}`
}

output

function concat {
  local a="${1}"
  local b="${2}"
  echo "${a}${b}"
}

Invoking properties

input

const a = 'abc'
const b = a.toLowercase()

output

declare a='abc'
declare b="$(__callProperty a toLowercase)"

Invoking properties with parameters

input

const arr = ['abc']
arr.push('xyz')

output

declare arr=('abc')
__callProperty arr push 'xyz'

Operators

input

const a = 'abc' + 'xyz'

output

declare a="$(__operator_addition 'abc' 'zyx')"

output helpers

# e.g.
__callProperty() {
  if isArray arr
  then
    if property === 'push'
    then
      arr+=("${arr[@]}")
    fi
  fi
}

Lambda functions

input

const concat = (a) => {
  const c = `${a}-super`
  return (b) => {
    return `${c}${b}`
  }
}
const withOne = concat('one')
const result = withOne('two')

output

function concat {
  # params:
  local a="${1}"
  # function body:
  local c="${a}-super"
  # preapplied function:
  # lambda declaration is:
  # [type, name, scoped_declarations_to_eval]
  local -a declaration=(
    function
    __lambda_concat_1
    "$(declare -p c)"
  )
  echo "$(declare -p declaration)"
}

# all functions are top level
function __lambda_concat_1 {
  # scoped variables:
  eval "${1}"; shift;
  # actual function body:
  local b="${1}"
  echo "${c}${b}"
}

function __callVar {
  # TODO: add check if var is a declaration
  eval "${!1}"; shift;
  if [[ "${declaration[1]}" == "function" ]]
  then
    "${declaration[1]}" "${declaration[2]}" "$@"
  fi
}

declare withOne="$(concat 'one')"
declare result="$(__callVar withOne 'two')"

Lambdas with scoped variables

input

const arr = [1, 2, 3]
const result = arr
  .map(num => num + 1)
  .map(num => num - 1)

output

declare declaration=(1 2 3)
declare arr="$(declare -p declaration)"
unset declaration

__lamda_arr_map_anon_1() {
  # scoped variables (if any):
  eval "${1}"; shift;
  # actual function body:
  local num="${1}"
  echo "$(__operator_addition num 1)"
}
__lamda_arr_map_anon_2() {
  # scoped variables (if any):
  eval "${1}"; shift;
  # actual function body:
  local num="${1}"
  echo "$(__operator_substraction num 1)"
}

declare _result1="$(__callProperty arr map __lamda_arr_map_anon_1)"
declare result="$(__callProperty _result1 map __lamda_arr_map_anon_2)"
unset _result1

Objects and other literals

input

const outerObject = {a: 'boom'}
const outerHello = 'hello from outer space!'
const obj = {
  a: {
    aa: 123,
    bbb: {c: ['inner1', 'inner2', 'inner3']}
  },
  b: 'hello',
  c: outerObject,
  d: outerHello,
}

echo(obj.a.aa)

output

#!/usr/bin/env bash
declare -A outerObject=(
  [__type]=object

  [a]="boom"
)

declare outerHello='hello from outer space!'

declare -a __obj_a_bbb_c=(inner1 inner2 inner3)
declare -A __obj_a_bbb=([ref_c]=__obj_a_bbb_c [__type]=object)
declare -A __obj_a=([aa]=123 [ref_bbb]="__obj_a_bbb" [__type]=object)
declare -A obj=(
  [__type]=object

  [ref_a]="__obj_a"
  [b]="hello"
  [ref_c]="outerObject"
  # we need the TypeScript type to tell whether this is a reference or a plain object
  [d]="${outerHello}"
)

# perhaps instead of using the ref_ prefix, we should use an uncommon prefix in the value
# that way we can reuse it for other reference-related functionality

@objectProperty() {
  local objName="$1"
  local property="$2"
  shift
  shift
  local refName="${objName}[\"ref_${property}\"]"
  local typeName="${objName}[\"__type\"]"
  # we need to check type, because bash will return the value of first property if it doesn't exist on the object 🤦
  if [[ -v "${refName}" && "${!typeName}" == 'object' ]]; then
    local value="${!refName}"
    if [[ "${#}" -gt 0 ]]; then
      @objectProperty "${value}" "$@"
      return
    fi
    echo "__ref:${value}"
  else
    refName="${objName}[${property}]"
    if [[ -v "${refName}" ]]; then
      if [[ "${#}" -gt 0 ]]; then
        echo "Error: Cannot read property '${1}' of a non-object '${property}'."
        return
      fi
      echo "${!refName}"
    else
      echo "Error: Cannot read property '${property}' of '${objName}'."
    fi
  fi
}

@objectProperty obj a aa

About

TypeScript to bash transpiler. Because.

Resources

License

Releases

No releases published

Packages

No packages published
You can’t perform that action at this time.