mirror of
https://github.com/Kugelschieber/asl.git
synced 2026-01-18 12:00:25 +00:00
Merge remote-tracking branch 'refs/remotes/DeKugelschieber/master'
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
**1.2.0**
|
||||||
|
|
||||||
|
* better error output
|
||||||
|
* concurrent compiling
|
||||||
|
* errors are handled per file and won't stop the whole compilation
|
||||||
|
* function name check for build in functions
|
||||||
|
* simpler syntax for "null" and unary buildin functions
|
||||||
|
|
||||||
|
**1.1.1**
|
||||||
|
|
||||||
|
* arrays can now be declared within expressions
|
||||||
|
* code keyword bug fix
|
||||||
|
|
||||||
**1.1.0**
|
**1.1.0**
|
||||||
|
|
||||||
* changed syntax of foreach
|
* changed syntax of foreach
|
||||||
|
|||||||
46
README.md
46
README.md
@@ -36,6 +36,19 @@ asl [-v|-r|-pretty|--help] <input directory> <output directory>
|
|||||||
asl ./missions/myMission/myScripts ./missions/myMission/compiledScripts
|
asl ./missions/myMission/myScripts ./missions/myMission/compiledScripts
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Since 1.2.0 ASL requires a [supportInfo](https://community.bistudio.com/wiki/supportInfo) file, which must be generated, named "types" and placed right next to the binary. So if you have the asl.exe there must also be a file called types right next to it. The content looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
t:DIARY_RECORD
|
||||||
|
t:LOCATION
|
||||||
|
b:ARRAY waypointattachobject SCALAR,OBJECT
|
||||||
|
b:OBJECT,GROUP enableattack BOOL
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
A current types file will be delivered with the current release, but not updated when Arma is.
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
### Comments
|
### Comments
|
||||||
@@ -68,6 +81,9 @@ var one = array[0];
|
|||||||
|
|
||||||
// accessing using a statement:
|
// accessing using a statement:
|
||||||
var zwo = array[33/3-2];
|
var zwo = array[33/3-2];
|
||||||
|
|
||||||
|
// it is possble to use arrays in expressions:
|
||||||
|
var emptyArray = one-[0];
|
||||||
```
|
```
|
||||||
|
|
||||||
### Control structures
|
### Control structures
|
||||||
@@ -138,6 +154,8 @@ var _x = add();
|
|||||||
// result in _x is 0
|
// result in _x is 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When trying to define a function with a name that exists in the build in function set, you'll get an compile error.
|
||||||
|
|
||||||
### Call build in commands
|
### Call build in commands
|
||||||
|
|
||||||
To call SQF build in commands (like hint, getDir, addItem, ...) we have to use a different syntax.
|
To call SQF build in commands (like hint, getDir, addItem, ...) we have to use a different syntax.
|
||||||
@@ -158,6 +176,13 @@ foo(x, y, z)(1, 2, 3);
|
|||||||
[x, y, z] foo [1, 2, 3];
|
[x, y, z] foo [1, 2, 3];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the build in function does not accept parameters or only on one side (unary function), it can be called with a single pair of brackets:
|
||||||
|
|
||||||
|
```
|
||||||
|
hint("your text");
|
||||||
|
shownWatch();
|
||||||
|
```
|
||||||
|
|
||||||
### Special functions
|
### Special functions
|
||||||
|
|
||||||
There are some special functions in SQF, which also require special syntax in ASL. The examples presented here show how they are written in ASL and what the output will look like. Remember that ASL is case sensitive!
|
There are some special functions in SQF, which also require special syntax in ASL. The examples presented here show how they are written in ASL and what the output will look like. Remember that ASL is case sensitive!
|
||||||
@@ -231,7 +256,7 @@ Keywords should not be used as identifiers. Here is a full list of all keywords
|
|||||||
| var |
|
| var |
|
||||||
| if |
|
| if |
|
||||||
| while |
|
| while |
|
||||||
| witch |
|
| switch |
|
||||||
| for |
|
| for |
|
||||||
| foreach |
|
| foreach |
|
||||||
| func |
|
| func |
|
||||||
@@ -248,22 +273,18 @@ Keywords should not be used as identifiers. Here is a full list of all keywords
|
|||||||
|
|
||||||
## What's missing?
|
## What's missing?
|
||||||
|
|
||||||
The following features are not implemented yet, but will be in 1.1.0 or a future version:
|
The following features are not implemented yet, but will be in 1.3.0 or a future version:
|
||||||
|
|
||||||
* scopes
|
* scopes
|
||||||
* else if
|
* else if
|
||||||
* arrays within expressions (like someArray-[someEntity])
|
* selector in expression
|
||||||
|
|
||||||
scopes won't be supported, since they are a stupid concept and can be replaced by functions.
|
scopes won't be supported, since they are a stupid concept and can be replaced by functions.
|
||||||
There is a simple workaround for arrays within expressions:
|
|
||||||
|
Selectors in expressions do not work (yet):
|
||||||
|
|
||||||
```
|
```
|
||||||
// want: ... forEach allCurators-[myCurator];
|
var x = ([1, 2, 3]-[1, 2])[0]; // should result in 3, but does not work
|
||||||
|
|
||||||
var myCuratorArray = [myCurator]; // or a more complex expression within array
|
|
||||||
foreach allCurators-myCuratorArray {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
@@ -278,6 +299,11 @@ For further information you can read the SQF tutorial and documentation of scrip
|
|||||||
* [Scripting commands](https://community.bistudio.com/wiki/Category:Scripting_Commands_Arma_3)
|
* [Scripting commands](https://community.bistudio.com/wiki/Category:Scripting_Commands_Arma_3)
|
||||||
* [Scripting preprocessor](https://community.bistudio.com/wiki/PreProcessor_Commands)
|
* [Scripting preprocessor](https://community.bistudio.com/wiki/PreProcessor_Commands)
|
||||||
|
|
||||||
|
Interesting pages to visit:
|
||||||
|
|
||||||
|
* [Bohemia forum topic](https://forums.bistudio.com/topic/185649-asl-arma-scripting-language-compiler/)
|
||||||
|
* [Armaholic page](http://www.armaholic.com/page.php?id=29720)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|||||||
9
ToDo.md
9
ToDo.md
@@ -6,14 +6,15 @@
|
|||||||
* ~~pretty/minified printing~~
|
* ~~pretty/minified printing~~
|
||||||
* ~~usage~~
|
* ~~usage~~
|
||||||
* ~~recursive compiling~~
|
* ~~recursive compiling~~
|
||||||
* concurrent compiling
|
* ~~concurrent compiling~~
|
||||||
* ~~inline buildin function call -> foo(a)(bar(x)(y));~~
|
* ~~inline buildin function call -> foo(a)(bar(x)(y));~~
|
||||||
* ~~negative values e.g. -1, operator !~~
|
* ~~negative values e.g. -1, operator !~~
|
||||||
* ~~tokenizer splits commands like "format" -> for, mat~~
|
* ~~tokenizer splits commands like "format" -> for, mat~~
|
||||||
* ~~try ... catch~~
|
* ~~try ... catch~~
|
||||||
* better error reporting
|
* ~~better error reporting~~
|
||||||
- recover panic
|
- ~~recover panic~~
|
||||||
- line, column
|
- ~~line, column~~
|
||||||
|
* type check for functions
|
||||||
|
|
||||||
## Special cases
|
## Special cases
|
||||||
|
|
||||||
|
|||||||
206
src/main/asl.go
206
src/main/asl.go
@@ -1,34 +1,36 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"parser"
|
|
||||||
"tokenizer"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
|
||||||
"os"
|
"os"
|
||||||
|
"parser"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"tokenizer"
|
||||||
|
"types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
extension = ".asl"
|
extension = ".asl"
|
||||||
sqfextension = ".sqf"
|
sqfextension = ".sqf"
|
||||||
PathSeparator = string(os.PathSeparator)
|
typeinfo = "types"
|
||||||
|
PathSeparator = string(os.PathSeparator)
|
||||||
)
|
)
|
||||||
|
|
||||||
type ASLFile struct {
|
type ASLFile struct {
|
||||||
in string
|
in string
|
||||||
out string
|
out string
|
||||||
newname string
|
newname string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
recursive bool = false
|
recursive bool = false
|
||||||
pretty bool = false
|
pretty bool = false
|
||||||
exit bool = false
|
exit bool = false
|
||||||
aslFiles []ASLFile
|
aslFiles []ASLFile
|
||||||
inDir string
|
inDir string
|
||||||
)
|
)
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
@@ -41,78 +43,118 @@ func usage() {
|
|||||||
fmt.Println("<output directory> output directory, directory structure will be created corresponding to input directory")
|
fmt.Println("<output directory> output directory, directory structure will be created corresponding to input directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses compiler flags.
|
||||||
func flags(flag string) bool {
|
func flags(flag string) bool {
|
||||||
flag = strings.ToLower(flag)
|
flag = strings.ToLower(flag)
|
||||||
|
|
||||||
if flag[0] == '-' {
|
if flag[0] != '-' {
|
||||||
if flag == "-v" {
|
return false
|
||||||
fmt.Println("asl version "+version)
|
}
|
||||||
exit = true
|
|
||||||
} else if flag == "-r" {
|
|
||||||
recursive = true
|
|
||||||
} else if flag == "-pretty" {
|
|
||||||
pretty = true
|
|
||||||
} else if flag == "--help" {
|
|
||||||
usage()
|
|
||||||
exit = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
if flag == "-v" {
|
||||||
}
|
fmt.Println("asl version " + version)
|
||||||
|
exit = true
|
||||||
|
} else if flag == "-r" {
|
||||||
|
recursive = true
|
||||||
|
} else if flag == "-pretty" {
|
||||||
|
pretty = true
|
||||||
|
} else if flag == "--help" {
|
||||||
|
usage()
|
||||||
|
exit = true
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loads types from types file.
|
||||||
|
// If none is provided, an error will be printed.
|
||||||
|
func loadTypes() {
|
||||||
|
if err := types.LoadTypes(typeinfo); err != nil {
|
||||||
|
fmt.Println("No 'types' file provided. Please add type information to this file from 'supportInfo' script command output.")
|
||||||
|
exit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a list of all ASL files to compile.
|
||||||
func readAslFiles(path string) {
|
func readAslFiles(path string) {
|
||||||
dir, err := ioutil.ReadDir(path)
|
dir, err := ioutil.ReadDir(path)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error reading in directory!")
|
fmt.Println("Error reading in directory!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(dir); i++ {
|
for i := 0; i < len(dir); i++ {
|
||||||
name := dir[i].Name()
|
name := dir[i].Name()
|
||||||
|
|
||||||
if dir[i].IsDir() && recursive {
|
if dir[i].IsDir() && recursive {
|
||||||
readAslFiles(filepath.FromSlash(path+PathSeparator+name))
|
readAslFiles(filepath.FromSlash(path + PathSeparator + name))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dir[i].IsDir() && strings.ToLower(filepath.Ext(name)) == extension {
|
if !dir[i].IsDir() && strings.ToLower(filepath.Ext(name)) == extension {
|
||||||
in := filepath.FromSlash(path+PathSeparator+dir[i].Name())
|
in := filepath.FromSlash(path + PathSeparator + dir[i].Name())
|
||||||
out := filepath.FromSlash("./"+path[len(inDir):len(path)])
|
out := filepath.FromSlash("./" + path[len(inDir):len(path)])
|
||||||
newname := name[:len(name)-len(filepath.Ext(name))]
|
newname := name[:len(name)-len(filepath.Ext(name))]
|
||||||
|
|
||||||
file := ASLFile{in, out, newname}
|
file := ASLFile{in, out, newname}
|
||||||
aslFiles = append(aslFiles, file)
|
aslFiles = append(aslFiles, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recovers and prints thrown error.
|
||||||
|
func recoverCompileError(file string, waiter chan bool) {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println("Compile error in file "+file+":", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
waiter <- true // the show must go on
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiles a single ASL file.
|
||||||
|
func compileFile(path string, file ASLFile, waiter chan bool) {
|
||||||
|
defer recoverCompileError(file.in, waiter)
|
||||||
|
|
||||||
|
// read file
|
||||||
|
out := filepath.FromSlash(path + PathSeparator + file.out + PathSeparator + file.newname + sqfextension)
|
||||||
|
fmt.Println(file.in + " -> " + out)
|
||||||
|
code, err := ioutil.ReadFile(file.in)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading file: " + file.in)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile
|
||||||
|
token := tokenizer.Tokenize(code, false)
|
||||||
|
compiler := parser.Compiler{}
|
||||||
|
sqf := compiler.Parse(token, pretty)
|
||||||
|
|
||||||
|
os.MkdirAll(filepath.FromSlash(path+PathSeparator+file.out), 0777)
|
||||||
|
err = ioutil.WriteFile(out, []byte(sqf), 0666)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error writing file: " + file.out)
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
waiter <- true // done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiles ASL files concurrently.
|
||||||
func compile(path string) {
|
func compile(path string) {
|
||||||
for i := 0; i < len(aslFiles); i++ {
|
waiter := make(chan bool, len(aslFiles))
|
||||||
out := filepath.FromSlash(path+PathSeparator+aslFiles[i].out+PathSeparator+aslFiles[i].newname+sqfextension)
|
|
||||||
fmt.Println(aslFiles[i].in+" -> "+out)
|
|
||||||
code, err := ioutil.ReadFile(aslFiles[i].in)
|
|
||||||
|
|
||||||
if err != nil {
|
// fire compile
|
||||||
fmt.Println("Error reading file: "+aslFiles[i].in)
|
for i := 0; i < len(aslFiles); i++ {
|
||||||
continue
|
go compileFile(path, aslFiles[i], waiter)
|
||||||
}
|
}
|
||||||
|
|
||||||
token := tokenizer.Tokenize(code)
|
// wait until all files are compiled
|
||||||
compiler := parser.Compiler{}
|
for i := 0; i < len(aslFiles); i++ {
|
||||||
sqf := compiler.Parse(token, pretty)
|
<-waiter
|
||||||
|
}
|
||||||
os.MkdirAll(filepath.FromSlash(path+PathSeparator+aslFiles[i].out), 0777)
|
|
||||||
err = ioutil.WriteFile(out, []byte(sqf), 0666)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error writing file: "+aslFiles[i].out)
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -120,29 +162,37 @@ func main() {
|
|||||||
|
|
||||||
// flags
|
// flags
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
usage()
|
usage()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for i = 1; i < len(args) && flags(args[i]); i++ {}
|
for i = 1; i < len(args) && flags(args[i]); i++ {
|
||||||
|
}
|
||||||
|
|
||||||
if exit {
|
if exit {
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// load type information
|
||||||
|
loadTypes()
|
||||||
|
|
||||||
|
if exit {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// in/out parameter
|
// in/out parameter
|
||||||
out := ""
|
out := ""
|
||||||
|
|
||||||
if i < len(args) {
|
if i < len(args) {
|
||||||
inDir = args[i]
|
inDir = args[i]
|
||||||
i++
|
i++
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if i < len(args) {
|
if i < len(args) {
|
||||||
out = args[i]
|
out = args[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
readAslFiles(inDir)
|
readAslFiles(inDir)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"tokenizer"
|
"tokenizer"
|
||||||
|
"types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const new_line = "\r\n"
|
const new_line = "\r\n"
|
||||||
@@ -10,7 +12,7 @@ const new_line = "\r\n"
|
|||||||
// and writes SQF code into desired location.
|
// and writes SQF code into desired location.
|
||||||
func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
|
func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
|
||||||
if !c.initParser(token, prettyPrinting) {
|
if !c.initParser(token, prettyPrinting) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
for c.tokenIndex < len(token) {
|
for c.tokenIndex < len(token) {
|
||||||
@@ -21,9 +23,9 @@ func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseBlock() {
|
func (c *Compiler) parseBlock() {
|
||||||
if c.get().Preprocessor {
|
if c.get().Preprocessor {
|
||||||
c.parsePreprocessor()
|
c.parsePreprocessor()
|
||||||
} else if c.accept("var") {
|
} else if c.accept("var") {
|
||||||
c.parseVar()
|
c.parseVar()
|
||||||
} else if c.accept("if") {
|
} else if c.accept("if") {
|
||||||
c.parseIf()
|
c.parseIf()
|
||||||
@@ -42,9 +44,9 @@ func (c *Compiler) parseBlock() {
|
|||||||
} else if c.accept("try") {
|
} else if c.accept("try") {
|
||||||
c.parseTryCatch()
|
c.parseTryCatch()
|
||||||
} else if c.accept("exitwith") {
|
} else if c.accept("exitwith") {
|
||||||
c.parseExitWith()
|
c.parseExitWith()
|
||||||
} else if c.accept("waituntil") {
|
} else if c.accept("waituntil") {
|
||||||
c.parseWaitUntil()
|
c.parseWaitUntil()
|
||||||
} else if c.accept("case") || c.accept("default") {
|
} else if c.accept("case") || c.accept("default") {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@@ -57,9 +59,9 @@ func (c *Compiler) parseBlock() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parsePreprocessor() {
|
func (c *Compiler) parsePreprocessor() {
|
||||||
// we definitely want a new line before and after
|
// we definitely want a new line before and after
|
||||||
c.appendOut(new_line+c.get().Token+new_line, false)
|
c.appendOut(new_line+c.get().Token+new_line, false)
|
||||||
c.next()
|
c.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseVar() {
|
func (c *Compiler) parseVar() {
|
||||||
@@ -70,34 +72,35 @@ func (c *Compiler) parseVar() {
|
|||||||
if c.accept("=") {
|
if c.accept("=") {
|
||||||
c.next()
|
c.next()
|
||||||
c.appendOut(" = ", false)
|
c.appendOut(" = ", false)
|
||||||
|
c.parseExpression(true)
|
||||||
if c.accept("[") {
|
|
||||||
c.parseArray()
|
|
||||||
} else {
|
|
||||||
c.parseExpression(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.expect(";")
|
c.expect(";")
|
||||||
c.appendOut(";", true)
|
c.appendOut(";", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseArray() {
|
func (c *Compiler) parseArray(out bool) string {
|
||||||
|
output := ""
|
||||||
c.expect("[")
|
c.expect("[")
|
||||||
c.appendOut("[", false)
|
output += "["
|
||||||
|
|
||||||
if !c.accept("]") {
|
if !c.accept("]") {
|
||||||
c.parseExpression(true)
|
output += c.parseExpression(false)
|
||||||
|
|
||||||
for c.accept(",") {
|
for c.accept(",") {
|
||||||
c.next()
|
c.next()
|
||||||
c.appendOut(",", false)
|
output += "," + c.parseExpression(false)
|
||||||
c.parseExpression(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.expect("]")
|
c.expect("]")
|
||||||
c.appendOut("]", false)
|
output += "]"
|
||||||
|
|
||||||
|
if out {
|
||||||
|
c.appendOut(output, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseIf() {
|
func (c *Compiler) parseIf() {
|
||||||
@@ -214,6 +217,12 @@ func (c *Compiler) parseForeach() {
|
|||||||
|
|
||||||
func (c *Compiler) parseFunction() {
|
func (c *Compiler) parseFunction() {
|
||||||
c.expect("func")
|
c.expect("func")
|
||||||
|
|
||||||
|
// check for build in function
|
||||||
|
if buildin := types.GetFunction(c.get().Token); buildin != nil {
|
||||||
|
panic(errors.New(c.get().Token + " is a build in function, choose a different name"))
|
||||||
|
}
|
||||||
|
|
||||||
c.appendOut(c.get().Token+" = {", true)
|
c.appendOut(c.get().Token+" = {", true)
|
||||||
c.next()
|
c.next()
|
||||||
c.expect("(")
|
c.expect("(")
|
||||||
@@ -238,12 +247,12 @@ func (c *Compiler) parseFunctionParameter() {
|
|||||||
c.next()
|
c.next()
|
||||||
|
|
||||||
if c.accept("=") {
|
if c.accept("=") {
|
||||||
c.next()
|
c.next()
|
||||||
value := c.get().Token
|
value := c.get().Token
|
||||||
c.next()
|
c.next()
|
||||||
c.appendOut("[\""+name+"\","+value+"]", false)
|
c.appendOut("[\""+name+"\","+value+"]", false)
|
||||||
} else {
|
} else {
|
||||||
c.appendOut("\""+name+"\"", false)
|
c.appendOut("\""+name+"\"", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.accept(")") {
|
if !c.accept(")") {
|
||||||
@@ -278,47 +287,47 @@ func (c *Compiler) parseTryCatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseExitWith() {
|
func (c *Compiler) parseExitWith() {
|
||||||
c.expect("exitwith")
|
c.expect("exitwith")
|
||||||
c.expect("{")
|
c.expect("{")
|
||||||
c.appendOut("if (true) exitWith {", true)
|
c.appendOut("if (true) exitWith {", true)
|
||||||
c.parseBlock()
|
c.parseBlock()
|
||||||
c.expect("}")
|
c.expect("}")
|
||||||
c.appendOut("};", true)
|
c.appendOut("};", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseWaitUntil() {
|
func (c *Compiler) parseWaitUntil() {
|
||||||
c.expect("waituntil")
|
c.expect("waituntil")
|
||||||
c.expect("(")
|
c.expect("(")
|
||||||
c.appendOut("waitUntil {", false)
|
c.appendOut("waitUntil {", false)
|
||||||
c.parseExpression(true)
|
c.parseExpression(true)
|
||||||
|
|
||||||
if c.accept(";") {
|
if c.accept(";") {
|
||||||
c.next()
|
c.next()
|
||||||
c.appendOut(";", false)
|
c.appendOut(";", false)
|
||||||
c.parseExpression(true)
|
c.parseExpression(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.expect(")")
|
c.expect(")")
|
||||||
c.expect(";")
|
c.expect(";")
|
||||||
c.appendOut("};", true)
|
c.appendOut("};", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseInlineCode() string {
|
func (c *Compiler) parseInlineCode() string {
|
||||||
c.expect("code")
|
c.expect("code")
|
||||||
c.expect("(")
|
c.expect("(")
|
||||||
|
|
||||||
code := c.get().Token
|
code := c.get().Token
|
||||||
c.next()
|
c.next()
|
||||||
output := "{}"
|
output := "{}"
|
||||||
|
|
||||||
if len(code) > 2 {
|
if len(code) > 2 {
|
||||||
compiler := Compiler{}
|
compiler := Compiler{}
|
||||||
output = "{"+compiler.Parse(tokenizer.Tokenize([]byte(code[1:len(code)-1])), false)+"}"
|
output = "{" + compiler.Parse(tokenizer.Tokenize([]byte(code[1:len(code)-1]), true), false) + "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
c.expect(")")
|
c.expect(")")
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything that does not start with a keyword.
|
// Everything that does not start with a keyword.
|
||||||
@@ -358,30 +367,22 @@ func (c *Compiler) parseFunctionCall(out bool, name string) string {
|
|||||||
output := ""
|
output := ""
|
||||||
|
|
||||||
c.expect("(")
|
c.expect("(")
|
||||||
leftParams, leftParamCount := c.parseParameter(false)
|
paramsStr, paramCount := c.parseParameter(false)
|
||||||
c.expect(")")
|
c.expect(")")
|
||||||
|
|
||||||
if c.accept("(") {
|
// buildin function
|
||||||
// buildin function
|
buildin := types.GetFunction(name)
|
||||||
c.next()
|
|
||||||
rightParams, rightParamCount := c.parseParameter(false)
|
|
||||||
c.expect(")")
|
|
||||||
|
|
||||||
if leftParamCount > 1 {
|
if buildin != nil {
|
||||||
leftParams = "[" + leftParams + "]"
|
if buildin.Type == types.NULL {
|
||||||
}
|
output = name
|
||||||
|
} else if buildin.Type == types.UNARY {
|
||||||
if rightParamCount > 1 {
|
output = c.parseUnaryFunction(name, paramsStr, paramCount)
|
||||||
rightParams = "[" + rightParams + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftParamCount > 0 {
|
|
||||||
output = leftParams + " " + name + " " + rightParams
|
|
||||||
} else {
|
} else {
|
||||||
output = name + " " + rightParams
|
output = c.parseBinaryFunction(name, paramsStr, buildin, paramCount)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
output = "[" + leftParams + "] call " + name
|
output = "[" + paramsStr + "] call " + name
|
||||||
}
|
}
|
||||||
|
|
||||||
if out {
|
if out {
|
||||||
@@ -391,12 +392,49 @@ func (c *Compiler) parseFunctionCall(out bool, name string) string {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseUnaryFunction(name, paramsStr string, paramCount int) string {
|
||||||
|
output := ""
|
||||||
|
|
||||||
|
if paramCount == 1 {
|
||||||
|
output = name + " " + paramsStr
|
||||||
|
} else {
|
||||||
|
output = "[" + paramsStr + "] call " + name
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseBinaryFunction(name string, leftParamsStr string, buildin *types.FunctionType, paramCount int) string {
|
||||||
|
output := ""
|
||||||
|
|
||||||
|
c.next()
|
||||||
|
rightParamsStr, rightParamCount := c.parseParameter(false)
|
||||||
|
c.expect(")")
|
||||||
|
|
||||||
|
if paramCount > 1 {
|
||||||
|
leftParamsStr = "[" + leftParamsStr + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if rightParamCount > 1 {
|
||||||
|
rightParamsStr = "[" + rightParamsStr + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramCount > 0 {
|
||||||
|
output = leftParamsStr + " " + name + " " + rightParamsStr
|
||||||
|
} else {
|
||||||
|
output = name + " " + rightParamsStr
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseParameter(out bool) (string, int) {
|
func (c *Compiler) parseParameter(out bool) (string, int) {
|
||||||
output := ""
|
output := ""
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
for !c.accept(")") {
|
for !c.accept(")") {
|
||||||
output += c.parseExpression(out)
|
expr := c.parseExpression(out)
|
||||||
|
output += expr
|
||||||
count++
|
count++
|
||||||
|
|
||||||
if !c.accept(")") {
|
if !c.accept(")") {
|
||||||
@@ -458,17 +496,19 @@ func (c *Compiler) parseIdentifier() string {
|
|||||||
output := ""
|
output := ""
|
||||||
|
|
||||||
if c.accept("code") {
|
if c.accept("code") {
|
||||||
output += c.parseInlineCode()
|
output += c.parseInlineCode()
|
||||||
} else if c.seek("(") && !c.accept("!") && !c.accept("-") {
|
} else if c.seek("(") && !c.accept("!") && !c.accept("-") {
|
||||||
name := c.get().Token
|
name := c.get().Token
|
||||||
c.next()
|
c.next()
|
||||||
output = "(" + c.parseFunctionCall(false, name) + ")"
|
output = "(" + c.parseFunctionCall(false, name) + ")"
|
||||||
|
} else if c.accept("[") {
|
||||||
|
output += c.parseArray(false)
|
||||||
} else if c.seek("[") {
|
} else if c.seek("[") {
|
||||||
output += "("+c.get().Token
|
output += "(" + c.get().Token
|
||||||
c.next()
|
c.next()
|
||||||
c.expect("[")
|
c.expect("[")
|
||||||
output += " select ("+c.parseExpression(false)+"))"
|
output += " select (" + c.parseExpression(false) + "))"
|
||||||
c.expect("]")
|
c.expect("]")
|
||||||
} else if c.accept("!") || c.accept("-") {
|
} else if c.accept("!") || c.accept("-") {
|
||||||
output = c.get().Token
|
output = c.get().Token
|
||||||
c.next()
|
c.next()
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"errors"
|
||||||
"tokenizer"
|
"strconv"
|
||||||
|
"tokenizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
tokens []tokenizer.Token
|
tokens []tokenizer.Token
|
||||||
tokenIndex int
|
tokenIndex int
|
||||||
out string
|
out string
|
||||||
offset int
|
offset int
|
||||||
pretty bool
|
pretty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initilizes the parser.
|
// Initilizes the parser.
|
||||||
@@ -38,7 +39,7 @@ func (c *Compiler) accept(token string) bool {
|
|||||||
// Throws if current token does not match expected one.
|
// Throws if current token does not match expected one.
|
||||||
func (c *Compiler) expect(token string) {
|
func (c *Compiler) expect(token string) {
|
||||||
if !c.tokenEqual(token, c.get()) {
|
if !c.tokenEqual(token, c.get()) {
|
||||||
panic("Parse error, expected '" + token + "' but was '" + c.get().Token + "' in line "+strconv.Itoa(c.get().Line)+" at "+strconv.Itoa(c.get().Column))
|
panic(errors.New("Parse error, expected '" + token + "' but was '" + c.get().Token + "' in line " + strconv.Itoa(c.get().Line) + " at " + strconv.Itoa(c.get().Column)))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.next()
|
c.next()
|
||||||
@@ -62,7 +63,7 @@ func (c *Compiler) next() {
|
|||||||
// Returns current token or throws, if no more tokens are available.
|
// Returns current token or throws, if no more tokens are available.
|
||||||
func (c *Compiler) get() tokenizer.Token {
|
func (c *Compiler) get() tokenizer.Token {
|
||||||
if c.tokenIndex >= len(c.tokens) {
|
if c.tokenIndex >= len(c.tokens) {
|
||||||
panic("No more tokens")
|
panic(errors.New("No more tokens"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.tokens[c.tokenIndex]
|
return c.tokens[c.tokenIndex]
|
||||||
@@ -1,166 +1,200 @@
|
|||||||
package parser_test
|
package parser_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"tokenizer"
|
|
||||||
"parser"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"parser"
|
||||||
"testing"
|
"testing"
|
||||||
|
"tokenizer"
|
||||||
|
"types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
types_file = "../../test/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParserDeclaration(t *testing.T) {
|
func TestParserDeclaration(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_var.asl")
|
got := getCompiled(t, "../../test/tokenizer_var.asl")
|
||||||
want := "x = 1;\r\narray = [1,2,3];\r\n"
|
want := "x = 1;\r\narray = [1,2,3];\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserAssignment(t *testing.T) {
|
func TestParserAssignment(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_assignment.asl")
|
got := getCompiled(t, "../../test/parser_assignment.asl")
|
||||||
want := "x = 1;\r\n"
|
want := "x = 1;\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserIf(t *testing.T) {
|
func TestParserIf(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_if.asl")
|
got := getCompiled(t, "../../test/tokenizer_if.asl")
|
||||||
want := "if (a<b) then {\r\n};\r\n"
|
want := "if (a<b) then {\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserWhile(t *testing.T) {
|
func TestParserWhile(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_while.asl")
|
got := getCompiled(t, "../../test/tokenizer_while.asl")
|
||||||
want := "while {true} do {\r\n};"
|
want := "while {true} do {\r\n};"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserFor(t *testing.T) {
|
func TestParserFor(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_for.asl")
|
got := getCompiled(t, "../../test/tokenizer_for.asl")
|
||||||
want := "for [{i=0}, {i<100}, {i=i+1}] do {\r\n};\r\n"
|
want := "for [{i=0}, {i<100}, {i=i+1}] do {\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserForeach(t *testing.T) {
|
func TestParserForeach(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_foreach.asl")
|
got := getCompiled(t, "../../test/tokenizer_foreach.asl")
|
||||||
want := "{\r\nunit = _x;\r\n} forEach (allUnits);\r\n"
|
want := "{\r\nunit = _x;\r\n} forEach (allUnits);\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserSwitch(t *testing.T) {
|
func TestParserSwitch(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_switch.asl")
|
got := getCompiled(t, "../../test/tokenizer_switch.asl")
|
||||||
want := "switch (x) do {\r\ncase 1:\r\n{\r\nx = 1;\r\n};\r\ncase 2:\r\n{\r\nx = 2;\r\n};\r\ndefault:\r\n{\r\nx = 3;\r\n};\r\n};\r\n"
|
want := "switch (x) do {\r\ncase 1:\r\n{\r\nx = 1;\r\n};\r\ncase 2:\r\n{\r\nx = 2;\r\n};\r\ndefault:\r\n{\r\nx = 3;\r\n};\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserFunction(t *testing.T) {
|
func TestParserFunction(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_func.asl")
|
got := getCompiled(t, "../../test/tokenizer_func.asl")
|
||||||
want := "TestFunction = {\r\nparams [\"param0\",\"param1\"];\r\nreturn true;\r\n};\r\n"
|
want := "TestFunction = {\r\nparams [\"param0\",\"param1\"];\r\nreturn true;\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserAssignResult(t *testing.T) {
|
func TestParserAssignResult(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_assign_result.asl")
|
got := getCompiled(t, "../../test/parser_assign_result.asl")
|
||||||
want := "x = ([1, 2, 3] call foo);\r\ny = ([1, 2, 3] call bar);\r\n"
|
want := "x = ([1, 2, 3] call foo);\r\ny = ([1, 2, 3] call bar);\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserExpression(t *testing.T) {
|
func TestParserExpression(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_expression.asl")
|
got := getCompiled(t, "../../test/parser_expression.asl")
|
||||||
want := "x = -(1+(2+3))/(6*(someVariable+99-100))-(20)+!anotherVariable+([] call foo);\r\n"
|
want := "x = -(1+(2+3))/(6*(someVariable+99-100))-(20)+!anotherVariable+([] call foo);\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserExpression2(t *testing.T) {
|
func TestParserExpression2(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_expression2.asl")
|
got := getCompiled(t, "../../test/parser_expression2.asl")
|
||||||
want := "x = true||(3>=4&&5<8);\r\n"
|
want := "x = true||(3>=4&&5<8);\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserFunctionCall(t *testing.T) {
|
func TestParserFunctionCall(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_func_call.asl")
|
got := getCompiled(t, "../../test/parser_func_call.asl")
|
||||||
want := "myFunc = {\r\nparams [\"a\",\"b\"];\r\nreturn a>b;\r\n};\r\n[1+3/4, 2-(66*22)/3-((123))] call myFunc;\r\n"
|
want := "myFunc = {\r\nparams [\"a\",\"b\"];\r\nreturn a>b;\r\n};\r\n[1+3/4, 2-(66*22)/3-((123))] call myFunc;\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserBuildinFunctionCall(t *testing.T) {
|
func TestParserNullBuildinFunctionCall(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_buildin_func.asl")
|
types.LoadTypes(types_file)
|
||||||
want := "_x = (([player, foo] getVar bar) setHit [\"head\", \"tail\"]);\r\n"
|
|
||||||
|
got := getCompiled(t, "../../test/parser_null_buildin_func.asl")
|
||||||
|
want := "_volume = (radioVolume);\r\n"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserUnaryBuildinFunctionCall(t *testing.T) {
|
||||||
|
types.LoadTypes(types_file)
|
||||||
|
|
||||||
|
got := getCompiled(t, "../../test/parser_unary_buildin_func.asl")
|
||||||
|
want := "_isReady = (unitReady soldier);\r\n"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserBinaryBuildinFunctionCall(t *testing.T) {
|
||||||
|
types.LoadTypes(types_file)
|
||||||
|
|
||||||
|
got := getCompiled(t, "../../test/parser_binary_buildin_func.asl")
|
||||||
|
want := "someCar setHit [\"motor\", 1];\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserOperator(t *testing.T) {
|
func TestParserOperator(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_operator.asl")
|
got := getCompiled(t, "../../test/parser_operator.asl")
|
||||||
want := "if (x==y&&x!=y&&x<=y&&x>=y&&x<y&&x>y) then {\r\n};\r\n"
|
want := "if (x==y&&x!=y&&x<=y&&x>=y&&x<y&&x>y) then {\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserTryCatch(t *testing.T) {
|
func TestParserTryCatch(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_try_catch.asl")
|
got := getCompiled(t, "../../test/parser_try_catch.asl")
|
||||||
want := "try {\r\n} catch {\r\n};\r\n"
|
want := "try {\r\n} catch {\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserNegationFunctionCall(t *testing.T) {
|
func TestParserNegationFunctionCall(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_negation.asl")
|
got := getCompiled(t, "../../test/parser_negation.asl")
|
||||||
want := "x = !([] call foo);\r\n"
|
want := "x = !([] call foo);\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserExitWith(t *testing.T) {
|
func TestParserExitWith(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_exitwith.asl")
|
got := getCompiled(t, "../../test/parser_exitwith.asl")
|
||||||
want := "if (true) exitWith {\r\n};\r\n"
|
want := "if (true) exitWith {\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserWaitUntil(t *testing.T) {
|
func TestParserWaitUntil(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_waituntil.asl")
|
got := getCompiled(t, "../../test/parser_waituntil.asl")
|
||||||
want := "waitUntil {x=x+1;x<100};\r\n"
|
want := "waitUntil {x=x+1;x<100};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserArray(t *testing.T) {
|
func TestParserArray(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_array.asl")
|
got := getCompiled(t, "../../test/parser_array.asl")
|
||||||
want := "x = [1,2,3];\r\ny = (x select (1));\r\n"
|
want := "x = [1,2,3];\r\ny = (x select (1));\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserFunctionParams(t *testing.T) {
|
func TestParserFunctionParams(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_func_params.asl")
|
got := getCompiled(t, "../../test/parser_func_params.asl")
|
||||||
want := "myFunc = {\r\nparams [[\"a\",1],[\"b\",2]];\r\nreturn a+b;\r\n};\r\n"
|
want := "myFunc = {\r\nparams [[\"a\",1],[\"b\",2]];\r\nreturn a+b;\r\n};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserInlineCode(t *testing.T) {
|
func TestParserInlineCode(t *testing.T) {
|
||||||
got := getCompiled(t, "test/parser_code.asl")
|
got := getCompiled(t, "../../test/parser_code.asl")
|
||||||
want := "inline_code = {a = 1;b = 2;if (a<b) then {[] call foo;};};\r\n"
|
want := "inline_code = {a = 1;b = 2;if (a<b) then {[] call foo;};};\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParserPreprocessor(t *testing.T) {
|
func TestParserPreprocessor(t *testing.T) {
|
||||||
got := getCompiled(t, "test/tokenizer_preprocessor.asl")
|
types.LoadTypes(types_file)
|
||||||
|
|
||||||
|
got := getCompiled(t, "../../test/tokenizer_preprocessor.asl")
|
||||||
want := "\r\n#define HELLO_WORLD \"Hello World!\"\r\nhint HELLO_WORLD;\r\n"
|
want := "\r\n#define HELLO_WORLD \"Hello World!\"\r\nhint HELLO_WORLD;\r\n"
|
||||||
|
|
||||||
equal(t, got, want)
|
equal(t, got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParserExpressionArray(t *testing.T) {
|
||||||
|
got := getCompiled(t, "../../test/parser_expression_array.asl")
|
||||||
|
want := "x = [1,2,3]-[2,3];\r\n"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
func getCompiled(t *testing.T, file string) string {
|
func getCompiled(t *testing.T, file string) string {
|
||||||
code, err := ioutil.ReadFile(file)
|
code, err := ioutil.ReadFile(file)
|
||||||
|
|
||||||
@@ -169,7 +203,7 @@ func getCompiled(t *testing.T, file string) string {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens := tokenizer.Tokenize(code)
|
tokens := tokenizer.Tokenize(code, false)
|
||||||
compiler := parser.Compiler{}
|
compiler := parser.Compiler{}
|
||||||
|
|
||||||
return compiler.Parse(tokens, true)
|
return compiler.Parse(tokens, true)
|
||||||
|
|||||||
@@ -5,60 +5,66 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Token string
|
Token string
|
||||||
Preprocessor bool
|
Preprocessor bool
|
||||||
Line int
|
Line int
|
||||||
Column int
|
Column int
|
||||||
}
|
}
|
||||||
|
|
||||||
var delimiter = []byte{
|
var (
|
||||||
'=',
|
delimiter = []byte{
|
||||||
';',
|
'=',
|
||||||
'{',
|
';',
|
||||||
'}',
|
'{',
|
||||||
'(',
|
'}',
|
||||||
')',
|
'(',
|
||||||
'[',
|
')',
|
||||||
']',
|
'[',
|
||||||
'<',
|
']',
|
||||||
'>',
|
'<',
|
||||||
'!',
|
'>',
|
||||||
',',
|
'!',
|
||||||
':',
|
',',
|
||||||
'&',
|
':',
|
||||||
'|',
|
'&',
|
||||||
'+',
|
'|',
|
||||||
'-',
|
'+',
|
||||||
'*',
|
'-',
|
||||||
'/'} // TODO: modulo?
|
'*',
|
||||||
|
'/'} // TODO: modulo?
|
||||||
|
|
||||||
var keywords = []string{
|
keywords = []string{
|
||||||
"var",
|
"var",
|
||||||
"if",
|
"if",
|
||||||
"while",
|
"while",
|
||||||
"switch",
|
"switch",
|
||||||
"for",
|
"for",
|
||||||
"foreach",
|
"foreach",
|
||||||
"func",
|
"func",
|
||||||
"true",
|
"true",
|
||||||
"false",
|
"false",
|
||||||
"case",
|
"case",
|
||||||
"default",
|
"default",
|
||||||
"return",
|
"return",
|
||||||
"try",
|
"try",
|
||||||
"catch",
|
"catch",
|
||||||
"exitwith",
|
"exitwith",
|
||||||
"waituntil",
|
"waituntil",
|
||||||
"code"}
|
"code"}
|
||||||
|
|
||||||
var whitespace = []byte{' ', '\n', '\t', '\r'}
|
whitespace = []byte{' ', '\n', '\t', '\r'}
|
||||||
var identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
||||||
var preprocessor = byte('#')
|
preprocessor = byte('#')
|
||||||
var new_line = []byte{'\r', '\n'}
|
new_line = []byte{'\r', '\n'}
|
||||||
|
)
|
||||||
|
|
||||||
// Tokenizes the given byte array into syntax tokens,
|
// Tokenizes the given byte array into syntax tokens,
|
||||||
// which can be parsed later.
|
// which can be parsed later.
|
||||||
func Tokenize(code []byte) []Token {
|
func Tokenize(code []byte, doStripSlashes bool) []Token {
|
||||||
|
if doStripSlashes {
|
||||||
|
code = stripSlashes(code)
|
||||||
|
}
|
||||||
|
|
||||||
code = removeComments(code)
|
code = removeComments(code)
|
||||||
tokens := make([]Token, 0)
|
tokens := make([]Token, 0)
|
||||||
token, mask, isstring, line, column := "", false, false, 0, 0
|
token, mask, isstring, line, column := "", false, false, 0, 0
|
||||||
@@ -68,8 +74,8 @@ func Tokenize(code []byte) []Token {
|
|||||||
column++
|
column++
|
||||||
|
|
||||||
if byteArrayContains(new_line, c) {
|
if byteArrayContains(new_line, c) {
|
||||||
line++
|
line++
|
||||||
column = 0
|
column = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// string masks (backslash)
|
// string masks (backslash)
|
||||||
@@ -91,8 +97,8 @@ func Tokenize(code []byte) []Token {
|
|||||||
} else {
|
} else {
|
||||||
// preprocessor, delimeter, keyword or variable/expression
|
// preprocessor, delimeter, keyword or variable/expression
|
||||||
if c == preprocessor {
|
if c == preprocessor {
|
||||||
tokens = append(tokens, preprocessorLine(code, &i, line, column))
|
tokens = append(tokens, preprocessorLine(code, &i, line, column))
|
||||||
token = ""
|
token = ""
|
||||||
} else if byteArrayContains(delimiter, c) {
|
} else if byteArrayContains(delimiter, c) {
|
||||||
if token != "" {
|
if token != "" {
|
||||||
tokens = append(tokens, Token{token, false, line, column})
|
tokens = append(tokens, Token{token, false, line, column})
|
||||||
@@ -114,6 +120,28 @@ func Tokenize(code []byte) []Token {
|
|||||||
return tokens
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removes slashes from input code.
|
||||||
|
// This is used for the "code" keyword for correct strings in resulting code.
|
||||||
|
func stripSlashes(code []byte) []byte {
|
||||||
|
newcode := make([]byte, len(code))
|
||||||
|
j, mask := 0, false
|
||||||
|
|
||||||
|
for i := 0; i < len(code); i++ {
|
||||||
|
c := code[i]
|
||||||
|
|
||||||
|
if c == '\\' && !mask {
|
||||||
|
mask = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newcode[j] = code[i]
|
||||||
|
mask = false
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
return newcode
|
||||||
|
}
|
||||||
|
|
||||||
// Removes all comments from input byte array.
|
// Removes all comments from input byte array.
|
||||||
// Comments are single line comments, starting with // (two slashes),
|
// Comments are single line comments, starting with // (two slashes),
|
||||||
// multi line comments with /* ... */ (slash star, star slash).
|
// multi line comments with /* ... */ (slash star, star slash).
|
||||||
@@ -154,31 +182,31 @@ func removeComments(code []byte) []byte {
|
|||||||
|
|
||||||
// Reads preprocessor command until end of line
|
// Reads preprocessor command until end of line
|
||||||
func preprocessorLine(code []byte, i *int, lineNr, column int) Token {
|
func preprocessorLine(code []byte, i *int, lineNr, column int) Token {
|
||||||
c := byte('0')
|
c := byte('0')
|
||||||
var line string
|
var line string
|
||||||
|
|
||||||
for *i < len(code) {
|
for *i < len(code) {
|
||||||
c = code[*i]
|
c = code[*i]
|
||||||
|
|
||||||
if byteArrayContains(new_line, c) {
|
if byteArrayContains(new_line, c) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
line += string(c)
|
line += string(c)
|
||||||
(*i)++
|
(*i)++
|
||||||
}
|
}
|
||||||
|
|
||||||
// read all new line characters (\r and \n)
|
// read all new line characters (\r and \n)
|
||||||
c = code[*i]
|
c = code[*i]
|
||||||
|
|
||||||
for byteArrayContains(new_line, c) {
|
for byteArrayContains(new_line, c) {
|
||||||
(*i)++
|
(*i)++
|
||||||
c = code[*i]
|
c = code[*i]
|
||||||
}
|
}
|
||||||
|
|
||||||
(*i)-- // for will count up 1, so subtract it here
|
(*i)-- // for will count up 1, so subtract it here
|
||||||
|
|
||||||
return Token{line, true, lineNr, column}
|
return Token{line, true, lineNr, column}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the next character in code starting at i.
|
// Returns the next character in code starting at i.
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package tokenizer_test
|
package tokenizer_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"tokenizer"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
"tokenizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTokenizerVar(t *testing.T) {
|
func TestTokenizerVar(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_var.asl")
|
got := getTokens(t, "../../test/tokenizer_var.asl")
|
||||||
want := []string{"var", "x", "=", "1", ";", "var", "array", "=", "[", "1", ",", "2", ",", "3", "]", ";"}
|
want := []string{"var", "x", "=", "1", ";", "var", "array", "=", "[", "1", ",", "2", ",", "3", "]", ";"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -15,7 +15,7 @@ func TestTokenizerVar(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerIf(t *testing.T) {
|
func TestTokenizerIf(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_if.asl")
|
got := getTokens(t, "../../test/tokenizer_if.asl")
|
||||||
want := []string{"if", "a", "<", "b", "{", "}"}
|
want := []string{"if", "a", "<", "b", "{", "}"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -23,7 +23,7 @@ func TestTokenizerIf(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerWhile(t *testing.T) {
|
func TestTokenizerWhile(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_while.asl")
|
got := getTokens(t, "../../test/tokenizer_while.asl")
|
||||||
want := []string{"while", "true", "{", "}"}
|
want := []string{"while", "true", "{", "}"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -31,7 +31,7 @@ func TestTokenizerWhile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerFor(t *testing.T) {
|
func TestTokenizerFor(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_for.asl")
|
got := getTokens(t, "../../test/tokenizer_for.asl")
|
||||||
want := []string{"for", "var", "i", "=", "0", ";", "i", "<", "100", ";", "i", "=", "i", "+", "1", "{", "}"}
|
want := []string{"for", "var", "i", "=", "0", ";", "i", "<", "100", ";", "i", "=", "i", "+", "1", "{", "}"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -39,7 +39,7 @@ func TestTokenizerFor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerForach(t *testing.T) {
|
func TestTokenizerForach(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_foreach.asl")
|
got := getTokens(t, "../../test/tokenizer_foreach.asl")
|
||||||
want := []string{"foreach", "unit", "=", ">", "allUnits", "{", "}"}
|
want := []string{"foreach", "unit", "=", ">", "allUnits", "{", "}"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -47,7 +47,7 @@ func TestTokenizerForach(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerSwitch(t *testing.T) {
|
func TestTokenizerSwitch(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_switch.asl")
|
got := getTokens(t, "../../test/tokenizer_switch.asl")
|
||||||
want := []string{"switch", "x", "{", "case", "1", ":", "x", "=", "1", ";", "case", "2", ":", "x", "=", "2", ";", "default", ":", "x", "=", "3", ";", "}"}
|
want := []string{"switch", "x", "{", "case", "1", ":", "x", "=", "1", ";", "case", "2", ":", "x", "=", "2", ";", "default", ":", "x", "=", "3", ";", "}"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -55,7 +55,7 @@ func TestTokenizerSwitch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerFunction(t *testing.T) {
|
func TestTokenizerFunction(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_func.asl")
|
got := getTokens(t, "../../test/tokenizer_func.asl")
|
||||||
want := []string{"func", "TestFunction", "(", "param0", ",", "param1", ")", "{", "return", "true", ";", "}"}
|
want := []string{"func", "TestFunction", "(", "param0", ",", "param1", ")", "{", "return", "true", ";", "}"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -63,7 +63,7 @@ func TestTokenizerFunction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerExpression(t *testing.T) {
|
func TestTokenizerExpression(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_expr.asl")
|
got := getTokens(t, "../../test/tokenizer_expr.asl")
|
||||||
want := []string{"x", "=", "(", "(", "1", "+", "2", "+", "3", ")", "*", "4", "/", "2", ")", "+", "foo", "(", "1", ",", "2", ",", "3", ")", ";"}
|
want := []string{"x", "=", "(", "(", "1", "+", "2", "+", "3", ")", "*", "4", "/", "2", ")", "+", "foo", "(", "1", ",", "2", ",", "3", ")", ";"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -71,7 +71,7 @@ func TestTokenizerExpression(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerIdentifier(t *testing.T) {
|
func TestTokenizerIdentifier(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_identifier.asl")
|
got := getTokens(t, "../../test/tokenizer_identifier.asl")
|
||||||
want := []string{"var", "format", "=", "\"should not be for mat!\"", ";"}
|
want := []string{"var", "format", "=", "\"should not be for mat!\"", ";"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -79,7 +79,7 @@ func TestTokenizerIdentifier(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerInlineCode(t *testing.T) {
|
func TestTokenizerInlineCode(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_code.asl")
|
got := getTokens(t, "../../test/tokenizer_code.asl")
|
||||||
want := []string{"var", "x", "=", "code", "(", "\"var x = 5;\"", ")", ";"}
|
want := []string{"var", "x", "=", "code", "(", "\"var x = 5;\"", ")", ";"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
@@ -87,8 +87,17 @@ func TestTokenizerInlineCode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenizerPreprocessor(t *testing.T) {
|
func TestTokenizerPreprocessor(t *testing.T) {
|
||||||
got := getTokens(t, "test/tokenizer_preprocessor.asl")
|
got := getTokens(t, "../../test/tokenizer_preprocessor.asl")
|
||||||
want := []string{"#define HELLO_WORLD \"Hello World!\"", "hint", "(", ")", "(", "HELLO_WORLD", ")", ";"}
|
want := []string{"#define HELLO_WORLD \"Hello World!\"", "hint", "(", "HELLO_WORLD", ")", ";"}
|
||||||
|
|
||||||
|
compareLength(t, &got, &want)
|
||||||
|
compareTokens(t, &got, &want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenizerMask(t *testing.T) {
|
||||||
|
got := getTokens(t, "../../test/tokenizer_mask.asl")
|
||||||
|
want := []string{"var", "x", "=", "\"Hello \\\"World\\\"\"", ";",
|
||||||
|
"var", "y", "=", "code", "(", "\"var z = \\\"Hello \\\\\"World\\\\\"\\\";\"", ")", ";"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
compareTokens(t, &got, &want)
|
compareTokens(t, &got, &want)
|
||||||
@@ -130,5 +139,5 @@ func getTokens(t *testing.T, file string) []tokenizer.Token {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenizer.Tokenize(code)
|
return tokenizer.Tokenize(code, false)
|
||||||
}
|
}
|
||||||
|
|||||||
146
src/types/loader.go
Normal file
146
src/types/loader.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// type for object types
|
||||||
|
TYPE = 1
|
||||||
|
NAN = "NaN"
|
||||||
|
ARRAY = "ARRAY"
|
||||||
|
|
||||||
|
// types for functions
|
||||||
|
NULL = 2
|
||||||
|
UNARY = 3
|
||||||
|
BINARY = 4
|
||||||
|
|
||||||
|
win_new_line = "\r\n"
|
||||||
|
unix_new_line = "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FunctionType struct {
|
||||||
|
Name string
|
||||||
|
Type int // one of the constants NULL, UNARY, BINARY
|
||||||
|
ArgsLeft int
|
||||||
|
ArgsRight int // number of args on left side for binary functions
|
||||||
|
}
|
||||||
|
|
||||||
|
var functions []FunctionType
|
||||||
|
|
||||||
|
// Returns function type information by name.
|
||||||
|
// If not found, the parameter will be nil.
|
||||||
|
func GetFunction(name string) *FunctionType {
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
|
||||||
|
for _, function := range functions {
|
||||||
|
if function.Name == name {
|
||||||
|
return &function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads type information from file.
|
||||||
|
// The format is specified by 'supportInfo' command: https://community.bistudio.com/wiki/supportInfo
|
||||||
|
func LoadTypes(path string) error {
|
||||||
|
content, err := ioutil.ReadFile(path)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := strings.Replace(win_new_line, unix_new_line, string(content), -1) // make this work on windows and unix
|
||||||
|
functions = make([]FunctionType, 0)
|
||||||
|
parseTypes(data)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTypes(content string) {
|
||||||
|
lines := strings.Split(content, unix_new_line)
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
if len(line) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if line[0] == 'n' {
|
||||||
|
parseNullFunction(line)
|
||||||
|
} else if line[0] == 'u' {
|
||||||
|
parseUnaryFunction(line)
|
||||||
|
} else if line[0] == 'b' {
|
||||||
|
parseBinaryFunction(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNullFunction(line string) {
|
||||||
|
parts := getParts(line)
|
||||||
|
functions = append(functions, FunctionType{parts[0], NULL, 0, 0})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUnaryFunction(line string) {
|
||||||
|
parts := getParts(line)
|
||||||
|
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
args := getArgs(parts[1])
|
||||||
|
|
||||||
|
var argsCount int
|
||||||
|
|
||||||
|
if args[0] != ARRAY {
|
||||||
|
argsCount = len(args) - getNaNArgs(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
functions = append(functions, FunctionType{parts[0], UNARY, argsCount, 0})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBinaryFunction(line string) {
|
||||||
|
parts := getParts(line)
|
||||||
|
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
argsLeft := getArgs(parts[0])
|
||||||
|
argsRight := getArgs(parts[2])
|
||||||
|
|
||||||
|
var argsLeftCount int
|
||||||
|
var argsRightCount int
|
||||||
|
|
||||||
|
if argsLeft[0] != ARRAY {
|
||||||
|
argsLeftCount = len(argsLeft) - getNaNArgs(argsLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
if argsRight[0] != ARRAY {
|
||||||
|
argsRightCount = len(argsRight) - getNaNArgs(argsRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
functions = append(functions, FunctionType{parts[1], BINARY, argsLeftCount, argsRightCount})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getParts(line string) []string {
|
||||||
|
line = line[2:]
|
||||||
|
return strings.Split(line, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArgs(part string) []string {
|
||||||
|
return strings.Split(part, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNaNArgs(args []string) int {
|
||||||
|
nan := 0
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
if arg == NAN {
|
||||||
|
nan++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nan
|
||||||
|
}
|
||||||
18
src/types/loader_test.go
Normal file
18
src/types/loader_test.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTypesGetFunction(t *testing.T) {
|
||||||
|
if err := types.LoadTypes("../../test/types"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
function := types.GetFunction("hint")
|
||||||
|
|
||||||
|
if function == nil {
|
||||||
|
t.Error("Function 'hint' not found in type list")
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/parser_binary_buildin_func.asl
Normal file
1
test/parser_binary_buildin_func.asl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
setHit(someCar)("motor", 1);
|
||||||
@@ -1 +0,0 @@
|
|||||||
var _x = setHit(getVar(player, foo)(bar))("head", "tail");
|
|
||||||
1
test/parser_expression_array.asl
Normal file
1
test/parser_expression_array.asl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var x = [1, 2, 3]-[2, 3];
|
||||||
1
test/parser_null_buildin_func.asl
Normal file
1
test/parser_null_buildin_func.asl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var _volume = radioVolume();
|
||||||
1
test/parser_unary_buildin_func.asl
Normal file
1
test/parser_unary_buildin_func.asl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var _isReady = unitReady(soldier);
|
||||||
2
test/tokenizer_mask.asl
Normal file
2
test/tokenizer_mask.asl
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
var x = "Hello \"World\"";
|
||||||
|
var y = code("var z = \"Hello \\"World\\"\";");
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
#define HELLO_WORLD "Hello World!"
|
#define HELLO_WORLD "Hello World!"
|
||||||
hint()(HELLO_WORLD);
|
hint(HELLO_WORLD);
|
||||||
|
|||||||
2198
test/types
Normal file
2198
test/types
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,12 +3,13 @@ A visual tool set to ease the work of asl developers.
|
|||||||
Maintained by yours truly: [654wak654](https://github.com/654wak654/)
|
Maintained by yours truly: [654wak654](https://github.com/654wak654/)
|
||||||
|
|
||||||
**Notepad++ Syntax Higligthing**: https://github.com/DeKugelschieber/asl/blob/master/tools/asl.xml
|
**Notepad++ Syntax Higligthing**: https://github.com/DeKugelschieber/asl/blob/master/tools/asl.xml
|
||||||
|
|
||||||
Feel free to contribute with another text editor's syntax higligthing plugin.
|
Feel free to contribute with another text editor's syntax higligthing plugin.
|
||||||
|
|
||||||
##ASL GUI <img src="https://img.shields.io/badge/version-1.1.0.0-orange.svg" alt="version">
|
##ASL GUI <img src="https://img.shields.io/badge/version-1.1.0.0-orange.svg" alt="version">
|
||||||
An optional Java interface to make the compile procces of ASL faster and more user-friendly. It's released under the MIT licence just like the core project. It also helps with error reporting of asl.
|
An optional Java interface to make the compile procces of ASL faster and more user-friendly. It's released under the MIT licence just like the core project. It also helps with error reporting of asl.
|
||||||
|
|
||||||
**Version 1.1.00**
|
**Version 1.1.0.0**
|
||||||
- New Arma 3-themed look, and error reporting straight from asl.
|
- New Arma 3-themed look, and error reporting straight from asl.
|
||||||
- Program now depends on asl.exe, they need to be in the same directory.
|
- Program now depends on asl.exe, they need to be in the same directory.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user