diff --git a/README.md b/README.md index afb1053..3b44735 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # ASL -**ASL is under heavy development and not production ready, the features listed here are not fully implemented. Please visit again when the final version is releaesed. If you like to contribute or if you are just interested, go on.** - ASL stands for Arma Scripting Language, a C-style scripting language compiled to SQF. ASL is intended to simplify Arma 3 mod and mission development and eliminate the pain of SQF's bad syntax. @@ -16,6 +14,29 @@ Main reasons for ASL: The compiler is written in Go and implemented as a simple recursive decent parser and uses concurrency to compile multiple files at once, which makes it really fast. +## Usage + +ASL is a command line tool. After you have downloaded it, execute it using the terminal/"cmd" by navigating to the binary or setting PATH variable and executing the following statement: + +``` +asl [-v|-r|-pretty|--help] +``` + +| Parameter | Optional/Required | Meaning | +| --------- | ----------------- | ------- | +| -v | optional | Shows ASL version. | +| -r | optional | Read input directory recursively. | +| -pretty | optional | Enable pretty printing to SQF. | +| --help | optional | Show usage. | +| | required | Directory to read ASL files from. | +| | required | Directory for SQF output. Can be the same as input directory. | + +**Example:** + +``` +asl ./missions/myMission/myScripts ./missions/myMission/compiledScripts +``` + ## Syntax ### Comments diff --git a/ToDo.md b/ToDo.md index f877a60..4eb2482 100644 --- a/ToDo.md +++ b/ToDo.md @@ -5,7 +5,7 @@ * ~~solution for build in commands which do not require left values~~ * ~~pretty/minified printing~~ * ~~usage~~ -* recursive compiling +* ~~recursive compiling~~ * concurrent compiling * ~~inline buildin function call -> foo(a)(bar(x)(y));~~ * ~~negative values e.g. -1, operator !~~ diff --git a/in/complex.asl b/in/complex.asl index 2e28424..82373cf 100644 --- a/in/complex.asl +++ b/in/complex.asl @@ -22,13 +22,13 @@ if (!isNil _getunit) then { // unit exists? */ if !isServer && player != player { - exit()(); // does not work for SQF, need to implement exitWith... + waituntil(player == player); } var _getunit = select(_this)(0); var _file = select(_this)(1); -if !(isNil()(_getunit)) { +if !isNil()(_getunit) { var _unit = objNull; call()(compile()(format()("_unit = %1", _getunit))); diff --git a/in/sub/sub.asl b/in/sub/sub.asl new file mode 100644 index 0000000..54faa44 --- /dev/null +++ b/in/sub/sub.asl @@ -0,0 +1,8 @@ +var x = 1; +var y = 3; + +func foo(x, y) { + return x+y +} + +hint()(format()("%1", foo(x, y))); diff --git a/in/sub/test.txt b/in/sub/test.txt new file mode 100644 index 0000000..e69de29 diff --git a/out/complex.sqf b/out/complex.sqf new file mode 100644 index 0000000..f4620fd --- /dev/null +++ b/out/complex.sqf @@ -0,0 +1,15 @@ +if (!isServer&&player!=player) then { +waitUntil {player==player}; +}; +_getunit = (_this select 0); +_file = (_this select 1); +if (!(isNil _getunit)) then { +_unit = objNull; +call (compile (format ["_unit = %1", _getunit])); +if ((local _unit)) then { +try { +_unit execVM _file; +} catch { +}; +}; +}; diff --git a/out/simple.sqf b/out/simple.sqf new file mode 100644 index 0000000..ca0c81d --- /dev/null +++ b/out/simple.sqf @@ -0,0 +1,7 @@ +waitUntil {x=x+1;x<100}; +if (timeIsOver) then { +if (true) exitWith { +[] call foo; +[] call bar; +}; +}; diff --git a/out/sub/sub.sqf b/out/sub/sub.sqf new file mode 100644 index 0000000..b770bba --- /dev/null +++ b/out/sub/sub.sqf @@ -0,0 +1,8 @@ +x = 1; +y = 3; +foo = { +x = _this select 0; +y = _this select 1; +return x+y; +}; +hint (format ["%1", ([x, y] call foo)]); diff --git a/src/main/asl.go b/src/main/asl.go index 332f11c..241a7cc 100644 --- a/src/main/asl.go +++ b/src/main/asl.go @@ -4,24 +4,140 @@ import ( "asl" "fmt" "io/ioutil" + "path/filepath" + "os" + "strings" ) -const version = "0.1" +const version = "1.0.0" +const extension = ".asl" +const sqfextension = ".sqf" + +type ASLFile struct { + in string + out string + newname string +} + +var recursive bool = false +var pretty bool = false +var exit bool = false +var aslFiles []ASLFile +var inDir string func usage() { - fmt.Println("Usage: asl [-v|-r|-pretty] []\n") + fmt.Println("Usage: asl [-v|-r|-pretty|--help] \n") fmt.Println("-v (optional) shows asl version") fmt.Println("-r (optional) recursivly compile all asl files in folder") fmt.Println("-pretty (optional) activates pretty printing\n") - fmt.Println(" file or directory to compile") - fmt.Println(" (optional) output file/folder, if not set, files will be created alongside their asl files") + fmt.Println("--help (optional) shows usage\n") + fmt.Println(" directory to compile") + fmt.Println(" output directory, directory structure will be created corresponding to input directory") +} + +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 +} + +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(path+"/"+name) + continue + } + + if !dir[i].IsDir() && strings.ToLower(filepath.Ext(name)) == extension { + in := path+"/"+dir[i].Name() + out := "./"+path[len(inDir):len(path)] + newname := name[:len(name)-len(filepath.Ext(name))] + + file := ASLFile{in, out, newname} + aslFiles = append(aslFiles, file) + } + } +} + +func compile(path string) { + for i := 0; i < len(aslFiles); i++ { + out := path+"/"+aslFiles[i].out+"/"+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 := asl.Tokenize(code) + sqf := asl.Parse(token, pretty) + + os.MkdirAll(path+"/"+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() { - // read test file - code, _ := ioutil.ReadFile("in/simple.asl") - token := asl.Tokenize(code) - out := asl.Parse(token, true) - - fmt.Print("OUTPUT:\n-------\n" + out) + args := os.Args + + // flags + if len(args) < 2 { + usage() + return + } + + var i int + for i = 1; i < len(args) && flags(args[i]); i++ {} + + if exit { + return + } + + // in/out parameter + out := "" + + if i < len(args) { + inDir = args[i] + i++ + } else { + return + } + + if i < len(args) { + out = args[i] + } + + readAslFiles(inDir) + compile(out) }