Skip to content
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,25 @@ for i := 0; i < len(s); i++ {
}
```

### Programs
### Programs/Scripts
```golang
// Programs are called by stating the program name preceded by an @.
@ls("-a")
// Programs/Scripts are called by stating the name preceded by an @.
@dir("/b")
```

```golang
// Similar to Bash/Batch, the output can be piped into another program.
@ls("-a") | @sort()
// Similar to Bash/Batch, the output can be piped into another program/script.
@dir("/b") | @sort("/r")
```

```golang
// To capture the output, just assign the call chain to variables.
stdout, stderr, code := @ls("-a") | @sort()
stdout, stderr, code := @dir("/b") | @sort("/r")
```

```golang
// To specify the path to a program/script, a string literal is used.
@`helper\dir.bat`("/b") // Equivalent to @"helper\\dir.bat"("/b")
```

### Imports
Expand Down
7 changes: 6 additions & 1 deletion converters/batch/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,13 @@ func forLabel(count int) string {
func (c *converter) StringToString(value string) string {
c.addLf()

// Escape all "!".
value = strings.ReplaceAll(value, "!", "^!")

// Replace "\n" with Batch newline value.
return strings.ReplaceAll(value, "\n", "!LF!")
value = strings.ReplaceAll(value, "\n", "!LF!")

return value
}

func (c *converter) Dump() (string, error) {
Expand Down
98 changes: 0 additions & 98 deletions main.go

This file was deleted.

28 changes: 20 additions & 8 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,16 @@ func (p *Parser) evaluateVarDefinition(ctx context) (Statement, error) {

// Check if the amount of values is equal to the amount of variable names.
if valuesTypesLen != variablesLen {
return nil, p.atError(fmt.Sprintf("got %d initialisation values but %d variables", valuesTypesLen, variablesLen), nextToken)
pluralInit := ""
pluralValues := ""

if valuesTypesLen != 1 {
pluralInit = "s"
}
if variablesLen != 1 {
pluralValues = "s"
}
return nil, p.atError(fmt.Sprintf("got %d initialisation value%s but %d variable%s", valuesTypesLen, pluralInit, variablesLen, pluralValues), nextToken)
}

// If a type has been specified, make sure the returned types fit this type.
Expand Down Expand Up @@ -1355,7 +1364,7 @@ func (p *Parser) evaluateFunctionDefinition(ctx context) (Statement, error) {
closingBrace := p.eat()

if closingBrace.Type() != lexer.CLOSING_ROUND_BRACKET {
return nil, p.expectedError("closing bracket", closingBrace)
return nil, p.expectedError(`")"`, closingBrace)
}
}
returnTypeToken := p.peek()
Expand Down Expand Up @@ -1994,7 +2003,7 @@ func (p *Parser) evaluateSingleExpression(ctx context) (Expression, error) {
closingToken := p.eat()

if closingToken.Type() != lexer.CLOSING_ROUND_BRACKET {
return nil, p.expectedError("closing bracket", closingToken)
return nil, p.expectedError(`")"`, closingToken)
}

// Handle slice instantiation.
Expand Down Expand Up @@ -2300,7 +2309,7 @@ func (p *Parser) evaluateArguments(typeName string, name string, params []Variab
openingBraceToken := p.eat()

if openingBraceToken.Type() != lexer.OPENING_ROUND_BRACKET {
return nil, p.expectedError("opening bracket", openingBraceToken)
return nil, p.expectedError(`"("`, openingBraceToken)
}
nextToken := p.peek()
args := []Expression{}
Expand Down Expand Up @@ -2347,7 +2356,7 @@ func (p *Parser) evaluateArguments(typeName string, name string, params []Variab
tokenType := nextToken.Type()

if !slices.Contains([]lexer.TokenType{lexer.COMMA, lexer.CLOSING_ROUND_BRACKET}, tokenType) {
err = p.expectedError("comma or closing bracket", nextToken)
err = p.expectedError(`"," or ")"`, nextToken)
break
} else if tokenType == lexer.COMMA {
p.eat()
Expand All @@ -2370,7 +2379,7 @@ func (p *Parser) evaluateArguments(typeName string, name string, params []Variab
closingBraceToken := p.eat()

if closingBraceToken.Type() != lexer.CLOSING_ROUND_BRACKET {
return nil, p.expectedError("closing bracket", closingBraceToken)
return nil, p.expectedError(`")"`, closingBraceToken)
}
return args, nil
}
Expand Down Expand Up @@ -2438,8 +2447,11 @@ func (p *Parser) evaluateAppCall(ctx context) (Call, error) {
nextToken = p.eat()
name := nextToken.Value()

if nextToken.Type() != lexer.IDENTIFIER {
return nil, p.expectedError("program identifier", nextToken)
switch nextToken.Type() {
case lexer.IDENTIFIER, lexer.STRING_LITERAL:
// Nothing to do, those cases are valid.
default:
return nil, p.expectedError("program identifier or string literal", nextToken)
}
args, err := p.evaluateArguments("program", name, nil, ctx)

Expand Down
27 changes: 27 additions & 0 deletions tests/appcall_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package tests

import (
"fmt"
"os"
"path"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -33,6 +35,31 @@ func TestLsCallPipeToGrepCallSuccess(t *testing.T) {
})
}

func TestBashFileFromSubDirCallCallPipeToGrepCallSuccess(t *testing.T) {
transpileBashFunc(t, func(dir string) (string, error) {
subdir := path.Join(dir, "subdir")
err := os.Mkdir(subdir, 0700)

if err != nil {
return "", err
}
bashFile := path.Join(subdir, "bash.sh")
err = os.WriteFile(bashFile, []byte("ls $1"), 0700)

if err != nil {
return "", err
}
return `
` + fmt.Sprintf(`var stdout, stderr, code = @"%s"("%s") | @grep(".tsh")`, bashFile, dir) + `

print(stdout, code)
`, nil
}, func(output string, err error) {
require.Nil(t, err)
require.Equal(t, "test.tsh 0", output)
})
}

func TestLsCallFail(t *testing.T) {
transpileBashFunc(t, func(dir string) (string, error) {
return `
Expand Down
27 changes: 27 additions & 0 deletions tests/appcall_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package tests

import (
"fmt"
"os"
"path"
"strings"
"testing"

Expand Down Expand Up @@ -34,6 +36,31 @@ func TestDirCallPipeToFindstrCallSuccess(t *testing.T) {
})
}

func TestBatFileFromSubDirCallPipeToFindstrCallSuccess(t *testing.T) {
transpileBatchFunc(t, func(dir string) (string, error) {
subdir := path.Join(dir, "subdir")
err := os.Mkdir(subdir, 0700)

if err != nil {
return "", err
}
batFile := path.Join(subdir, "bat.bat")
err = os.WriteFile(batFile, []byte("dir /b %1"), 0700)

if err != nil {
return "", err
}
return `
` + fmt.Sprintf(`var stdout, stderr, code = @"%s"("%s") | @findstr(".tsh")`, strings.ReplaceAll(strings.ReplaceAll(batFile, `/`, `\`), `\`, `\\`), strings.ReplaceAll(dir, `\`, `\\`)) + `

print(stdout, code)
`, nil
}, func(output string, err error) {
require.Nil(t, err)
require.Equal(t, "test.tsh 0", output)
})
}

func TestDirCallFail(t *testing.T) {
transpileBatchFunc(t, func(dir string) (string, error) {
return `
Expand Down
4 changes: 2 additions & 2 deletions tests/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func testCopySuccess(t *testing.T, transpilerFunc transpilerFunc) {
func testReadSuccess(t *testing.T, transpilerFunc transpilerFunc) {
file := "read-test.txt"
content := "Hello World"
os.WriteFile(file, []byte(content), 0x777)
os.WriteFile(file, []byte(content), 0700)
defer os.Remove(file)

transpilerFunc(t, `
Expand Down Expand Up @@ -156,7 +156,7 @@ func testCopyInFunctionSuccess(t *testing.T, transpilerFunc transpilerFunc) {
func testReadInFunctionSuccess(t *testing.T, transpilerFunc transpilerFunc) {
file := "read-test.txt"
content := "Hello World"
os.WriteFile(file, []byte(content), 0x777)
os.WriteFile(file, []byte(content), 0700)
defer os.Remove(file)

transpilerFunc(t, `
Expand Down
4 changes: 2 additions & 2 deletions tests/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func transpileFunc(t *testing.T, source sourceCallout, targetFileName string, co

if err == nil {
var code string
err = os.WriteFile(file, []byte(src), 0x777)
err = os.WriteFile(file, []byte(src), 0700)

require.Nil(t, err)
code, err = trans.Transpile(file, converter)
Expand All @@ -42,7 +42,7 @@ func transpileFunc(t *testing.T, source sourceCallout, targetFileName string, co
// If transpilation was successful, run the code.
if err == nil {
targetFile := filepath.Join(dir, targetFileName)
err = os.WriteFile(targetFile, []byte(code), 0x777)
err = os.WriteFile(targetFile, []byte(code), 0700)

require.Nil(t, err)
cmd := exec.Command(targetFile)
Expand Down