Releases: abs-lang/abs
v2.3.1
A fresh bugfix for ABS!
#402 fixes a nasty bug with silent returns (return
statements without a value, which default to NULL
).
The PR fixes them and adds some more sophisticated tests. A return
without a value, when interpreted before this PR, would terminate the script without executing the rest of the code.
This snippet:
if false {
return
}
return 3
would return NULL
instead of 3
.
v2.3.0
A fresh new minor release of ABS: always be shipping! 🚢
ABS allows you to check whether you should upgrade 😄
When you run the REPL with abs
, it will periodically print whether there's a new release you should upgrade to (#392):
$ abs
Hello user, welcome to the ABS (2.2.2) programming language!
*** Update available: 2.3.0 (your version is 2.2.2) ***
Type 'quit' when you're done, 'help' if you get lost!
⧐
A new command has also been added if you want to explicitly check for a new version:
$ abs --check-update
Update available: 2.3.0 (your version is 2.2.2)
Upgrade to Go 1.15
ABS is now built with Go 1.15 (#397). As a result, we dropped support for darwin/386
(#396).
The REPL used to suppress output when ending with a comment
A fairly annoying behavior (#388) we fixed by discarding comments right at the lexer's level (#394).
A big thank you to @gromgit who's the main contributor behind this release -- see ya! 👋
v2.2.2
This release fixes a bug with parsing negative numbers (#385): before, -10
would be parsed as (minus, number) whereas
right now it's going to be parsed as (number) only.
This fixes a bug with precedences, where -1.str()
would be parsed as (-, (1, str)), leading to first calling the str method on the positive number, then applying the minus.
Before:
⧐ -1.234.str()
ERROR: unknown operator: -STRING
[1:1] -1.234.str()
After:
⧐ -1.234.str()
-1.234
If a space is found between the minus and the number, the old parsing mechanism still applies.
It's inspired by Ruby, where:
$ - 1.to_s()
-@: undefined method `-@' for "1":String
$-1.to_s()
-1
$ -10.3.floor()
-11
$ - 10.3.floor()
-10
In addition, the documentation for strings has been improved (#384).
Thanks to @gromgit both for finding the bug as well as improving the documentation ❤️
v2.2.1
v2.2.0
A fresh new minor release of ABS: always be shipping! 🚢
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! 👋
v2.1.0
A fresh new minor release of ABS: always be shipping! 🚢
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! 👋
v2.0.0
A fresh new major release of ABS: always be shipping! 🚢
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
🚀 🚀 🚀 this is a big one! 🚀 🚀 🚀
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! 👋
v1.12.1
v1.12.0
A new minor release of ABS: always be shipping! 🚢
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}