Abs Versions Save

Home of the ABS programming language: the joy of shell scripting.

2.2.1

3 years ago

This is a bugfix release that fixes a bug in the @util.memoize decorator. Earlier, the decorator used a shared in-memory map for all functions, meaning that 2 different functions with the same arguments would collide and memoize each other's results (#379).

2.2.0

3 years ago

A fresh new minor release of ABS: always be shipping! :ship:

Interactive REPL through the @cli module

The @cli.repl() mode allows to run interactive REPLs based on the commands registered within the CLI:

cli = require('@cli')

res = {"count": 0}

@cli.cmd("count", "prints a counter", {})
f counter(arguments, flags) {
    echo(res.count)
}

@cli.cmd("incr", "Increment our counter", {})
f incr(arguments, flags) {
    res.count += 1
    return "ok"
}

@cli.cmd("incr_by", "Increment our counter", {})
f incr_by(arguments, flags) {
    echo("Increment by how much?")
    n = stdin().number()
    res.count += n
    return "ok"
}

cli.repl()

You can see it in action:

$ ./cli
help
Available commands:

  * count - prints a counter
  * help - print this help message
  * incr - Increment our counter
  * incr_by - Increment our counter
count
0
incr
ok
incr
ok
count
2
incr_by
Increment by how much?
-10
ok
count
-8

Memoizing through @util.memoize

A new @util native module was introduced to allow memoization of functions:

memo = require('@util').memoize

@memo(60)
f long_task(x, y) {
    sleep(1000)
    return "done"
}

echo(long_task(1, 1)) # waits 1s
echo(long_task(1, 1)) # immediate
echo(long_task(1, 1)) # immediate
sleep(61000) # sleep for a min...
echo(long_task(1, 1)) # waits 1s
echo(long_task(1, 2)) # waits 1s

New function: unix_ms

unix_ms() returns the current Unix epoch, in milliseconds.

unix_ms() # 1594049453157

See ya! :wave:

2.1.0

4 years ago

A fresh new minor release of ABS: always be shipping! :ship:

Optional function parameters

You can now make a parameter optional by specifying its default value:

f greet(name, greeting = "hello") {
    echo("$greeting $name!")
}
greet("user") # hello user!
greet("user", "hola") # hola user!

A default value can be any expression (doesn't have to be a literal):

f test(x = 1){x}; test() # 1
f test(x = "test".split("")){x}; test() # ["t", "e", "s", "t"]
f test(x = {}){x}; test() # {}
y = 100; f test(x = y){x}; test() # 100
x = 100; f test(x = x){x}; test() # 100
x = 100; f test(x = x){x}; test(1) # 1

Note that mandatory arguments always need to be declared before optional ones:

f(x = null, y){}
# parser errors:
# 	found mandatory parameter after optional one
# 	[1:13]	f(x = null, y){}

Descending number ranges

Number ranges (x..y) now support generating ranges in descending order:

3..1 # [3, 2, 1]

See ya! :wave:

2.0.0

4 years ago

A fresh new major release of ABS: always be shipping! :ship:

This release was originally intended to be out as 1.13.0, but a backward-incompatible change forced us to shift gears. Don't fear the upgrade as the incompatible changes are extremely easy to address -- and there's only 3 of them.

Let's get rollin'!

Introducing the ABS standard library

:rocket: :rocket: :rocket: this is a big one! :rocket: :rocket: :rocket:

We've started to integrate a standard library within ABS, with modules such as runtime and cli to help you build apps with ease. Requiring a module from the standard library is extremely simple:

runtime = require('@runtime')
runtime.version # 2.0.0

Standard modules are required through the @ prefix. To learn more about ABS's standard library visit the documentation.

Building CLI applications with the @cli module

As part of the initial rollout of the standard library, we've included a @cli module that helps you building CLI applications. It has a simple API that takes advantage of decorators:

#!/usr/bin/env abs
cli = require('@cli')

@cli.cmd("ip", "finds our IP address", {})
f ip_address(arguments, flags) {
    return `curl icanhazip.com`
}

@cli.cmd("date", "Is it Friday already?", {"format": ""})
f date(arguments, flags) {
    format = flags.format
    return `date ${format}`
}

cli.run()

You can save this script as cli and make it executable with chmod +x ./cli. Then you will be able to use the CLI app:

$ ./cli 
Available commands:

  * date - Is it Friday already?
  * help - print this help message
  * ip - finds our IP address

$ ./cli help
Available commands:

  * date - Is it Friday already?
  * help - print this help message
  * ip - finds our IP address

$ ./cli ip  
87.201.252.69

$ ./cli date
Sat Apr  4 18:06:35 +04 2020

$ ./cli date --format +%s
1586009212

Fresh new decorators

Decorators have gone through a major revamp and are now 100% compatible with Python's ones, which we find extremely powerful:

f uppercase(fn) {
    return f() {
        return fn(...).upper()
    }
}

@uppercase
f stringer(x) {
    return x.str()
}

stringer({}) # "{}"
stringer(12) # "12"
stringer("hello") # "HELLO"

The revamp was needed in order to allow any kind of expression to be used as a decorator, and not just plain functions -- for example:

  • @decorator
  • @decorator()
  • @module.decorator()

Deprecated functions

We have removed the functions slice and contains, which were available on both arrays and strings. They have been deprecated for a while and can be easily replaced by the index notation ([1, 2, 3].slice(0, 1) is equivalent to [1, 2, 3][:1]) and the in operator ([1, 2, 3].includes(1) is equivalent to 1 in [1, 2, 3]).

Upgrade guide to ABS 2

We've recapped these 3 backwards-incompatible changes in a simple upgrade guide.

Misc

  • fixed a panic when converting index ranges to strings (#364)

See ya! :wave:

1.12.1

4 years ago

This is a bugfix release that fixes a nasty bug when using both index (a.b) and property accessors (a['b']) in assignments, such as a.b.c['d']['e'] (#356).

1.12.0

4 years ago

A new minor release of ABS: always be shipping! :ship:

Function bonanza!

We've been adding a plethora of functions in this release -- check'em out!

array.chunk(size)

Splits the array into chunks of the given size:

[1, 2, 3].chunk(2) # [[1, 2], [3]]
[1, 2, 3].chunk(10) # [[1,2,3]]
[1, 2, 3].chunk(1.2) # argument to chunk must be a positive integer, got '1.2'

array.intersect(other_array)

Computes the intersection between 2 arrays:

[1, 2, 3].intersect([]) # []
[1, 2, 3].intersect([3]) # [3]
[1, 2, 3].intersect([3, 1]) # [1, 3]
[1, 2, 3].intersect([1, 2, 3, 4]) # [1, 2, 3]

array.diff(other_array)

Computes the difference between 2 arrays, returning elements that are only on the first array:

[1, 2, 3].diff([]) # [1, 2, 3]
[1, 2, 3].diff([3]) # [1, 2]
[1, 2, 3].diff([3, 1]) # [2]
[1, 2, 3].diff([1, 2, 3, 4]) # []

For symmetric difference see diff_symmetric(...)

array.diff_symmetric(other_array)

Computes the symmetric difference between 2 arrays (elements that are only on either of the 2):

[1, 2, 3].diff([]) # [1, 2, 3]
[1, 2, 3].diff([3]) # [1, 2]
[1, 2, 3].diff([3, 1]) # [2]
[1, 2, 3].diff([1, 2, 3, 4]) # [4]

array.union(other_array)

Computes the union between 2 arrays:

[1, 2, 3].union([1, 2, 3, 4]) # [1, 2, 3, 4]
[1, 2, 3].union([3]) # [1, 2, 3]
[].union([3, 1]) # [3, 1]
[1, 2].union([3, 4]) # [1, 2, 3, 4]

array.flatten()

Flattens an array a single level deep:

[[1, 2], 3, [4]].flatten([1, 2, 3, 4]) # [1, 2, 3, 4]
[[1, 2, 3, 4]].flatten([1, 2, 3, 4]) # [1, 2, 3, 4]

array.flatten_deep()

Flattens an array recursively until no member is an array:

[[[1, 2], [[[[3]]]], [4]]].flatten_deep() # [1, 2, 3, 4]
[[1, [2, 3], 4]].flatten_deep() # [1, 2, 3, 4]

array.max()

Finds the highest number in an array:

[].max() # NULL
[0, 5, -10, 100].max() # 100

array.min()

Finds the lowest number in an array:

[].min() # NULL
[0, 5, -10, 100].min() # -10

array.reduce(fn, accumulator)

Reduces the array to a value by iterating through its elements and applying fn to them:

[1, 2, 3, 4].reduce(f(value, element) { return value + element }, 0) # 10
[1, 2, 3, 4].reduce(f(value, element) { return value + element }, 10) # 20

array.partition(fn)

Partitions the array by applying fn to all of its elements and using the result of the function invocation as the key to partition by:

f odd(n) {
  return !!(n % 2)
}
[0, 1, 2, 3, 4, 5].partition(odd) # [[0, 2, 4], [1, 3, 5]]
[1, "1", {}].partition(str) # [[1, "1"], [{}]]

number.between(min, max)

Checks whether the number is between min and max:

10.between(0, 100) # true
10.between(10, 100) # true
10.between(11, 100) # false

number.clamp(min, max)

Clamps the number between min and max:

10.clamp(0, 100) # 10
10.clamp(0, 5) # 5
10.clamp(50, 100) # 50
1.5.clamp(2.5, 3) # 2.5

string.camel()

Converts the string to camelCase:

"a short sentence".camel() # aShortSentence

string.snake()

Converts the string to snake_case:

"a short sentence".snake() # a_short_sentence

string.kebab()

Converts the string to kebab-case:

"a short sentence".snake() # a-short-sentence

Shorthand syntax for array.find(...)

A shorthand syntax supports passing a hash and comparing elements to the given hash:

[null, {"key": "val", "test": 123}].find({"key": "val"}) # {"key": "val", "test": 123}

Misc

  • upgraded all deps (#341)
  • split tests for builtin functions to their own module, which eases testing (#337)
  • simplified the release process, which now depends on a single VERSION file (#335)

1.11.4

4 years ago

This patch release enhances decorators, allowing multiple decorators to be declared over a function (#332):

@debug()
@sleep()
@upper()
f greet(name) {
  echo("hello $name")
}

1.11.3

4 years ago

This is a patch release that fixes a nasty scenario with string interpolation (#330). You can now interpolate variables both with $var and ${var} so that variables can be embedded within other strings "string${var}string" (#331).

1.11.2

4 years ago

This is a patch release that upgrades ABS to be built through Go 1.14. Ther are no changes to the language besides the improvements brought by this new release of Go.

https://golang.org/doc/go1.14

1.11.1

4 years ago

This is a bugfix release that fixes a regression introduced in 1.10.1: early returns from within for and for in loops were enabled for all return values, not just explicit ones.

An explicit return is defined as using the return keyword:

for x in 1..10 {
  return x
}

but the early return feature was enabled also for implicit returns:

for x in 1..10 {
  x <-- this loop only runs one, and return the first value of x
}

which would make code such as this fail:

list = []
for x in 1..10 {
  list.push(x) <-- this will be considered an implicit return, and only run once
}

This was fixed with #325.

Please note that the 1.10.x branch will not see a backport of this fix.