Skip to content

Commit

Permalink
Merge e86b550 into 8df5915
Browse files Browse the repository at this point in the history
  • Loading branch information
odino committed Feb 15, 2019
2 parents 8df5915 + e86b550 commit 0cfbfe3
Show file tree
Hide file tree
Showing 15 changed files with 496 additions and 130 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: repl
run:
docker run -tiv $$(pwd):/go/src/github.com/abs-lang/abs --name abs --rm abs
docker run -ti -v $$(pwd):/go/src/github.com/abs-lang/abs -v /home/`whoami`/.abs_history:/root/.abs_history --name abs --rm abs
fmt:
go fmt ./...
build:
Expand Down
2 changes: 1 addition & 1 deletion docs/installer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ MACHINE_TYPE=$(uname -m)
if [ "${MACHINE_TYPE}" = 'x86_64' ]; then
ARCH="amd64"
fi
VERSION=1.1.0
VERSION=1.2.0

echo "Trying to detect the details of your architecture."
echo ""
Expand Down
65 changes: 64 additions & 1 deletion docs/introduction/how-to-run-abs-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ Afterwards, you can run ABS scripts with:
``` bash
$ abs path/to/scripts.abs
```
You can also run an executable abs script directly from bash
using a bash shebang line at the top of the script file:
```bash
$ cat ~/bin/remote.abs
#! /usr/local/bin/abs
# remote paths are <target>:<path>
from_path = arg(2)
to_path = arg(3)
if ! (from_path && to_path) {
if ! from_path {from_path = "<missing>"}
if ! to_path {to_path = "<missing>"}
echo("FROM: %s, TO: %s", from_path, to_path)
exit(1)
}
...
# the executable abs script above is in the PATH at ~/bin/remote.abs
$ remote.abs
FROM: <missing>, TO: <missing>
```
Scripts do not have to have a specific extension,
although it's recommended to use `.abs` as a
Expand Down Expand Up @@ -47,6 +66,50 @@ ERROR: not a function: STRING
⧐ ip
94.204.178.37
```
### REPL Command History
Interactive REPL sessions can restore and save the command
history to a history file containing a maximum number of command lines.
The prompt live history is restored from the history file when
the REPL starts and then saved again when the REPL exits. This way you
can navigate through the command lines from your previous sessions
by using the up and down arrow keys at the prompt.
+ Note well that the live prompt history will show duplicate command
lines, but the saved history will only contain a single command
when the previous command and the current command are the same.
The history file name and the maximum number of lines are
configurable through the OS environment. The default values are
`ABS_HISTORY_FILE="~/.abs_history"` and `ABS_MAX_HISTORY_LINES=1000`.
+ If you wish to suppress the command line history completely, just
set `ABS_MAX_HISTORY_LINES=0`. In this case the history file
will not be created.
For example:
```bash
$ export ABS_HISTORY_FILE="~/my_abs_hist"
$ export ABS_MAX_HISTORY_LINES=500
$ abs
Hello user, welcome to the ABS (1.1.0) programming language!
Type 'quit' when you are done, 'help' if you get lost!
⧐ pwd()
/home/user/git/abs
⧐ cd()
/home/user
⧐ echo("hello")
hello
⧐ quit
Adios!
$ cat ~/my_abs_hist`; echo
pwd()
cd()
echo("hello")
$
```
## Why is abs interpreted?
Expand All @@ -64,4 +127,4 @@ it took to run a Bash script?
That's about it for this section!
You can now head over to read about ABS's syntax,
starting with [assignments](/syntax/assignments)!
starting with [assignments](/syntax/assignments)!
42 changes: 40 additions & 2 deletions docs/types/builtin-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,48 @@ type("") # "STRING"
type({}) # "HASH"
```
### cd() or cd(path)
Sets the current working directory to `homeDir` or the given `path`
in both Linux and Windows.
Note that the path may have a `'~/'` prefix which will be replaced
with `'homeDir/'`. Also, in Windows, any `'/'` path separator will be
replaced with `'\'` and path names are not case-sensitive.
Returns the `'/fully/expanded/path'` to the new current working directory and `path.ok`.
If `path.ok` is `false`, that means there was an error changing directory:
``` bash
path = cd()
path.ok # true
path # /home/user or C:\Users\user
here = pwd()
path = cd("/path/to/nowhere")
path.ok # false
path # 'chdir /path/to/nowhere: no such file or directory'
here == pwd() # true
cd("~/git/abs") # /home/user/git/abs or C:\Users\user\git\abs
cd("..") # /home/user/git or C:\Users\user\git
cd("/usr/local/bin") # /usr/local/bin
dirs = cd() && `ls`.lines()
len(dirs) # number of directories in homeDir
```
### pwd()
Returns the working directory where the script was started for -- equivalent
to `env("PWD")`:
Returns the path to the current working directory -- equivalent
to `env("PWD")`.
If executed from a script this will initially be the directory
containing the script.
To change the working directory, see `cd()`.
``` bash
pwd() # /go/src/github.com/abs-lang/abs
Expand Down
22 changes: 16 additions & 6 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"regexp"
"runtime"
"strconv"
"strings"

Expand Down Expand Up @@ -937,7 +938,7 @@ func applyFunction(tok token.Token, fn object.Object, args []object.Object) obje
return unwrapReturnValue(evaluated)

case *object.Builtin:
return fn.Fn(args...)
return fn.Fn(tok, args...)

default:
return newError(tok, "not a function: %s", fn.Type())
Expand All @@ -956,7 +957,7 @@ func applyMethod(tok token.Token, o object.Object, method string, args []object.
}

args = append([]object.Object{o}, args...)
return f.Fn(args...)
return f.Fn(tok, args...)
}

func extendFunctionEnv(
Expand Down Expand Up @@ -1094,8 +1095,17 @@ func evalCommandExpression(tok token.Token, cmd string, env *object.Environment)
return v.Inspect()
})

commands := []string{"-c", cmd}
c := exec.Command("bash", commands...)
// thanks to @haifenghuang
var commands []string
var executor string
if runtime.GOOS == "windows" {
commands = []string{"/C", cmd}
executor = "cmd.exe"
} else { //assume it's linux, darwin, freebsd, openbsd, solaris, etc
commands = []string{"-c", cmd}
executor = "bash"
}
c := exec.Command(executor, commands...)
c.Env = os.Environ()
var out bytes.Buffer
var stderr bytes.Buffer
Expand All @@ -1107,6 +1117,6 @@ func evalCommandExpression(tok token.Token, cmd string, env *object.Environment)
if err != nil {
return &object.String{Token: tok, Value: stderr.String(), Ok: FALSE}
}

return &object.String{Token: tok, Value: strings.Trim(out.String(), "\n"), Ok: TRUE}
// trim space at both ends of out.String(); works in both linux and windows
return &object.String{Token: tok, Value: strings.TrimSpace(out.String()), Ok: TRUE}
}
54 changes: 36 additions & 18 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package evaluator
import (
"flag"
"fmt"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -48,6 +49,9 @@ func TestEvalNumberExpression(t *testing.T) {
input string
expected float64
}{
{"1e1", 10},
{"1e-1", 0.1},
{"1e+1", 10},
{"5.5", 5.5},
{"1.1 + 2.1", 3.2},
{"5.5 + 2.2", 7.7},
Expand Down Expand Up @@ -500,12 +504,11 @@ b, c = [1, 2]; b`, 1},
{`a = 10 + 1 + 2
b, c = [1, 2]; a`, 13},
{`
tz = $(echo "10/20")
tz = "10/20"
a, b = tz.split("/")
a.int()
`, 10},
}

for _, tt := range tests {
evaluated := testEval(tt.input)
switch expected := tt.expected.(type) {
Expand Down Expand Up @@ -664,6 +667,8 @@ func TestBuiltinFunctions(t *testing.T) {
{`arg("o")`, "argument 0 to arg(...) is not supported (got: o, allowed: NUMBER)"},
{`arg(3)`, ""},
{`pwd().split("").reverse().slice(0, 33).reverse().join("").replace("\\", "/", -1).suffix("/evaluator")`, true}, // Little trick to get travis to run this test, as the base path is not /go/src/
{`cwd = cd(); cwd == pwd()`, true},
{`cwd = cd("path/to/nowhere"); cwd == pwd()`, false},
{`rand(1)`, 0},
{`int(10)`, 10},
{`int(10.5)`, 10},
Expand Down Expand Up @@ -995,7 +1000,7 @@ func TestInExpressions(t *testing.T) {
errObj, ok := evaluated.(*object.Error)

if !ok {
t.Errorf("no error object returned. got=%T(%+v)", evaluated)
t.Errorf("no error object returned. got=%T(%+v)", evaluated, evaluated)
continue
}
logErrorWithPosition(t, errObj.Message, expected)
Expand All @@ -1011,13 +1016,12 @@ func TestBuiltinProperties(t *testing.T) {
}{
{`"a".ok`, false},
{`"a".inv`, "invalid property 'inv' on type STRING"},
{"a = $(date);\na.ok", true},
{"a = $(echo hello);\na.ok", true},
{`{}.a`, nil},
{`{"a": 1}.a`, 1},
{`{1: 1}.1`, "unusable as hash key: NUMBER"},
{`[].a`, "invalid property 'a' on type ARRAY"},
}

for _, tt := range tests {
evaluated := testEval(tt.input)

Expand Down Expand Up @@ -1081,22 +1085,36 @@ func TestBuiltinProperties(t *testing.T) {
}

func TestCommand(t *testing.T) {
tests := []struct {
type testLine struct {
input string
expected interface{}
}{
{`a = "A"; b = "B"; eee = "-e"; $(echo $eee -n $a$a$b$b$c$c)`, "AABB"},
{`$(echo -n "123")`, "123"},
{`$(echo -n hello world)`, "hello world"},
{`$(echo hello world | xargs echo -n)`, "hello world"},
{`$(echo \$CONTEXT)`, "abs"},
{"a = 'A'; b = 'B'; eee = '-e'; `echo $eee -n $a$a$b$b$c$c`", "AABB"},
{"`echo -n '123'`", "123"},
{"`echo -n hello world`", "hello world"},
{"`echo hello world | xargs echo -n`", "hello world"},
{"`echo \\$CONTEXT`", "abs"},
}

var tests []testLine
if runtime.GOOS == "windows" {
// cmd.exe commands
tests = []testLine{
{`a = "A"; b = "B"; $(echo $a$a$b$b$c$c)`, "AABB"},
{`$(echo 123)`, "123"},
{`$(echo hello world)`, "hello world"},
{"a = 'A'; b = 'B'; `echo $a$a$b$b$c$c`", "AABB"},
{"`echo 123`", "123"},
{"`echo hello world`", "hello world"},
}
} else {
// bash commands
tests = []testLine{
{`a = "A"; b = "B"; eee = "-e"; $(echo $eee -n $a$a$b$b$c$c)`, "AABB"},
{`$(echo -n "123")`, "123"},
{`$(echo -n hello world)`, "hello world"},
{`$(echo hello world | xargs echo -n)`, "hello world"},
{`$(echo \$CONTEXT)`, "abs"},
{"a = 'A'; b = 'B'; eee = '-e'; `echo $eee -n $a$a$b$b$c$c`", "AABB"},
{"`echo -n '123'`", "123"},
{"`echo -n hello world`", "hello world"},
{"`echo hello world | xargs echo -n`", "hello world"},
{"`echo \\$CONTEXT`", "abs"},
}
}
for _, tt := range tests {
evaluated := testEval(tt.input)

Expand Down

0 comments on commit 0cfbfe3

Please sign in to comment.