Skip to content

Commit

Permalink
Merge branch '2.6.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
odino committed Apr 15, 2022
2 parents 895a855 + f3a537d commit 68af347
Show file tree
Hide file tree
Showing 62 changed files with 242 additions and 72 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.5.2
2.6.0
24 changes: 24 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,27 @@ type Expression interface {
expressionNode()
}

// Deferrable is used to be able to
// check whether an expression needs
// to be executed now or at the end
// of the scope
type Deferrable interface {
IsDeferred() bool
SetDeferred(bool)
}

// Deferred is a struct that can be embedded
// in order to define whether the current node
// needs to be executed right away or whether
// it should be deferred until the end of the
// current scope.
type Deferred struct {
deferred bool
}

func (d *Deferred) IsDeferred() bool { return d.deferred }
func (d *Deferred) SetDeferred(deferred bool) { d.deferred = deferred }

// Represents the whole program
// as a bunch of statements
type Program struct {
Expand Down Expand Up @@ -275,6 +296,7 @@ type MethodExpression struct {
Method Expression
Arguments []Expression
Optional bool
Deferred
}

func (me *MethodExpression) expressionNode() {}
Expand Down Expand Up @@ -416,6 +438,7 @@ func (fe *ForExpression) String() string {
type CommandExpression struct {
Token token.Token // The command itself
Value string
Deferred
}

func (ce *CommandExpression) expressionNode() {}
Expand Down Expand Up @@ -479,6 +502,7 @@ type CallExpression struct {
Token token.Token // The '(' token
Function Expression // Identifier or FunctionLiteral
Arguments []Expression
Deferred
}

func (ce *CallExpression) expressionNode() {}
Expand Down
1 change: 1 addition & 0 deletions docs/src/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ module.exports = {
'syntax/system-commands',
'syntax/operators',
'syntax/comments',
'syntax/defer',
]
},
{
Expand Down
Binary file modified docs/src/.vuepress/public/abs.wasm
Binary file not shown.
10 changes: 5 additions & 5 deletions docs/src/docs/misc/3pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ and a few things might change.
In order to install a package, you simply need to run `abs get`:

``` bash
$ abs get github.com/abs-lang/abs-sample-module
$ abs get github.com/abs-lang/abs-sample-module
🌘 - Downloading archive
Unpacking...
Creating alias...
Expand Down Expand Up @@ -43,7 +43,7 @@ Unpacking...
Creating alias...
Install Success. You can use the module with `require("abs-sample-module")`
$ cat packages.abs.json
$ cat packages.abs.json
{
"abs-sample-module": "./vendor/github.com/abs-lang/abs-sample-module"
}
Expand All @@ -53,7 +53,7 @@ If an alias is already taken, the installer will let you know that you
will need to use the full path when requiring the module:

```
$ echo '{"abs-sample-module": "xyz"}' > packages.abs.json
$ echo '{"abs-sample-module": "xyz"}' > packages.abs.json
$ abs get github.com/abs-lang/abs-sample-module
🌘 - Downloading archive
Expand All @@ -67,7 +67,7 @@ When requiring a module, ABS will try to load the `index.abs` file unless
another file is specified:

```
$ ~/projects/abs/builds/abs
$ ~/projects/abs/builds/abs
Hello alex, welcome to the ABS programming language!
Type 'quit' when you're done, 'help' if you get lost!
Expand All @@ -85,4 +85,4 @@ f() {return hello world;}

Currently, the installer supports modules hosted on:

* GitHub
* GitHub
2 changes: 1 addition & 1 deletion docs/src/docs/misc/configuring-the-repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ user@hostname:~/git/abs$ cwd = cd()
user@hostname:~$ `ls .absrc`
.absrc
user@hostname:~$
```
```
4 changes: 2 additions & 2 deletions docs/src/docs/misc/error.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ When using ABS, you might bump into errors within your code. When the interprete

Note that there are 2 phases of the interpreter: parser and evaluator.

When the parser phase encounters a syntax error it will continue to process the rest of the file and report all of the syntax errors it finds.
When the parser phase encounters a syntax error it will continue to process the rest of the file and report all of the syntax errors it finds.

However, the evaluator phase will quit immediately when it encounters an evaluation error. Thus, you may need to run the ABS interpreter multiple times to find all the run-time errors.

Expand Down Expand Up @@ -60,4 +60,4 @@ ERROR: type mismatch: NUMBER + STRING
$ echo $?
99
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/misc/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ Hello user, welcome to the ABS programming language!
Type 'quit' when you're done, 'help' if you get lost!
⧐ ABS_INTERACTIVE
true
```
```
4 changes: 2 additions & 2 deletions docs/src/docs/misc/technical-details.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ permalink: /misc/technical-details

# A few technical details...

The ABS interpreter is built with Golang version `1.15`, and is mostly based on [the interpreter book](https://interpreterbook.com/) written by [Thorsten Ball](https://twitter.com/thorstenball).
The ABS interpreter is built with Golang version `1.18`, and is mostly based on [the interpreter book](https://interpreterbook.com/) written by [Thorsten Ball](https://twitter.com/thorstenball).

ABS is extremely different from Monkey, the "fictional" language the reader builds throughout the book, but the base structure (lexer, parser, evaluator) are still very much based on Thorsten's work.

Expand Down Expand Up @@ -217,4 +217,4 @@ Error: add number to null hash element
>>> h["g"] += 1
ERROR: type mismatch: NULL + NUMBER
[78:8] h["g"] += 1
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/misc/upgrade-from-abs-1-to-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,4 @@ As you can see there are 2 main differences:

## What more?

That's really it: upgrading to ABS 2 should be an extremely painless process!
That's really it: upgrading to ABS 2 should be an extremely painless process!
4 changes: 2 additions & 2 deletions docs/src/docs/standard-lib/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ cli.cmd = f(name, description, flags) {
flags[k] = v
}
}

# Call the original cmd
result = fn.call([args()[3:], flags])

Expand Down Expand Up @@ -99,4 +99,4 @@ f help() {
}

return cli
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/standard-lib/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ Returns the name of the runtime:

```py
runtime.name # "abs"
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/standard-lib/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ Arguments are serialized using the `str()` method:

```bash
[12, {}, 0.23, "hello"].str() # "[12, {}, 0.23, \"hello\"]"
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/syntax/comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ Though you can comment after a statement:

```bash
x = 1 # Now, this is a cool assignment!
```
```
57 changes: 57 additions & 0 deletions docs/src/docs/syntax/defer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
permalink: /syntax/defer
---

# Defer <Badge text="experimental" type="warning"/>

Sometimes it is very helpful to guarantee a certain function is executed
regardless of what code path we take: you can use the `defer` keyword for
this.

```py
echo(1)
defer echo(3)
echo(2)
# 1
# 2
# 3
```

When you schedule a function to be deferred, it will executed right at
the end of the current scope. A `defer` inside a function will then
execute at the end of that function itself:

```py
echo(1)
f fn() {
defer echo(3)
echo(2)
}
fn()
echo(4)
# 1
# 2
# 3
# 4
```

You can `defer` any callable: a function call, a method or even a system
command. This can be very helpful if you need to run a cleanup function
right before wrapping up with your code:

```sh
defer `rm my-file.txt`
"some text" > "my-file.txt"

...
...
"some other text" >> "my-file.txt"
```

In this case, you will be guaranteed to execute the command that removes
`my-file.txt` before the program closes.

Be aware that code that is deferred does not have access to the return value
of its scope, and will supress errors -- if a `defer` block messes up you're
not going to see any error. This behavior is experimental, but we would most
likely like to give this kind of control through [try...catch...finally](https://github.com/abs-lang/abs/issues/118).
2 changes: 1 addition & 1 deletion docs/src/docs/syntax/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,4 +403,4 @@ Bitwise left shift:
```bash
1 << 1 # 2
1 << "hello" # ERROR: type mismatch: NUMBER << STRING
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/syntax/system-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,4 @@ if filename.prefix('~/') || filename.prefix(homedir) {

# execute the command with live stdIO
exec("$sudo $cmd $filename")
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/types/decorator.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@ mmm, we were pretty slow...
```

Decorators are heavily inspired by [Python](https://www.python.org/dev/peps/pep-0318/) -- if you wish to understand
how they work more in depth we'd recommend reading this [primer on Python decorators](https://realpython.com/primer-on-python-decorators).
how they work more in depth we'd recommend reading this [primer on Python decorators](https://realpython.com/primer-on-python-decorators).
2 changes: 1 addition & 1 deletion docs/src/docs/types/hash.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,4 @@ Since hash values can be of any type, we can create objects with custom function
```bash
hash = {"greeter": f(name) { return "Hello $name!" }}
hash.greeter("Sally") # "Hello Sally!"
```
```
2 changes: 1 addition & 1 deletion docs/src/docs/types/number.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,4 @@ Returns a string containing the number:

```bash
99.str() # "99"
```
```
4 changes: 2 additions & 2 deletions docs/src/docs/types/string.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ Parses the string as JSON, returning a [hash](/types/hash):
Converts the string to kebab-case:

```bash
"a short sentence".snake() # a-short-sentence
"a short sentence".kebab() # a-short-sentence
```

### last_index(str)
Expand Down Expand Up @@ -490,4 +490,4 @@ Uppercases the string:

```bash
"string".upper() # "STRING"
```
```
42 changes: 38 additions & 4 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,36 +231,70 @@ func Eval(node ast.Node, env *object.Environment) object.Object {

func evalProgram(program *ast.Program, env *object.Environment) object.Object {
var result object.Object
deferred := []*ast.ExpressionStatement{}

loop:
for _, statement := range program.Statements {
x, ok := statement.(*ast.ExpressionStatement)

if ok {
if d, ok := x.Expression.(ast.Deferrable); ok && d.IsDeferred() {
deferred = append(deferred, x)
continue
}
}
result = Eval(statement, env)
switch result := result.(type) {

switch ret := result.(type) {
case *object.ReturnValue:
return result.Value
result = ret.Value
break loop
case *object.Error:
return result
break loop
}
}

for _, statement := range deferred {
Eval(statement, env)
}

return result
}

// This should fundamentally be using the same function as evalProgram,
// but there are some subtle difference on how they brak / handle return
// values. You will see a lot of repeated code between the 2, especially
// since we introduced `defer` which adds a bit of complexity to both.
func evalBlockStatement(
block *ast.BlockStatement,
env *object.Environment,
) object.Object {
var result object.Object
deferred := []*ast.ExpressionStatement{}

for _, statement := range block.Statements {
x, ok := statement.(*ast.ExpressionStatement)

if ok {
if d, ok := x.Expression.(ast.Deferrable); ok && d.IsDeferred() {
deferred = append(deferred, x)
continue
}
}
result = Eval(statement, env)

if result != nil {
rt := result.Type()
if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ {
return result
break
}
}
}

for _, statement := range deferred {
Eval(statement, env)
}

return result
}

Expand Down
Loading

0 comments on commit 68af347

Please sign in to comment.