mirror of
https://github.com/Kugelschieber/asl.git
synced 2026-01-18 03:50:25 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fe69dc3d4 | ||
|
|
611307d68a | ||
|
|
98a4873427 | ||
|
|
9096adfeed | ||
|
|
944b91c16b | ||
|
|
28fbbd7e10 | ||
|
|
3311fa79c7 | ||
|
|
bb026d45c7 | ||
|
|
80cbcd8f9a | ||
|
|
346859761e | ||
|
|
b13dc22807 | ||
|
|
da3d097d5d | ||
|
|
6dd9323d78 | ||
|
|
e598cae71f | ||
|
|
97d220eb47 | ||
|
|
9786c63d52 | ||
|
|
51cc0f5700 | ||
|
|
3b40b3427d | ||
|
|
3c1ccbba76 | ||
|
|
3ee4000ef5 | ||
|
|
005a3bf101 | ||
|
|
a156f2b76d | ||
|
|
737b96d5ac | ||
|
|
e3a7c5d6ac | ||
|
|
e1bf92f4aa | ||
|
|
fbfbb8a55c | ||
|
|
9d909a0b8a | ||
|
|
3f6b854a09 | ||
|
|
05e6937201 | ||
|
|
ed1d19851e | ||
|
|
4dfa2ee3ef | ||
|
|
29804f0d20 | ||
|
|
59831fed32 | ||
|
|
69e684a230 | ||
|
|
90e8941a2c | ||
|
|
7ca3cfb1ad | ||
|
|
65a7c4aae3 | ||
|
|
acced0474b | ||
|
|
d07611c3a5 | ||
|
|
afbc290281 | ||
|
|
2e94cf8efb | ||
|
|
41b8b74639 | ||
|
|
df79809cdd | ||
|
|
1c67be7d2e | ||
|
|
8477b443b0 | ||
|
|
511ceb128f | ||
|
|
07ce5e7a0b | ||
|
|
ca26fbf6d4 | ||
|
|
0c63114c44 | ||
|
|
1eb5ce8035 | ||
|
|
68f05f09ea | ||
|
|
aa83a9a093 | ||
|
|
5bcfda10bd | ||
|
|
664b435715 | ||
|
|
11c9d19c25 |
17
.project
17
.project
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>asl</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.googlecode.goclipse.goBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.googlecode.goclipse.core.goNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
**1.2.2**
|
||||
|
||||
* bugfix: deadlock on compiling multile files at once
|
||||
|
||||
**1.2.1**
|
||||
|
||||
* bugfix: new line after while for pretty printing
|
||||
* bugfix: build in unary function with multiple arguments
|
||||
|
||||
**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
|
||||
|
||||
117
README.md
117
README.md
@@ -15,27 +15,40 @@ Main reasons for ASL:
|
||||
|
||||
## Usage
|
||||
|
||||
ASL is a command line tool. After you have downloaded it, navigation to the binary and execute it:
|
||||
ASL is a command line tool. After you have downloaded it, navigate to the binary and execute it:
|
||||
|
||||
```
|
||||
asl [-v|-r|-pretty|--help] <input directory> <output directory>
|
||||
asl.exe [-v|-r|-pretty|--help] <input directory> <output directory>
|
||||
```
|
||||
|
||||
| Parameter | Optional/Required | Meaning |
|
||||
| --------- | ----------------- | ------- |
|
||||
| -v | optional | Shows ASL version. |
|
||||
| Parameter | Optional/Required | Description |
|
||||
| --------- | ----------------- | ----------- |
|
||||
| -v | optional | Show ASL version. |
|
||||
| -r | optional | Read input directory recursively. |
|
||||
| -pretty | optional | Enable pretty printing to SQF. |
|
||||
| --help | optional | Show usage. |
|
||||
| input directory | required | Directory to read ASL files from (use ./ for relative paths). |
|
||||
| output directory | required | Directory for SQF output. Can be the same as input directory (use ./ for relative paths). |
|
||||
| input directory | required | Input directory for ASL files (use ./ for relative paths). |
|
||||
| output directory | required | Output directory for SQF files. Can be the same as input directory (use ./ for relative paths). |
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
asl ./missions/myMission/myScripts ./missions/myMission/compiledScripts
|
||||
asl.exe ./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. The content looks like:
|
||||
|
||||
```
|
||||
...
|
||||
t:DIARY_RECORD
|
||||
t:LOCATION
|
||||
b:ARRAY waypointattachobject SCALAR,OBJECT
|
||||
b:OBJECT,GROUP enableattack BOOL
|
||||
...
|
||||
```
|
||||
|
||||
The types file will be delivered with the current release, but not updated when Arma is.
|
||||
|
||||
## Syntax
|
||||
|
||||
### Comments
|
||||
@@ -67,15 +80,15 @@ var array = [1, 2, 3];
|
||||
var one = array[0];
|
||||
|
||||
// accessing using a statement:
|
||||
var zwo = array[33/3-2];
|
||||
var two = array[33/3-2];
|
||||
|
||||
// it is possble to use arrays in expressions:
|
||||
var emptyArray = one-[0];
|
||||
var emptyArray = one-[1];
|
||||
```
|
||||
|
||||
### Control structures
|
||||
|
||||
Controll structure syntax is C-like. Notice they are all using the same brackets and do not require to set a semicolon at the end, unlike in SQF.
|
||||
Controll structure syntax is C-like. Notice the same brackets for all structures and no semicolon at the end, unlike in SQF:
|
||||
|
||||
```
|
||||
if 1 < 2 {
|
||||
@@ -88,13 +101,16 @@ while 1 < 2 {
|
||||
// ...
|
||||
}
|
||||
|
||||
for var _i = 0; _i < 100; _i = _i+1 { // var before identifier is optional
|
||||
for var _i = 0; _i < 100; _i = _i+1 {
|
||||
// ...
|
||||
}
|
||||
|
||||
foreach unit => allUnits { // foreach, iterates over all units in this case
|
||||
// element is available as "unit" here
|
||||
// _x is still available due to how SQF works!
|
||||
for _i = 0; _i < 100; _i = _i+1 { // same as above, var is optional before identifier "_i"
|
||||
// ...
|
||||
}
|
||||
|
||||
foreach _unit => allUnits { // iterates over all units in this case
|
||||
// element is available as "_unit" AND "_x" here ("_x" is used by SQF's foreach)
|
||||
}
|
||||
|
||||
switch x { // there is no "break" in SQF
|
||||
@@ -106,10 +122,10 @@ switch x { // there is no "break" in SQF
|
||||
// ...
|
||||
}
|
||||
|
||||
try {
|
||||
// ...
|
||||
try { // handles errors in "catch" block
|
||||
// errors thrown here...
|
||||
} catch {
|
||||
// ...
|
||||
// ... will be handled here
|
||||
}
|
||||
```
|
||||
|
||||
@@ -123,9 +139,7 @@ func add(_a, _b) {
|
||||
}
|
||||
|
||||
// Call it:
|
||||
|
||||
var _x = add(1, 2);
|
||||
// result in _x is 3
|
||||
var _x = add(1, 2); // result in _x is 3
|
||||
```
|
||||
|
||||
Functions support predefined parameters:
|
||||
@@ -136,14 +150,14 @@ func add(_a = 0, _b = 0) {
|
||||
}
|
||||
|
||||
// Call it:
|
||||
|
||||
var _x = add();
|
||||
// result in _x is 0
|
||||
var _x = add(); // result in _x is 0
|
||||
```
|
||||
|
||||
When trying to define a function with a name that exists in SQF's build in function set, you'll get an compile error. So declaring `func hint()...` won't compile.
|
||||
|
||||
### 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, ...) use the same syntax when using functions. An exception are "binary" functions. These are functions which accept parameters on both sides of the function name. Here is an example for "addItem":
|
||||
|
||||
```
|
||||
addItem(someUnit)("NVGoogles");
|
||||
@@ -152,7 +166,7 @@ addItem(someUnit)("NVGoogles");
|
||||
someUnit addItem "NVGoogles";
|
||||
```
|
||||
|
||||
Where the first brackets contain the parameters used in front of SQF command and the second ones behind SQF command. If more than one parameter is passed, it will be converted to an array. This syntax can be used for **all** build in functions (also spawn and so on).
|
||||
Where the first brackets contain the parameters used in front of SQF command and the second ones the parameters behind the SQF command. If more than one parameter is passed, it will be converted to an array. This syntax can be used for **all** build in functions.
|
||||
|
||||
```
|
||||
foo(x, y, z)(1, 2, 3);
|
||||
@@ -161,11 +175,18 @@ foo(x, y, z)(1, 2, 3);
|
||||
[x, y, z] foo [1, 2, 3];
|
||||
```
|
||||
|
||||
If the build in function accepts no parameters (null function) or on one side only (unary function), it can be called with a single pair of brackets:
|
||||
|
||||
```
|
||||
hint("your text");
|
||||
shownWatch();
|
||||
```
|
||||
|
||||
### 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 shows how they are written in ASL and what the output will look like. Remember that ASL is case sensitive!
|
||||
|
||||
**exitWith**
|
||||
**exitwith**
|
||||
|
||||
```
|
||||
exitwith { // NOT exitWith!
|
||||
@@ -178,7 +199,7 @@ if (true) exitWith {
|
||||
};
|
||||
```
|
||||
|
||||
**waitUntil**
|
||||
**waituntil**
|
||||
|
||||
```
|
||||
waituntil(condition); // NOT waitUntil!
|
||||
@@ -193,10 +214,9 @@ waitUntil {expression;condition};
|
||||
|
||||
**code**
|
||||
|
||||
The code function is used to compile inline code. This does **not** replace SQF compile buildin function, but will return the contained ASL code as SQF.
|
||||
The code function is used to compile inline code. This does **not** replace SQF's compile build in function, but will return the contained ASL code as SQF:
|
||||
|
||||
```
|
||||
// input:
|
||||
var x = code("var y = 5;"); // pass as string
|
||||
|
||||
// output:
|
||||
@@ -206,14 +226,14 @@ x = {y = 5;};
|
||||
## Preprocessor
|
||||
|
||||
The preprocessor works like the original one, with some limitations.
|
||||
Please visit the link at the bottom, to read about the preprocessor and how to use it. Generally, preprocessor lines must start with the hash key (#) and must stay in their own line. They are always printed as seperate lines. These features are not supported:
|
||||
Please visit the link at the bottom, to read about the preprocessor and how to use it. Generally, preprocessor lines must start with the hash key (#) and must stay in their own line. They are always printed as seperate lines in SQF. These features are not supported:
|
||||
|
||||
* replacing parts of words
|
||||
* multi line preprocessor commands
|
||||
* __EXEC (not used in SQF anyway)
|
||||
* EXEC (not used in SQF anyway)
|
||||
|
||||
If you use *__EXEC*, it will be replaced by a function call to it ([] call __EXEC).
|
||||
*__LINE__* and *__FILE__* can be used, since they are identifiers:
|
||||
If you use *EXEC*, it will be replaced by a function call to it ([] call __EXEC).
|
||||
*LINE* and *FILE* can be used like normal identifiers:
|
||||
|
||||
```
|
||||
if __LINE__ == 22 {
|
||||
@@ -227,14 +247,14 @@ if __FILE__ == "myScript.sqf" {
|
||||
|
||||
## List of all keywords
|
||||
|
||||
Keywords should not be used as identifiers. Here is a full list of all keywords in ASL. Remember that build in function names should not be used neither.
|
||||
Keywords must not be used as identifiers. Here is a full list of all keywords in ASL. Remember that build in function names must not be used neither, else you'll get an compile error.
|
||||
|
||||
| Keyword |
|
||||
| ------- |
|
||||
| var |
|
||||
| if |
|
||||
| while |
|
||||
| witch |
|
||||
| switch |
|
||||
| for |
|
||||
| foreach |
|
||||
| func |
|
||||
@@ -251,15 +271,19 @@ Keywords should not be used as identifiers. Here is a full list of all keywords
|
||||
|
||||
## 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 might be in a future version:
|
||||
|
||||
* scopes
|
||||
* else if
|
||||
* selector in expression
|
||||
**scopes**
|
||||
|
||||
scopes won't be supported, since they are a stupid concept and can be replaced by functions.
|
||||
Scopes won't be supported, since it's a stupid concept and can be replaced by functions.
|
||||
|
||||
Selectors in expressions do not work (yet):
|
||||
**else if**
|
||||
|
||||
Planned.
|
||||
|
||||
**Selector in expression**
|
||||
|
||||
Selectors in expressions do not work yet, so this is not possible:
|
||||
|
||||
```
|
||||
var x = ([1, 2, 3]-[1, 2])[0]; // should result in 3
|
||||
@@ -267,7 +291,7 @@ var x = ([1, 2, 3]-[1, 2])[0]; // should result in 3
|
||||
|
||||
## Contribute
|
||||
|
||||
To contribute please create pull requests or explain your ideas in the issue section on GitHub. Report any bugs or incompatible ASL <-> SQF syntax you can find.
|
||||
To contribute, please create pull requests or explain your ideas in the issue section on GitHub. Report any bugs or incompatible ASL <-> SQF syntax you can find with a short example.
|
||||
|
||||
## Further information
|
||||
|
||||
@@ -277,6 +301,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 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
|
||||
|
||||
MIT
|
||||
|
||||
22
ToDo.md
22
ToDo.md
@@ -1,22 +0,0 @@
|
||||
# ToDo
|
||||
|
||||
* ~~assign to returned values~~
|
||||
* ~~sqf: ... sqf whitespace~~
|
||||
* ~~solution for build in commands which do not require left values~~
|
||||
* ~~pretty/minified printing~~
|
||||
* ~~usage~~
|
||||
* ~~recursive compiling~~
|
||||
* concurrent compiling
|
||||
* ~~inline buildin function call -> foo(a)(bar(x)(y));~~
|
||||
* ~~negative values e.g. -1, operator !~~
|
||||
* ~~tokenizer splits commands like "format" -> for, mat~~
|
||||
* ~~try ... catch~~
|
||||
* better error reporting
|
||||
- recover panic
|
||||
- line, column
|
||||
|
||||
## Special cases
|
||||
|
||||
* ~~exitWith...~~
|
||||
* ~~waitUntil...~~
|
||||
* ~~!buildInFunc()()...~~
|
||||
6
build
Executable file
6
build
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
export GOROOT=/usr/local/go
|
||||
export PATH=$PATH:$GOROOT/bin
|
||||
export GOPATH=/home/marvin/Projekte/asl
|
||||
go build -ldflags "-s -w" src/main/asl.go
|
||||
6
run
Executable file
6
run
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
export GOROOT=/usr/local/go
|
||||
export PATH=$PATH:$GOROOT/bin
|
||||
export GOPATH=/home/marvin/Projekte/asl
|
||||
go run src/main/asl.go $1 $2
|
||||
6
run_tests
Executable file
6
run_tests
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
export GOROOT=/usr/local/go
|
||||
export PATH=$PATH:$GOROOT/bin
|
||||
export GOPATH=/home/marvin/Projekte/asl
|
||||
go test parser tokenizer types
|
||||
228
src/main/asl.go
228
src/main/asl.go
@@ -1,34 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"parser"
|
||||
"tokenizer"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"os"
|
||||
"parser"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"tokenizer"
|
||||
"types"
|
||||
)
|
||||
|
||||
const (
|
||||
version = "1.1.1"
|
||||
extension = ".asl"
|
||||
sqfextension = ".sqf"
|
||||
PathSeparator = string(os.PathSeparator)
|
||||
version = "1.2.2"
|
||||
extension = ".asl"
|
||||
sqfextension = ".sqf"
|
||||
typeinfo = "types"
|
||||
PathSeparator = string(os.PathSeparator)
|
||||
)
|
||||
|
||||
type ASLFile struct {
|
||||
in string
|
||||
out string
|
||||
newname string
|
||||
in string
|
||||
out string
|
||||
newname string
|
||||
}
|
||||
|
||||
var (
|
||||
recursive bool = false
|
||||
pretty bool = false
|
||||
exit bool = false
|
||||
aslFiles []ASLFile
|
||||
inDir string
|
||||
recursive bool = false
|
||||
pretty bool = false
|
||||
exit bool = false
|
||||
aslFiles []ASLFile
|
||||
inDir string
|
||||
)
|
||||
|
||||
func usage() {
|
||||
@@ -41,110 +43,146 @@ func usage() {
|
||||
fmt.Println("<output directory> output directory, directory structure will be created corresponding to input directory")
|
||||
}
|
||||
|
||||
// Parses compiler flags.
|
||||
func flags(flag string) bool {
|
||||
flag = strings.ToLower(flag)
|
||||
|
||||
if flag[0] == '-' {
|
||||
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 true
|
||||
}
|
||||
|
||||
return false
|
||||
flag = strings.ToLower(flag)
|
||||
|
||||
if flag[0] != '-' {
|
||||
return false
|
||||
}
|
||||
|
||||
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 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) {
|
||||
dir, err := ioutil.ReadDir(path)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error reading in directory!")
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(dir); i++ {
|
||||
name := dir[i].Name()
|
||||
|
||||
if dir[i].IsDir() && recursive {
|
||||
readAslFiles(filepath.FromSlash(path+PathSeparator+name))
|
||||
continue
|
||||
}
|
||||
dir, err := ioutil.ReadDir(path)
|
||||
|
||||
if !dir[i].IsDir() && strings.ToLower(filepath.Ext(name)) == extension {
|
||||
in := filepath.FromSlash(path+PathSeparator+dir[i].Name())
|
||||
out := filepath.FromSlash("./"+path[len(inDir):len(path)])
|
||||
newname := name[:len(name)-len(filepath.Ext(name))]
|
||||
|
||||
file := ASLFile{in, out, newname}
|
||||
aslFiles = append(aslFiles, file)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("Error reading in directory!")
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(dir); i++ {
|
||||
name := dir[i].Name()
|
||||
|
||||
if dir[i].IsDir() && recursive {
|
||||
readAslFiles(filepath.FromSlash(path + PathSeparator + name))
|
||||
continue
|
||||
}
|
||||
|
||||
if !dir[i].IsDir() && strings.ToLower(filepath.Ext(name)) == extension {
|
||||
in := filepath.FromSlash(path + PathSeparator + dir[i].Name())
|
||||
out := filepath.FromSlash("./" + path[len(inDir):len(path)])
|
||||
newname := name[:len(name)-len(filepath.Ext(name))]
|
||||
|
||||
file := ASLFile{in, out, newname}
|
||||
aslFiles = append(aslFiles, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recovers and prints thrown error.
|
||||
func recoverCompileError(file string) {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("Compile error in file "+file+":", r)
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a single ASL file.
|
||||
func compileFile(path string, file ASLFile) {
|
||||
defer recoverCompileError(file.in)
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles ASL files.
|
||||
func compile(path string) {
|
||||
for i := 0; i < len(aslFiles); i++ {
|
||||
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 {
|
||||
fmt.Println("Error reading file: "+aslFiles[i].in)
|
||||
continue
|
||||
}
|
||||
|
||||
token := tokenizer.Tokenize(code, false)
|
||||
compiler := parser.Compiler{}
|
||||
sqf := compiler.Parse(token, pretty)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(aslFiles); i++ {
|
||||
compileFile(path, aslFiles[i])
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
args := os.Args
|
||||
|
||||
|
||||
// flags
|
||||
if len(args) < 2 {
|
||||
usage()
|
||||
return
|
||||
usage()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var i int
|
||||
for i = 1; i < len(args) && flags(args[i]); i++ {}
|
||||
|
||||
if exit {
|
||||
return
|
||||
for i = 1; i < len(args) && flags(args[i]); i++ {
|
||||
}
|
||||
|
||||
|
||||
if exit {
|
||||
return
|
||||
}
|
||||
|
||||
// load type information
|
||||
loadTypes()
|
||||
|
||||
if exit {
|
||||
return
|
||||
}
|
||||
|
||||
// in/out parameter
|
||||
out := ""
|
||||
|
||||
|
||||
if i < len(args) {
|
||||
inDir = args[i]
|
||||
i++
|
||||
inDir = args[i]
|
||||
i++
|
||||
} else {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if i < len(args) {
|
||||
out = args[i]
|
||||
out = args[i]
|
||||
}
|
||||
|
||||
|
||||
readAslFiles(inDir)
|
||||
compile(out)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"tokenizer"
|
||||
"types"
|
||||
)
|
||||
|
||||
const new_line = "\r\n"
|
||||
@@ -10,7 +12,7 @@ const new_line = "\r\n"
|
||||
// and writes SQF code into desired location.
|
||||
func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
|
||||
if !c.initParser(token, prettyPrinting) {
|
||||
return ""
|
||||
return ""
|
||||
}
|
||||
|
||||
for c.tokenIndex < len(token) {
|
||||
@@ -21,9 +23,9 @@ func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
|
||||
}
|
||||
|
||||
func (c *Compiler) parseBlock() {
|
||||
if c.get().Preprocessor {
|
||||
c.parsePreprocessor()
|
||||
} else if c.accept("var") {
|
||||
if c.get().Preprocessor {
|
||||
c.parsePreprocessor()
|
||||
} else if c.accept("var") {
|
||||
c.parseVar()
|
||||
} else if c.accept("if") {
|
||||
c.parseIf()
|
||||
@@ -42,9 +44,9 @@ func (c *Compiler) parseBlock() {
|
||||
} else if c.accept("try") {
|
||||
c.parseTryCatch()
|
||||
} else if c.accept("exitwith") {
|
||||
c.parseExitWith()
|
||||
c.parseExitWith()
|
||||
} else if c.accept("waituntil") {
|
||||
c.parseWaitUntil()
|
||||
c.parseWaitUntil()
|
||||
} else if c.accept("case") || c.accept("default") {
|
||||
return
|
||||
} else {
|
||||
@@ -57,9 +59,9 @@ func (c *Compiler) parseBlock() {
|
||||
}
|
||||
|
||||
func (c *Compiler) parsePreprocessor() {
|
||||
// we definitely want a new line before and after
|
||||
c.appendOut(new_line+c.get().Token+new_line, false)
|
||||
c.next()
|
||||
// we definitely want a new line before and after
|
||||
c.appendOut(new_line+c.get().Token+new_line, false)
|
||||
c.next()
|
||||
}
|
||||
|
||||
func (c *Compiler) parseVar() {
|
||||
@@ -78,7 +80,7 @@ func (c *Compiler) parseVar() {
|
||||
}
|
||||
|
||||
func (c *Compiler) parseArray(out bool) string {
|
||||
output := ""
|
||||
output := ""
|
||||
c.expect("[")
|
||||
output += "["
|
||||
|
||||
@@ -87,17 +89,17 @@ func (c *Compiler) parseArray(out bool) string {
|
||||
|
||||
for c.accept(",") {
|
||||
c.next()
|
||||
output += ","+c.parseExpression(false)
|
||||
output += "," + c.parseExpression(false)
|
||||
}
|
||||
}
|
||||
|
||||
c.expect("]")
|
||||
output += "]"
|
||||
|
||||
|
||||
if out {
|
||||
c.appendOut(output, false)
|
||||
c.appendOut(output, false)
|
||||
}
|
||||
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -129,7 +131,7 @@ func (c *Compiler) parseWhile() {
|
||||
c.expect("{")
|
||||
c.parseBlock()
|
||||
c.expect("}")
|
||||
c.appendOut("};", false)
|
||||
c.appendOut("};", true)
|
||||
}
|
||||
|
||||
func (c *Compiler) parseSwitch() {
|
||||
@@ -215,6 +217,12 @@ func (c *Compiler) parseForeach() {
|
||||
|
||||
func (c *Compiler) parseFunction() {
|
||||
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.next()
|
||||
c.expect("(")
|
||||
@@ -231,20 +239,20 @@ func (c *Compiler) parseFunctionParameter() {
|
||||
if c.accept("{") {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
c.appendOut("params [", false)
|
||||
|
||||
for !c.accept(")") {
|
||||
name := c.get().Token
|
||||
c.next()
|
||||
|
||||
|
||||
if c.accept("=") {
|
||||
c.next()
|
||||
value := c.get().Token
|
||||
c.next()
|
||||
c.appendOut("[\""+name+"\","+value+"]", false)
|
||||
c.next()
|
||||
value := c.get().Token
|
||||
c.next()
|
||||
c.appendOut("[\""+name+"\","+value+"]", false)
|
||||
} else {
|
||||
c.appendOut("\""+name+"\"", false)
|
||||
c.appendOut("\""+name+"\"", false)
|
||||
}
|
||||
|
||||
if !c.accept(")") {
|
||||
@@ -252,7 +260,7 @@ func (c *Compiler) parseFunctionParameter() {
|
||||
c.appendOut(",", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
c.appendOut("];", true)
|
||||
}
|
||||
|
||||
@@ -279,47 +287,47 @@ func (c *Compiler) parseTryCatch() {
|
||||
}
|
||||
|
||||
func (c *Compiler) parseExitWith() {
|
||||
c.expect("exitwith")
|
||||
c.expect("{")
|
||||
c.appendOut("if (true) exitWith {", true)
|
||||
c.parseBlock()
|
||||
c.expect("}")
|
||||
c.appendOut("};", true)
|
||||
c.expect("exitwith")
|
||||
c.expect("{")
|
||||
c.appendOut("if (true) exitWith {", true)
|
||||
c.parseBlock()
|
||||
c.expect("}")
|
||||
c.appendOut("};", true)
|
||||
}
|
||||
|
||||
func (c *Compiler) parseWaitUntil() {
|
||||
c.expect("waituntil")
|
||||
c.expect("(")
|
||||
c.appendOut("waitUntil {", false)
|
||||
c.parseExpression(true)
|
||||
|
||||
if c.accept(";") {
|
||||
c.next()
|
||||
c.appendOut(";", false)
|
||||
c.parseExpression(true)
|
||||
}
|
||||
|
||||
c.expect(")")
|
||||
c.expect(";")
|
||||
c.appendOut("};", true)
|
||||
c.expect("waituntil")
|
||||
c.expect("(")
|
||||
c.appendOut("waitUntil {", false)
|
||||
c.parseExpression(true)
|
||||
|
||||
if c.accept(";") {
|
||||
c.next()
|
||||
c.appendOut(";", false)
|
||||
c.parseExpression(true)
|
||||
}
|
||||
|
||||
c.expect(")")
|
||||
c.expect(";")
|
||||
c.appendOut("};", true)
|
||||
}
|
||||
|
||||
func (c *Compiler) parseInlineCode() string {
|
||||
c.expect("code")
|
||||
c.expect("(")
|
||||
|
||||
code := c.get().Token
|
||||
c.next()
|
||||
output := "{}"
|
||||
|
||||
if len(code) > 2 {
|
||||
compiler := Compiler{}
|
||||
output = "{"+compiler.Parse(tokenizer.Tokenize([]byte(code[1:len(code)-1]), true), false)+"}"
|
||||
}
|
||||
|
||||
c.expect(")")
|
||||
|
||||
return output
|
||||
c.expect("code")
|
||||
c.expect("(")
|
||||
|
||||
code := c.get().Token
|
||||
c.next()
|
||||
output := "{}"
|
||||
|
||||
if len(code) > 2 {
|
||||
compiler := Compiler{}
|
||||
output = "{" + compiler.Parse(tokenizer.Tokenize([]byte(code[1:len(code)-1]), true), false) + "}"
|
||||
}
|
||||
|
||||
c.expect(")")
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// Everything that does not start with a keyword.
|
||||
@@ -359,30 +367,22 @@ func (c *Compiler) parseFunctionCall(out bool, name string) string {
|
||||
output := ""
|
||||
|
||||
c.expect("(")
|
||||
leftParams, leftParamCount := c.parseParameter(false)
|
||||
paramsStr, paramCount := c.parseParameter(false)
|
||||
c.expect(")")
|
||||
|
||||
if c.accept("(") {
|
||||
// buildin function
|
||||
c.next()
|
||||
rightParams, rightParamCount := c.parseParameter(false)
|
||||
c.expect(")")
|
||||
|
||||
if leftParamCount > 1 {
|
||||
leftParams = "[" + leftParams + "]"
|
||||
}
|
||||
|
||||
if rightParamCount > 1 {
|
||||
rightParams = "[" + rightParams + "]"
|
||||
}
|
||||
|
||||
if leftParamCount > 0 {
|
||||
output = leftParams + " " + name + " " + rightParams
|
||||
// buildin function
|
||||
buildin := types.GetFunction(name)
|
||||
|
||||
if buildin != nil {
|
||||
if buildin.Type == types.NULL {
|
||||
output = name
|
||||
} else if buildin.Type == types.UNARY {
|
||||
output = c.parseUnaryFunction(name, paramsStr, paramCount)
|
||||
} else {
|
||||
output = name + " " + rightParams
|
||||
output = c.parseBinaryFunction(name, paramsStr, buildin, paramCount)
|
||||
}
|
||||
} else {
|
||||
output = "[" + leftParams + "] call " + name
|
||||
output = "[" + paramsStr + "] call " + name
|
||||
}
|
||||
|
||||
if out {
|
||||
@@ -392,12 +392,49 @@ func (c *Compiler) parseFunctionCall(out bool, name string) string {
|
||||
return output
|
||||
}
|
||||
|
||||
func (c *Compiler) parseUnaryFunction(name, paramsStr string, paramCount int) string {
|
||||
output := ""
|
||||
|
||||
if paramCount == 1 {
|
||||
output = name + " " + paramsStr
|
||||
} else {
|
||||
output = name + " [" + paramsStr + "]"
|
||||
}
|
||||
|
||||
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) {
|
||||
output := ""
|
||||
count := 0
|
||||
|
||||
for !c.accept(")") {
|
||||
output += c.parseExpression(out)
|
||||
expr := c.parseExpression(out)
|
||||
output += expr
|
||||
count++
|
||||
|
||||
if !c.accept(")") {
|
||||
@@ -459,19 +496,19 @@ func (c *Compiler) parseIdentifier() string {
|
||||
output := ""
|
||||
|
||||
if c.accept("code") {
|
||||
output += c.parseInlineCode()
|
||||
output += c.parseInlineCode()
|
||||
} else if c.seek("(") && !c.accept("!") && !c.accept("-") {
|
||||
name := c.get().Token
|
||||
c.next()
|
||||
output = "(" + c.parseFunctionCall(false, name) + ")"
|
||||
} else if c.accept("[") {
|
||||
output += c.parseArray(false)
|
||||
output += c.parseArray(false)
|
||||
} else if c.seek("[") {
|
||||
output += "("+c.get().Token
|
||||
c.next()
|
||||
c.expect("[")
|
||||
output += " select ("+c.parseExpression(false)+"))"
|
||||
c.expect("]")
|
||||
output += "(" + c.get().Token
|
||||
c.next()
|
||||
c.expect("[")
|
||||
output += " select (" + c.parseExpression(false) + "))"
|
||||
c.expect("]")
|
||||
} else if c.accept("!") || c.accept("-") {
|
||||
output = c.get().Token
|
||||
c.next()
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"tokenizer"
|
||||
"errors"
|
||||
"strconv"
|
||||
"tokenizer"
|
||||
)
|
||||
|
||||
type Compiler struct {
|
||||
tokens []tokenizer.Token
|
||||
tokenIndex int
|
||||
out string
|
||||
offset int
|
||||
pretty bool
|
||||
tokens []tokenizer.Token
|
||||
tokenIndex int
|
||||
out string
|
||||
offset int
|
||||
pretty bool
|
||||
}
|
||||
|
||||
// Initilizes the parser.
|
||||
@@ -24,7 +25,7 @@ func (c *Compiler) initParser(token []tokenizer.Token, prettyPrinting bool) bool
|
||||
c.out = ""
|
||||
c.offset = 0
|
||||
c.pretty = prettyPrinting
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -38,7 +39,7 @@ func (c *Compiler) accept(token string) bool {
|
||||
// Throws if current token does not match expected one.
|
||||
func (c *Compiler) expect(token string) {
|
||||
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()
|
||||
@@ -62,7 +63,7 @@ func (c *Compiler) next() {
|
||||
// Returns current token or throws, if no more tokens are available.
|
||||
func (c *Compiler) get() tokenizer.Token {
|
||||
if c.tokenIndex >= len(c.tokens) {
|
||||
panic("No more tokens")
|
||||
panic(errors.New("No more tokens"))
|
||||
}
|
||||
|
||||
return c.tokens[c.tokenIndex]
|
||||
@@ -1,173 +1,208 @@
|
||||
package parser_test
|
||||
|
||||
import (
|
||||
"tokenizer"
|
||||
"parser"
|
||||
"io/ioutil"
|
||||
"parser"
|
||||
"testing"
|
||||
"tokenizer"
|
||||
"types"
|
||||
)
|
||||
|
||||
const (
|
||||
types_file = "../../test/types"
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
func TestParserAssignment(t *testing.T) {
|
||||
got := getCompiled(t, "test/parser_assignment.asl")
|
||||
got := getCompiled(t, "../../test/parser_assignment.asl")
|
||||
want := "x = 1;\r\n"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
func TestParserWhile(t *testing.T) {
|
||||
got := getCompiled(t, "test/tokenizer_while.asl")
|
||||
want := "while {true} do {\r\n};"
|
||||
got := getCompiled(t, "../../test/tokenizer_while.asl")
|
||||
want := "while {true} do {\r\n};\r\n"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
func TestParserBuildinFunctionCall(t *testing.T) {
|
||||
got := getCompiled(t, "test/parser_buildin_func.asl")
|
||||
want := "_x = (([player, foo] getVar bar) setHit [\"head\", \"tail\"]);\r\n"
|
||||
func TestParserNullBuildinFunctionCall(t *testing.T) {
|
||||
types.LoadTypes(types_file)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
func TestParserExpressionArray(t *testing.T) {
|
||||
got := getCompiled(t, "test/parser_expression_array.asl")
|
||||
got := getCompiled(t, "../../test/parser_expression_array.asl")
|
||||
want := "x = [1,2,3]-[2,3];\r\n"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
// bugfix: unary function parsing (e.g. "format")
|
||||
func TestBugfixParserUnaryFunction(t *testing.T) {
|
||||
got := getCompiled(t, "../../test/bugfix_unary_func_format.asl")
|
||||
want := "format [\"%1 %2\", \"value1\", \"value2\"];\r\n[\"a\", \"b\", \"c\"] call someFunc;\r\n"
|
||||
|
||||
equal(t, got, want)
|
||||
}
|
||||
|
||||
func getCompiled(t *testing.T, file string) string {
|
||||
code, err := ioutil.ReadFile(file)
|
||||
|
||||
@@ -176,7 +211,7 @@ func getCompiled(t *testing.T, file string) string {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
tokens := tokenizer.Tokenize(code)
|
||||
tokens := tokenizer.Tokenize(code, false)
|
||||
compiler := parser.Compiler{}
|
||||
|
||||
return compiler.Parse(tokens, true)
|
||||
|
||||
@@ -5,64 +5,66 @@ import (
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
Token string
|
||||
Token string
|
||||
Preprocessor bool
|
||||
Line int
|
||||
Column int
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
|
||||
var delimiter = []byte{
|
||||
'=',
|
||||
';',
|
||||
'{',
|
||||
'}',
|
||||
'(',
|
||||
')',
|
||||
'[',
|
||||
']',
|
||||
'<',
|
||||
'>',
|
||||
'!',
|
||||
',',
|
||||
':',
|
||||
'&',
|
||||
'|',
|
||||
'+',
|
||||
'-',
|
||||
'*',
|
||||
'/'} // TODO: modulo?
|
||||
var (
|
||||
delimiter = []byte{
|
||||
'=',
|
||||
';',
|
||||
'{',
|
||||
'}',
|
||||
'(',
|
||||
')',
|
||||
'[',
|
||||
']',
|
||||
'<',
|
||||
'>',
|
||||
'!',
|
||||
',',
|
||||
':',
|
||||
'&',
|
||||
'|',
|
||||
'+',
|
||||
'-',
|
||||
'*',
|
||||
'/'} // TODO: modulo?
|
||||
|
||||
var keywords = []string{
|
||||
"var",
|
||||
"if",
|
||||
"while",
|
||||
"switch",
|
||||
"for",
|
||||
"foreach",
|
||||
"func",
|
||||
"true",
|
||||
"false",
|
||||
"case",
|
||||
"default",
|
||||
"return",
|
||||
"try",
|
||||
"catch",
|
||||
"exitwith",
|
||||
"waituntil",
|
||||
"code"}
|
||||
keywords = []string{
|
||||
"var",
|
||||
"if",
|
||||
"while",
|
||||
"switch",
|
||||
"for",
|
||||
"foreach",
|
||||
"func",
|
||||
"true",
|
||||
"false",
|
||||
"case",
|
||||
"default",
|
||||
"return",
|
||||
"try",
|
||||
"catch",
|
||||
"exitwith",
|
||||
"waituntil",
|
||||
"code"}
|
||||
|
||||
var whitespace = []byte{' ', '\n', '\t', '\r'}
|
||||
var identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
||||
var preprocessor = byte('#')
|
||||
var new_line = []byte{'\r', '\n'}
|
||||
whitespace = []byte{' ', '\n', '\t', '\r'}
|
||||
identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
||||
preprocessor = byte('#')
|
||||
new_line = []byte{'\r', '\n'}
|
||||
)
|
||||
|
||||
// Tokenizes the given byte array into syntax tokens,
|
||||
// which can be parsed later.
|
||||
func Tokenize(code []byte, doStripSlashes bool) []Token {
|
||||
if doStripSlashes {
|
||||
code = stripSlashes(code);
|
||||
}
|
||||
|
||||
if doStripSlashes {
|
||||
code = stripSlashes(code)
|
||||
}
|
||||
|
||||
code = removeComments(code)
|
||||
tokens := make([]Token, 0)
|
||||
token, mask, isstring, line, column := "", false, false, 0, 0
|
||||
@@ -70,10 +72,10 @@ func Tokenize(code []byte, doStripSlashes bool) []Token {
|
||||
for i := 0; i < len(code); i++ {
|
||||
c := code[i]
|
||||
column++
|
||||
|
||||
|
||||
if byteArrayContains(new_line, c) {
|
||||
line++
|
||||
column = 0
|
||||
line++
|
||||
column = 0
|
||||
}
|
||||
|
||||
// string masks (backslash)
|
||||
@@ -95,8 +97,8 @@ func Tokenize(code []byte, doStripSlashes bool) []Token {
|
||||
} else {
|
||||
// preprocessor, delimeter, keyword or variable/expression
|
||||
if c == preprocessor {
|
||||
tokens = append(tokens, preprocessorLine(code, &i, line, column))
|
||||
token = ""
|
||||
tokens = append(tokens, preprocessorLine(code, &i, line, column))
|
||||
token = ""
|
||||
} else if byteArrayContains(delimiter, c) {
|
||||
if token != "" {
|
||||
tokens = append(tokens, Token{token, false, line, column})
|
||||
@@ -121,23 +123,23 @@ func Tokenize(code []byte, doStripSlashes bool) []Token {
|
||||
// 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 {
|
||||
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
|
||||
|
||||
newcode[j] = code[i]
|
||||
mask = false
|
||||
j++
|
||||
}
|
||||
|
||||
return newcode
|
||||
}
|
||||
|
||||
// Removes all comments from input byte array.
|
||||
@@ -180,31 +182,31 @@ func removeComments(code []byte) []byte {
|
||||
|
||||
// Reads preprocessor command until end of line
|
||||
func preprocessorLine(code []byte, i *int, lineNr, column int) Token {
|
||||
c := byte('0')
|
||||
var line string
|
||||
|
||||
for *i < len(code) {
|
||||
c = code[*i]
|
||||
|
||||
if byteArrayContains(new_line, c) {
|
||||
break
|
||||
}
|
||||
|
||||
line += string(c)
|
||||
(*i)++
|
||||
}
|
||||
|
||||
// read all new line characters (\r and \n)
|
||||
c = code[*i]
|
||||
|
||||
for byteArrayContains(new_line, c) {
|
||||
(*i)++
|
||||
c = code[*i]
|
||||
}
|
||||
|
||||
(*i)-- // for will count up 1, so subtract it here
|
||||
|
||||
return Token{line, true, lineNr, column}
|
||||
c := byte('0')
|
||||
var line string
|
||||
|
||||
for *i < len(code) {
|
||||
c = code[*i]
|
||||
|
||||
if byteArrayContains(new_line, c) {
|
||||
break
|
||||
}
|
||||
|
||||
line += string(c)
|
||||
(*i)++
|
||||
}
|
||||
|
||||
// read all new line characters (\r and \n)
|
||||
c = code[*i]
|
||||
|
||||
for byteArrayContains(new_line, c) {
|
||||
(*i)++
|
||||
c = code[*i]
|
||||
}
|
||||
|
||||
(*i)-- // for will count up 1, so subtract it here
|
||||
|
||||
return Token{line, true, lineNr, column}
|
||||
}
|
||||
|
||||
// Returns the next character in code starting at i.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package tokenizer_test
|
||||
|
||||
import (
|
||||
"tokenizer"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"tokenizer"
|
||||
)
|
||||
|
||||
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", "]", ";"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -15,7 +15,7 @@ func TestTokenizerVar(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", "{", "}"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -23,7 +23,7 @@ func TestTokenizerIf(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", "{", "}"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -31,7 +31,7 @@ func TestTokenizerWhile(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", "{", "}"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -39,7 +39,7 @@ func TestTokenizerFor(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", "{", "}"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -47,7 +47,7 @@ func TestTokenizerForach(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", ";", "}"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -55,7 +55,7 @@ func TestTokenizerSwitch(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", ";", "}"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -63,7 +63,7 @@ func TestTokenizerFunction(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", ")", ";"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -71,7 +71,7 @@ func TestTokenizerExpression(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!\"", ";"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -79,7 +79,7 @@ func TestTokenizerIdentifier(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;\"", ")", ";"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
@@ -87,18 +87,17 @@ func TestTokenizerInlineCode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTokenizerPreprocessor(t *testing.T) {
|
||||
got := getTokens(t, "test/tokenizer_preprocessor.asl")
|
||||
want := []string{"#define HELLO_WORLD \"Hello World!\"", "hint", "(", ")", "(", "HELLO_WORLD", ")", ";"}
|
||||
got := getTokens(t, "../../test/tokenizer_preprocessor.asl")
|
||||
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")
|
||||
//var y = code("var z = \"Hello \\"World\\"\";");
|
||||
got := getTokens(t, "../../test/tokenizer_mask.asl")
|
||||
want := []string{"var", "x", "=", "\"Hello \\\"World\\\"\"", ";",
|
||||
"var", "y", "=", "code", "(", "\"var z = \\\"Hello \\\\\"World\\\\\"\\\";\"", ")", ";"}
|
||||
"var", "y", "=", "code", "(", "\"var z = \\\"Hello \\\\\"World\\\\\"\\\";\"", ")", ";"}
|
||||
|
||||
compareLength(t, &got, &want)
|
||||
compareTokens(t, &got, &want)
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
2
test/bugfix_unary_func_format.asl
Normal file
2
test/bugfix_unary_func_format.asl
Normal file
@@ -0,0 +1,2 @@
|
||||
format("%1 %2", "value1", "value2"); // must result in format [...];
|
||||
someFunc("a", "b", "c"); // must result in ... call someFunc;
|
||||
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_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);
|
||||
@@ -1,2 +1,2 @@
|
||||
#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
BIN
tools/ASL GUI.exe
Normal file
BIN
tools/ASL GUI.exe
Normal file
Binary file not shown.
20
tools/README.md
Normal file
20
tools/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
#ASL Tools
|
||||
A visual tool set to ease the work of asl developers.
|
||||
Maintained by yours truly: [654wak654](https://github.com/654wak654/)
|
||||
|
||||
**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.
|
||||
|
||||
##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.
|
||||
|
||||
**Version 1.1.0.0**
|
||||
- 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.
|
||||
|
||||
**Version 1.0.0.0**
|
||||
- More style changes and bug fixes, marked ready for release.
|
||||
|
||||
**Version 0.3.0.0:**
|
||||
- Fixed some possible bugs, did some style fixes and other code adjustments. It's now is readable without getting cataracts. Mostly anyway...
|
||||
33
tools/asl.xml
Normal file
33
tools/asl.xml
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user