Skip to content

Commit

Permalink
Merge 6df6286 into 1bb6850
Browse files Browse the repository at this point in the history
  • Loading branch information
odino committed Jul 6, 2020
2 parents 1bb6850 + 6df6286 commit 9bf036a
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ABS is a programming language that works best when you're scripting on
your terminal. It tries to combine the elegance of languages
such as Python, or Ruby, to the convenience of Bash.

``` bash
``` js
tz = `cat /etc/timezone`;
continent, city = tz.split("/")

Expand Down
1 change: 1 addition & 0 deletions docs/_includes/toc.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
* [Introduction](/stdlib/intro)
* [@runtime](/stdlib/runtime)
* [@cli](/stdlib/cli)
* [@util](/stdlib/util)

## Miscellaneous

Expand Down
2 changes: 1 addition & 1 deletion docs/stdlib/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,4 @@ count

That's about it for this section!

You can now head over to read a little bit about [how to install 3rd party libraries](/misc/3pl).
You can now head over to read a little bit about [the util module](/stdlib/util).
39 changes: 39 additions & 0 deletions docs/stdlib/util.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# @util

The `@util` module provides various utilities.

## API

```py
util = require('@util')
```

### @util.memoize(ttl)

A decorator that allows memoization of a function based on
the arguments passed to it:

```py
@util.memoize(60)
f expensive_task(x, y, z) {
# do something very expensive here...
}
```

The first time `expensive_task` gets called with a set of arguments,
it will be execute. The next time it's called with the same set of
arguments its result will be fetched from a cache (currently
implemented in-memory). Executions are going to be cached for a
specific timeframe, `ttl` (in seconds).

Arguments are serialized using the `str()` method:

```bash
[12, {}, 0.23, "hello"].str() # "[12, {}, 0.23, \"hello\"]"
```

## Next

That's about it for this section!

You can now head over to read a little bit about [how to install 3rd party libraries](/misc/3pl).
8 changes: 8 additions & 0 deletions docs/types/builtin-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,14 @@ This will limit the source inclusion depth to 15 levels for this
`source()` statement and will also apply to future `source()`
statements until changed.
### unix_ms()
Returns the current unix epoch time, in milliseconds:
``` bash
unix_ms() # 1594049453157
```
## Next
That's about it for this section!
Expand Down
9 changes: 9 additions & 0 deletions evaluator/builtin_functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ func TestMap(t *testing.T) {
testBuiltinFunction(tests, t)
}

func TestUnixMs(t *testing.T) {
tests := []Tests{
{`x = unix_ms(); sleep(300); (unix_ms() - x) > 305`, false},
{`x = unix_ms(); sleep(300); (unix_ms() - x) > 295`, true},
}

testBuiltinFunction(tests, t)
}

func TestSum(t *testing.T) {
tests := []Tests{
{`[1, null].sum()`, "sum(...) can only be called on an homogeneous array, got [1, null]"},
Expand Down
10 changes: 10 additions & 0 deletions evaluator/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,11 @@ func getFns() map[string]*object.Builtin {
Types: []string{object.ARRAY_OBJ},
Fn: tsvFn,
},
// unix_ms() -- returns the current unix epoch, in milliseconds
"unix_ms": &object.Builtin{
Types: []string{},
Fn: unixMsFn,
},
}
}

Expand Down Expand Up @@ -579,6 +584,11 @@ func exitFn(tok token.Token, env *object.Environment, args ...object.Object) obj
return arg
}

// unix_ms()
func unixMsFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
return &object.Number{Value: float64(time.Now().UnixNano() / 1000000)}
}

// flag("my-flag")
func flagFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object {
// TODO:
Expand Down
14 changes: 14 additions & 0 deletions evaluator/stdlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ func stdlib_runtime_index_abs() ([]byte, error) {
)
}

var _stdlib_util_index_abs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x92\x4d\xcb\xdb\x30\x10\x84\xef\xfa\x15\x83\x73\xb1\xdb\xb7\xc2\x29\xf4\x12\xea\x9e\x7a\xec\x31\xd0\x43\x29\x41\x75\x56\x89\x40\x96\x82\xb4\x26\x25\xc1\xff\xbd\xc8\x1f\xc5\x1f\x79\x7d\x30\x68\xe7\xd9\xf5\xcc\x5a\x0d\x35\xfe\xd4\xa8\x1b\x2a\x3c\x3b\x21\x76\xf8\x4e\xb5\x0f\x8a\x7d\x00\x7b\x24\xd5\x3c\x48\xec\xc0\x57\x42\xa0\xd8\x5a\x86\xd7\x50\xd0\xad\xab\xd9\x78\x27\x85\x9e\xa8\x9c\xd9\x16\x78\x0a\x00\xd8\xe1\x27\xe1\xae\x1c\xa7\x29\x91\x7d\x20\xb0\x69\x28\x9d\xd2\xa4\xc6\x58\x6b\x22\xd5\xde\x9d\xdf\x46\x5e\x59\xeb\xef\xc6\x5d\xa0\x7d\xc0\xf1\xf8\x23\x26\xf6\x0f\xe1\x2b\xf6\x11\x39\x5d\x50\xca\xcf\x5f\xca\x42\xf6\x38\xb3\x45\xd5\xbf\x3f\x60\x5f\x96\xa5\xe8\xab\x81\xb8\x0d\x0e\x3a\xd7\x6e\xf2\xb1\x28\xcf\x8b\xe9\x89\x14\x8c\xb2\xe6\x41\xe7\x93\x0a\x97\x88\x0a\x52\x4a\x19\x39\xe4\xc5\x82\xab\x55\x7d\xa5\x33\x2a\x4c\xcb\xfa\xb5\xea\xfc\xbd\xc0\x9d\xbf\xa3\x42\xeb\xcc\xdf\x53\x13\xf3\x42\x2c\x44\xa3\xa7\x71\x4b\x2f\x0b\x4d\x72\xc4\xc7\x3e\xde\xa7\x7e\xda\x37\x94\x2f\xf0\x59\xb6\xb1\x6d\xf8\x41\x1b\xb0\x13\x9b\xd2\x94\x44\xde\xfc\x2d\x5f\xa5\x59\x86\x5f\x35\x07\x4a\x7b\xd2\x4e\xd6\xca\xda\x5c\x4a\xb9\xca\xf7\xee\x8a\xd2\x05\xdb\xd8\xc8\x38\x66\x87\x14\xf1\x6d\x2b\x0d\x61\xb2\x43\xfa\xe4\xca\xd1\xfc\x24\x5e\xac\x63\xde\x31\xd0\x9d\xe8\x84\x18\xd5\xc1\x46\x36\xde\xda\xec\xf0\xff\x96\x77\xff\x02\x00\x00\xff\xff\x7c\x7e\x69\xa8\x0d\x03\x00\x00")

func stdlib_util_index_abs() ([]byte, error) {
return bindata_read(
_stdlib_util_index_abs,
"stdlib/util/index.abs",
)
}

// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
Expand All @@ -67,6 +76,7 @@ func AssetNames() []string {
var _bindata = map[string]func() ([]byte, error){
"stdlib/cli/index.abs": stdlib_cli_index_abs,
"stdlib/runtime/index.abs": stdlib_runtime_index_abs,
"stdlib/util/index.abs": stdlib_util_index_abs,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
Expand Down Expand Up @@ -117,5 +127,9 @@ var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
"index.abs": &_bintree_t{stdlib_runtime_index_abs, map[string]*_bintree_t{
}},
}},
"util": &_bintree_t{nil, map[string]*_bintree_t{
"index.abs": &_bintree_t{stdlib_util_index_abs, map[string]*_bintree_t{
}},
}},
}},
}}
14 changes: 14 additions & 0 deletions evaluator/stdlib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ func TestRuntime(t *testing.T) {
testStdLib(tests, t)
}

func TestUtil(t *testing.T) {
tests := []tests{
{`memo = require('@util').memoize; @memo(1) f test() { return 1 }; test()`, 1},
{`memo = require('@util').memoize; @memo(1) f test(n) { return 1 + n }; test(10)`, 11},
{`memo = require('@util').memoize; x = {"y": 0}; @memo(0) f test() { x.y += 1 }; test(); x.y`, 1},
{`memo = require('@util').memoize; x = {"y": 0}; @memo(0) f test() { x.y += 1 }; test(); test(); test(); x.y`, 3},
{`memo = require('@util').memoize; x = {"y": 0}; @memo(10) f test() { x.y += 1 }; test(); test(); test(); x.y`, 1},
{`memo = require('@util').memoize; x = {"y": 0}; @memo(0.250) f test() { x.y += 1 }; test(); test(); test(); x.y`, 1},
{`memo = require('@util').memoize; x = {"y": 0}; @memo(0.250) f test() { x.y += 1 }; test(); test(); sleep(251); test(); x.y`, 2},
}

testStdLib(tests, t)
}

func testStdLib(tests []tests, t *testing.T) {
for _, tt := range tests {
evaluated := testEval(tt.input)
Expand Down
15 changes: 15 additions & 0 deletions examples/util.abs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
memo = require('@util').memoize

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

echo(long_task(1, 1))
echo(long_task(1, 1))
echo(long_task(1, 1))
echo(long_task(1, 1))
echo(long_task(1, 1))
echo(long_task(1, 1))
echo(long_task(1, 2))
38 changes: 38 additions & 0 deletions stdlib/util/index.abs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
memo_map = {}

# Decorator to memoize
# the result of a function.
f memoize(ttl) {
# We want to store time to the millisecond,
# allowing for TTLs to be < 1s (eg 0.250).
ttl = ttl * 1000

return f(fn) {
return f() {
serialized_args = ....str()
cached = memo_map[serialized_args]
now = unix_ms()

if cached {
if cached.ts + ttl - now > 0 {
return cached.result
}

memo_map.pop(serialized_args)
}

res = fn.call(...)

memo_map[serialized_args] = {
"ts": now,
"result": res
}

return res
}
}
}

return {
"memoize": memoize
}

0 comments on commit 9bf036a

Please sign in to comment.