mirror of
https://github.com/Kugelschieber/asl.git
synced 2026-01-18 12:00:25 +00:00
Compare commits
66 Commits
1.0.0
...
supportinf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
737b96d5ac | ||
|
|
e3a7c5d6ac | ||
|
|
e1bf92f4aa | ||
|
|
fbfbb8a55c | ||
|
|
9d909a0b8a | ||
|
|
3f6b854a09 | ||
|
|
05e6937201 | ||
|
|
ed1d19851e | ||
|
|
4dfa2ee3ef | ||
|
|
29804f0d20 | ||
|
|
59831fed32 | ||
|
|
69e684a230 | ||
|
|
d07611c3a5 | ||
|
|
afbc290281 | ||
|
|
2e94cf8efb | ||
|
|
41b8b74639 | ||
|
|
df79809cdd | ||
|
|
1c67be7d2e | ||
|
|
8477b443b0 | ||
|
|
511ceb128f | ||
|
|
07ce5e7a0b | ||
|
|
ca26fbf6d4 | ||
|
|
0c63114c44 | ||
|
|
1eb5ce8035 | ||
|
|
68f05f09ea | ||
|
|
aa83a9a093 | ||
|
|
5bcfda10bd | ||
|
|
664b435715 | ||
|
|
f1f564d89b | ||
|
|
fa766cc78c | ||
|
|
59f02f7782 | ||
|
|
1208bda68c | ||
|
|
11c9d19c25 | ||
|
|
3990a20cb0 | ||
|
|
07250ba2a7 | ||
|
|
2bc11fb125 | ||
|
|
0ca6566454 | ||
|
|
5c540ce481 | ||
|
|
f6b5ed4dd6 | ||
|
|
afad182f28 | ||
|
|
d2c32b3b85 | ||
|
|
67e950b41d | ||
|
|
6c55846e9f | ||
|
|
3ee9a310e8 | ||
|
|
8d5c0a6905 | ||
|
|
276ab86668 | ||
|
|
dc4eaf4b0f | ||
|
|
1d7f435530 | ||
|
|
aedf8e715e | ||
|
|
4da540317f | ||
|
|
6e4cef91bc | ||
|
|
ae8fad1abc | ||
|
|
755d5af263 | ||
|
|
bf751e63c9 | ||
|
|
c3d4661b7f | ||
|
|
d7013b01c3 | ||
|
|
393ddd4326 | ||
|
|
53ea74a19b | ||
|
|
0a66f15704 | ||
|
|
352bfd6a9b | ||
|
|
0b26c5bdf8 | ||
|
|
1cbcda4261 | ||
|
|
bba215cd8d | ||
|
|
a64bef135d | ||
|
|
7ed05f913b | ||
|
|
a703189f0c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/bin/
|
/bin/
|
||||||
/pkg/
|
/pkg/
|
||||||
/out/
|
/out/
|
||||||
|
/in/
|
||||||
|
|||||||
27
CHANGELOG.md
Normal file
27
CHANGELOG.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# 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
|
||||||
|
* function parameter count check for build in functions
|
||||||
|
|
||||||
|
**1.1.1**
|
||||||
|
|
||||||
|
* arrays can now be declared within expressions
|
||||||
|
* code keyword bug fix
|
||||||
|
|
||||||
|
**1.1.0**
|
||||||
|
|
||||||
|
* changed syntax of foreach
|
||||||
|
* private function variables
|
||||||
|
* default values for function parameters
|
||||||
|
* added preprocessor
|
||||||
|
* code inlining using new keyword "code"
|
||||||
|
* some code and repo cleanup
|
||||||
|
|
||||||
|
**1.0.0**
|
||||||
|
|
||||||
|
* first release
|
||||||
84
README.md
84
README.md
@@ -11,6 +11,7 @@ Main reasons for ASL:
|
|||||||
* easy to learn and understand
|
* easy to learn and understand
|
||||||
* full replacement of SQF
|
* full replacement of SQF
|
||||||
* compatible with Arma wiki and commands
|
* compatible with Arma wiki and commands
|
||||||
|
* comfortable
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -67,6 +68,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
|
||||||
@@ -76,6 +80,8 @@ Controll structure syntax is C-like. Notice they are all using the same brackets
|
|||||||
```
|
```
|
||||||
if 1 < 2 {
|
if 1 < 2 {
|
||||||
// ...
|
// ...
|
||||||
|
} else { // no else if yet
|
||||||
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
while 1 < 2 {
|
while 1 < 2 {
|
||||||
@@ -86,17 +92,16 @@ for var _i = 0; _i < 100; _i = _i+1 { // var before identifier is optional
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
each allUnits { // foreach, iterates over all units in this case
|
foreach unit => allUnits { // foreach, iterates over all units in this case
|
||||||
// element is available as _x here
|
// element is available as "unit" here
|
||||||
|
// _x is still available due to how SQF works!
|
||||||
}
|
}
|
||||||
|
|
||||||
switch x {
|
switch x { // there is no "break" in SQF
|
||||||
case 1:
|
case 1:
|
||||||
// ...
|
// ...
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
// ...
|
// ...
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
@@ -123,6 +128,19 @@ var _x = add(1, 2);
|
|||||||
// result in _x is 3
|
// result in _x is 3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Functions support predefined parameters:
|
||||||
|
|
||||||
|
```
|
||||||
|
func add(_a = 0, _b = 0) {
|
||||||
|
return _a+_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call it:
|
||||||
|
|
||||||
|
var _x = add();
|
||||||
|
// result in _x is 0
|
||||||
|
```
|
||||||
|
|
||||||
### 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.
|
||||||
@@ -134,7 +152,7 @@ addItem(someUnit)("NVGoogles");
|
|||||||
someUnit addItem "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.
|
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).
|
||||||
|
|
||||||
```
|
```
|
||||||
foo(x, y, z)(1, 2, 3);
|
foo(x, y, z)(1, 2, 3);
|
||||||
@@ -173,6 +191,40 @@ waitUntil {condition};
|
|||||||
waitUntil {expression;condition};
|
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.
|
||||||
|
|
||||||
|
```
|
||||||
|
// input:
|
||||||
|
var x = code("var y = 5;"); // pass as string
|
||||||
|
|
||||||
|
// output:
|
||||||
|
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:
|
||||||
|
|
||||||
|
* replacing parts of words
|
||||||
|
* multi line preprocessor commands
|
||||||
|
* __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 __LINE__ == 22 {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
if __FILE__ == "myScript.sqf" {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## List of all keywords
|
## 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 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.
|
||||||
@@ -182,7 +234,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 |
|
||||||
@@ -195,6 +247,23 @@ Keywords should not be used as identifiers. Here is a full list of all keywords
|
|||||||
| catch |
|
| catch |
|
||||||
| exitwith |
|
| exitwith |
|
||||||
| waituntil |
|
| waituntil |
|
||||||
|
| code |
|
||||||
|
|
||||||
|
## What's missing?
|
||||||
|
|
||||||
|
The following features are not implemented yet, but will be in 1.1.0 or a future version:
|
||||||
|
|
||||||
|
* scopes
|
||||||
|
* else if
|
||||||
|
* selector in expression
|
||||||
|
|
||||||
|
scopes won't be supported, since they are a stupid concept and can be replaced by functions.
|
||||||
|
|
||||||
|
Selectors in expressions do not work (yet):
|
||||||
|
|
||||||
|
```
|
||||||
|
var x = ([1, 2, 3]-[1, 2])[0]; // should result in 3
|
||||||
|
```
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
@@ -206,6 +275,7 @@ For further information you can read the SQF tutorial and documentation of scrip
|
|||||||
|
|
||||||
* [Arma Wiki](https://community.bistudio.com/wiki/Main_Page)
|
* [Arma Wiki](https://community.bistudio.com/wiki/Main_Page)
|
||||||
* [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)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
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
|
||||||
|
|
||||||
|
|||||||
BIN
gui/ASL GUI.exe
Normal file
BIN
gui/ASL GUI.exe
Normal file
Binary file not shown.
14
gui/README.md
Normal file
14
gui/README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
ASL GUI <img src="https://img.shields.io/badge/version-1.0.0.0-lightgrey.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.
|
||||||
|
|
||||||
|
Maintained by yours truly: [654wak654](https://github.com/654wak654/)
|
||||||
|
|
||||||
|
**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...
|
||||||
67
gui/src/asl/gui/DlgError.java
Normal file
67
gui/src/asl/gui/DlgError.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright 2015 Ozan Egitmen.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package asl.gui;
|
||||||
|
|
||||||
|
public class DlgError extends javax.swing.JDialog {
|
||||||
|
|
||||||
|
boolean isAbort = true;
|
||||||
|
|
||||||
|
public DlgError(java.awt.Frame parent, boolean modal, String errorMessage) {
|
||||||
|
super(parent, modal);
|
||||||
|
initComponents();
|
||||||
|
lblError.setText(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initComponents() {
|
||||||
|
|
||||||
|
lblError = new javax.swing.JLabel();
|
||||||
|
lblTitle = new javax.swing.JLabel();
|
||||||
|
|
||||||
|
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||||
|
setTitle("ERROR");
|
||||||
|
setIconImage(null);
|
||||||
|
setMinimumSize(new java.awt.Dimension(380, 150));
|
||||||
|
setResizable(false);
|
||||||
|
setType(java.awt.Window.Type.POPUP);
|
||||||
|
|
||||||
|
lblError.setFont(new java.awt.Font("Segoe UI Light", 0, 16));
|
||||||
|
lblError.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||||
|
lblError.setText("Some error");
|
||||||
|
|
||||||
|
lblTitle.setFont(new java.awt.Font("Segoe UI Light", 0, 16));
|
||||||
|
lblTitle.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||||
|
lblTitle.setText("asl.exe has encountered an error:");
|
||||||
|
lblTitle.setToolTipText("");
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||||
|
getContentPane().setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(lblTitle, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE).addComponent(lblError, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
|
||||||
|
layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addComponent(lblTitle).addGap(18, 18, 18).addComponent(lblError).addGap(27, 27, 27)));
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
private javax.swing.JLabel lblError;
|
||||||
|
private javax.swing.JLabel lblTitle;
|
||||||
|
}
|
||||||
370
gui/src/asl/gui/Main.java
Normal file
370
gui/src/asl/gui/Main.java
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright 2015 Ozan Egitmen.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package asl.gui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
|
import javax.swing.plaf.ColorUIResource;
|
||||||
|
|
||||||
|
public class Main extends javax.swing.JFrame {
|
||||||
|
|
||||||
|
Preferences prefs = Preferences.userRoot().node(this.getClass().getName());
|
||||||
|
boolean inputError = false, outputError = false, aslError = false;
|
||||||
|
|
||||||
|
public Main() {
|
||||||
|
initComponents();
|
||||||
|
getContentPane().setBackground(Color.WHITE);
|
||||||
|
lblASLError.setText(" ");
|
||||||
|
lblInputError.setText(" ");
|
||||||
|
lblOutputError.setText(" ");
|
||||||
|
txtASLDir.setText(prefs.get("aslDir", ""));
|
||||||
|
txtInputDir.setText(prefs.get("inputDir", ""));
|
||||||
|
txtOutputDir.setText(prefs.get("outputDir", ""));
|
||||||
|
cbCompileAll.setSelected(prefs.getBoolean("compileAll", false));
|
||||||
|
cbPrettyPrinting.setSelected(prefs.getBoolean("prettyPrinting", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fileChooser(String title, int fileType) {
|
||||||
|
JFileChooser chooser = new JFileChooser();
|
||||||
|
if (fileType == 0) {
|
||||||
|
chooser.setFileFilter(new FileNameExtensionFilter("Executable", "exe"));
|
||||||
|
chooser.setAcceptAllFileFilterUsed(false);
|
||||||
|
}
|
||||||
|
chooser.setFileSelectionMode(fileType);
|
||||||
|
chooser.setDialogTitle(title);
|
||||||
|
String selectedPath = "";
|
||||||
|
if (chooser.showOpenDialog(null) == 0)
|
||||||
|
selectedPath = chooser.getSelectedFile().toString();
|
||||||
|
else
|
||||||
|
chooser.cancelSelection();
|
||||||
|
return selectedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setErrorCondition(int i, boolean j) {
|
||||||
|
if (i == 0) {
|
||||||
|
lblASLError.setText(j ? "asl.exe isn't in this location! You can click this message to download it." : " ");
|
||||||
|
aslError = j;
|
||||||
|
} else if (i == 1) {
|
||||||
|
lblInputError.setText(j ? "This folder doesn't exist!" : " ");
|
||||||
|
inputError = j;
|
||||||
|
} else {
|
||||||
|
lblOutputError.setText(j ? "Output folder doesn't exsist! Click this message to create it." : " ");
|
||||||
|
outputError = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initComponents() {
|
||||||
|
|
||||||
|
lblInput = new javax.swing.JLabel();
|
||||||
|
txtInputDir = new javax.swing.JTextField();
|
||||||
|
lblOutput = new javax.swing.JLabel();
|
||||||
|
txtOutputDir = new javax.swing.JTextField();
|
||||||
|
btnInput = new javax.swing.JButton();
|
||||||
|
btnOutput = new javax.swing.JButton();
|
||||||
|
lblASL = new javax.swing.JLabel();
|
||||||
|
txtASLDir = new javax.swing.JTextField();
|
||||||
|
btnASL = new javax.swing.JButton();
|
||||||
|
jSeparator = new javax.swing.JSeparator();
|
||||||
|
lblASLSmall = new javax.swing.JLabel();
|
||||||
|
lblInputSmall = new javax.swing.JLabel();
|
||||||
|
lblOutputSmall = new javax.swing.JLabel();
|
||||||
|
cbCompileAll = new javax.swing.JCheckBox();
|
||||||
|
cbPrettyPrinting = new javax.swing.JCheckBox();
|
||||||
|
btnCompile = new javax.swing.JButton();
|
||||||
|
lblASLError = new javax.swing.JLabel();
|
||||||
|
lblInputError = new javax.swing.JLabel();
|
||||||
|
lblOutputError = new javax.swing.JLabel();
|
||||||
|
|
||||||
|
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
|
||||||
|
setTitle("ASL GUI");
|
||||||
|
setResizable(false);
|
||||||
|
|
||||||
|
lblInput.setFont(new java.awt.Font("Microsoft JhengHei UI Light", 0, 16));
|
||||||
|
lblInput.setText("Input Directory:");
|
||||||
|
lblInput.setOpaque(true);
|
||||||
|
|
||||||
|
txtInputDir.setFont(new java.awt.Font("Segoe UI Light", 0, 16));
|
||||||
|
|
||||||
|
lblOutput.setFont(new java.awt.Font("Microsoft JhengHei UI Light", 0, 16));
|
||||||
|
lblOutput.setText("Output Directory:");
|
||||||
|
lblOutput.setOpaque(true);
|
||||||
|
|
||||||
|
txtOutputDir.setFont(new java.awt.Font("Segoe UI Light", 0, 16));
|
||||||
|
|
||||||
|
btnInput.setText("...");
|
||||||
|
btnInput.setToolTipText("Opens a dialog to select input file");
|
||||||
|
btnInput.setFocusable(false);
|
||||||
|
btnInput.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
btnInputMouseClicked(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnOutput.setText("...");
|
||||||
|
btnOutput.setToolTipText("Opens a dialog to select output directory");
|
||||||
|
btnOutput.setFocusable(false);
|
||||||
|
btnOutput.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
btnOutputMouseClicked(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lblASL.setFont(new java.awt.Font("Microsoft JhengHei UI Light", 0, 16));
|
||||||
|
lblASL.setText("ASL Compiler Directory");
|
||||||
|
lblASL.setOpaque(true);
|
||||||
|
|
||||||
|
txtASLDir.setFont(new java.awt.Font("Segoe UI Light", 0, 16));
|
||||||
|
|
||||||
|
btnASL.setText("...");
|
||||||
|
btnASL.setToolTipText("Opens a dialog to select the compiler location");
|
||||||
|
btnASL.setFocusable(false);
|
||||||
|
btnASL.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
btnASLMouseClicked(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
jSeparator.setToolTipText("");
|
||||||
|
|
||||||
|
lblASLSmall.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 10));
|
||||||
|
lblASLSmall.setText("Location of the asl.exe file.");
|
||||||
|
lblASLSmall.setOpaque(true);
|
||||||
|
|
||||||
|
lblInputSmall.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 10));
|
||||||
|
lblInputSmall.setText("Directory of scripts that will be compiled in to the output directory.");
|
||||||
|
lblInputSmall.setOpaque(true);
|
||||||
|
|
||||||
|
lblOutputSmall.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 10));
|
||||||
|
lblOutputSmall.setText("Directory that the compiled .sqf script(s) will be saved in.");
|
||||||
|
lblOutputSmall.setOpaque(true);
|
||||||
|
|
||||||
|
cbCompileAll.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 11));
|
||||||
|
cbCompileAll.setText("Compile all scripts in subfolders too.");
|
||||||
|
cbCompileAll.setFocusable(false);
|
||||||
|
cbCompileAll.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||||
|
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||||
|
cbCompileAllStateChanged(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cbPrettyPrinting.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 11));
|
||||||
|
cbPrettyPrinting.setText("Activate pretty printing.");
|
||||||
|
cbPrettyPrinting.setFocusable(false);
|
||||||
|
cbPrettyPrinting.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||||
|
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||||
|
cbPrettyPrintingStateChanged(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnCompile.setFont(new java.awt.Font("Microsoft JhengHei UI Light", 0, 16));
|
||||||
|
btnCompile.setText("Compile");
|
||||||
|
btnCompile.setToolTipText("Opens a dialog to select output directory");
|
||||||
|
btnCompile.setFocusable(false);
|
||||||
|
btnCompile.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
btnCompileMouseClicked(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lblASLError.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 10));
|
||||||
|
lblASLError.setForeground(java.awt.Color.red);
|
||||||
|
lblASLError.setText("Some error");
|
||||||
|
lblASLError.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
lblASLErrorMouseClicked(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lblInputError.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 10));
|
||||||
|
lblInputError.setForeground(java.awt.Color.red);
|
||||||
|
lblInputError.setText("Some error");
|
||||||
|
|
||||||
|
lblOutputError.setFont(new java.awt.Font("Microsoft YaHei UI", 0, 10));
|
||||||
|
lblOutputError.setForeground(java.awt.Color.red);
|
||||||
|
lblOutputError.setText("Some error");
|
||||||
|
lblOutputError.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||||
|
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
lblOutputErrorMouseClicked(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||||
|
getContentPane().setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(jSeparator).addGroup(layout.createSequentialGroup().addGap(15, 15, 15).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(lblOutputError, javax.swing.GroupLayout.PREFERRED_SIZE, 371, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(lblInputError, javax.swing.GroupLayout.PREFERRED_SIZE, 371, javax.swing.GroupLayout.PREFERRED_SIZE).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false).addComponent(lblASLError, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 371, javax.swing.GroupLayout.PREFERRED_SIZE).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(lblASL).addComponent(lblASLSmall).addGroup(layout.createSequentialGroup().addComponent(txtASLDir, javax.swing.GroupLayout.PREFERRED_SIZE, 320, javax.swing.GroupLayout.PREFERRED_SIZE).addGap(6, 6, 6).addComponent(btnASL)).addGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING).addComponent(txtOutputDir, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 320, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(lblOutput, javax.swing.GroupLayout.Alignment.LEADING)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(btnOutput)).addComponent(lblOutputSmall).addComponent(lblInput).addGroup(layout.createSequentialGroup().addComponent(txtInputDir, javax.swing.GroupLayout.PREFERRED_SIZE, 320, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(btnInput)).addComponent(lblInputSmall).addComponent(cbCompileAll).addComponent(cbPrettyPrinting))).addComponent(btnCompile, javax.swing.GroupLayout.PREFERRED_SIZE, 373, javax.swing.GroupLayout.PREFERRED_SIZE)).addGap(20, 20, 20)));
|
||||||
|
layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addGap(6, 6, 6).addComponent(lblASL).addGap(3, 3, 3).addComponent(lblASLSmall).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(txtASLDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(btnASL, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)).addGap(4, 4, 4).addComponent(lblASLError).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE).addGap(3, 3, 3).addComponent(lblInput).addGap(3, 3, 3).addComponent(lblInputSmall, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(txtInputDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(btnInput, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)).addGap(4, 4, 4).addComponent(lblInputError).addGap(6, 6, 6).addComponent(lblOutput).addGap(3, 3, 3).addComponent(lblOutputSmall, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE).addGap(6, 6, 6).addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(txtOutputDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(btnOutput, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)).addGap(4, 4, 4).addComponent(lblOutputError).addGap(6, 6, 6).addComponent(cbCompileAll).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(cbPrettyPrinting).addGap(11, 11, 11).addComponent(btnCompile, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE).addGap(11, 11, 11)));
|
||||||
|
|
||||||
|
pack();
|
||||||
|
setLocationRelativeTo(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnInputMouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(evt)) {
|
||||||
|
String path = fileChooser("Select input directory", 1);
|
||||||
|
File inputDir = new File(path);
|
||||||
|
if (inputDir.exists()) {
|
||||||
|
prefs.put("inputDir", path);
|
||||||
|
txtInputDir.setText(path);
|
||||||
|
if (inputError)
|
||||||
|
setErrorCondition(1, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnOutputMouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(evt)) {
|
||||||
|
String path = fileChooser("Select output directory", 1);
|
||||||
|
File outputDir = new File(path);
|
||||||
|
if (outputDir.exists()) {
|
||||||
|
prefs.put("outputDir", path);
|
||||||
|
txtOutputDir.setText(path);
|
||||||
|
if (outputError)
|
||||||
|
setErrorCondition(2, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnASLMouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(evt)) {
|
||||||
|
String path = fileChooser("Select 'asl.exe' location", 0);
|
||||||
|
File asl = new File(path);
|
||||||
|
if (asl.exists()) {
|
||||||
|
prefs.put("aslDir", path);
|
||||||
|
txtASLDir.setText(path);
|
||||||
|
if (aslError)
|
||||||
|
setErrorCondition(0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnCompileMouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(evt)) {
|
||||||
|
JTextField[] dirFields = {txtASLDir, txtInputDir, txtOutputDir};
|
||||||
|
for (byte i = 0; i < 3; i++) {
|
||||||
|
File bleh = new File(dirFields[i].getText());
|
||||||
|
setErrorCondition(i, !bleh.exists());
|
||||||
|
}
|
||||||
|
if (aslError || inputError || outputError)
|
||||||
|
return;
|
||||||
|
String prettyPrinting = cbPrettyPrinting.isSelected() ? "-pretty" : "", compileAll = cbCompileAll.isSelected() ? "-r" : "", asl = txtASLDir.getText(), input = txtInputDir.getText(), output = txtOutputDir.getText(), error = " ";
|
||||||
|
try {
|
||||||
|
Process aslProcess = new ProcessBuilder(asl, compileAll, prettyPrinting, input, output).start();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(aslProcess.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
if (line.toLowerCase().contains("panic")) {
|
||||||
|
error = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aslProcess.waitFor();
|
||||||
|
} catch (IOException | InterruptedException ex) {
|
||||||
|
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
if (!error.equals(" ")) {
|
||||||
|
DlgError showError = new DlgError(this, true, error);
|
||||||
|
showError.setLocationRelativeTo(this);
|
||||||
|
showError.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cbCompileAllStateChanged(javax.swing.event.ChangeEvent evt) {
|
||||||
|
prefs.putBoolean("compileAll", cbCompileAll.isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cbPrettyPrintingStateChanged(javax.swing.event.ChangeEvent evt) {
|
||||||
|
prefs.putBoolean("prettyPrinting", cbPrettyPrinting.isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lblOutputErrorMouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(evt) && outputError) {
|
||||||
|
new File(txtOutputDir.getText()).mkdirs();
|
||||||
|
setErrorCondition(2, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lblASLErrorMouseClicked(java.awt.event.MouseEvent evt) {
|
||||||
|
if (SwingUtilities.isLeftMouseButton(evt) && aslError) {
|
||||||
|
try {
|
||||||
|
URI github = new URI("https://github.com/DeKugelschieber/asl/releases");
|
||||||
|
java.awt.Desktop.getDesktop().browse(github);
|
||||||
|
} catch (URISyntaxException | IOException ex) {
|
||||||
|
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
setErrorCondition(0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
|
||||||
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
|
||||||
|
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
UIManager.put("ToolTip.background", new ColorUIResource(255, 255, 255));
|
||||||
|
UIManager.put("ToolTip.foreground", new ColorUIResource(87, 87, 87));
|
||||||
|
Border lineBorder = BorderFactory.createLineBorder(new Color(118, 118, 118));
|
||||||
|
UIManager.put("ToolTip.border", lineBorder);
|
||||||
|
Border compoundBorder = BorderFactory.createCompoundBorder(UIManager.getBorder("ToolTip.border"), BorderFactory.createEmptyBorder(0, 2, 2, 3));
|
||||||
|
UIManager.put("ToolTip.border", compoundBorder);
|
||||||
|
java.awt.EventQueue.invokeLater(() -> {
|
||||||
|
new Main().setVisible(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private javax.swing.JButton btnASL;
|
||||||
|
private javax.swing.JButton btnCompile;
|
||||||
|
private javax.swing.JButton btnInput;
|
||||||
|
private javax.swing.JButton btnOutput;
|
||||||
|
private javax.swing.JCheckBox cbCompileAll;
|
||||||
|
private javax.swing.JCheckBox cbPrettyPrinting;
|
||||||
|
private javax.swing.JSeparator jSeparator;
|
||||||
|
private javax.swing.JLabel lblASL;
|
||||||
|
private javax.swing.JLabel lblASLError;
|
||||||
|
private javax.swing.JLabel lblASLSmall;
|
||||||
|
private javax.swing.JLabel lblInput;
|
||||||
|
private javax.swing.JLabel lblInputError;
|
||||||
|
private javax.swing.JLabel lblInputSmall;
|
||||||
|
private javax.swing.JLabel lblOutput;
|
||||||
|
private javax.swing.JLabel lblOutputError;
|
||||||
|
private javax.swing.JLabel lblOutputSmall;
|
||||||
|
private javax.swing.JTextField txtASLDir;
|
||||||
|
private javax.swing.JTextField txtInputDir;
|
||||||
|
private javax.swing.JTextField txtOutputDir;
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
var array = [1, 2, 3];
|
|
||||||
var x = array[0];
|
|
||||||
|
|
||||||
if array[(1-1)+10/10] == 2 {
|
|
||||||
foo();
|
|
||||||
}
|
|
||||||
@@ -1,485 +0,0 @@
|
|||||||
package asl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const TAB = " "
|
|
||||||
|
|
||||||
// Parses tokens, validates code to a specific degree
|
|
||||||
// and writes SQF code into desired location.
|
|
||||||
func Parse(token []Token, prettyPrinting bool) string {
|
|
||||||
if !initParser(token, prettyPrinting) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for tokenIndex < len(token) {
|
|
||||||
parseBlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBlock() {
|
|
||||||
if accept("var") {
|
|
||||||
parseVar()
|
|
||||||
} else if accept("if") {
|
|
||||||
parseIf()
|
|
||||||
} else if accept("while") {
|
|
||||||
parseWhile()
|
|
||||||
} else if accept("switch") {
|
|
||||||
parseSwitch()
|
|
||||||
} else if accept("for") {
|
|
||||||
parseFor()
|
|
||||||
} else if accept("foreach") {
|
|
||||||
parseForeach()
|
|
||||||
} else if accept("func") {
|
|
||||||
parseFunction()
|
|
||||||
} else if accept("return") {
|
|
||||||
parseReturn()
|
|
||||||
} else if accept("try") {
|
|
||||||
parseTryCatch()
|
|
||||||
} else if accept("exitwith") {
|
|
||||||
parseExitWith()
|
|
||||||
} else if accept("waituntil") {
|
|
||||||
parseWaitUntil()
|
|
||||||
} else if accept("case") || accept("default") {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
parseStatement()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !end() && !accept("}") {
|
|
||||||
parseBlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseVar() {
|
|
||||||
expect("var")
|
|
||||||
appendOut(get().token, false)
|
|
||||||
next()
|
|
||||||
|
|
||||||
if accept("=") {
|
|
||||||
next()
|
|
||||||
appendOut(" = ", false)
|
|
||||||
|
|
||||||
if accept("[") {
|
|
||||||
parseArray()
|
|
||||||
} else {
|
|
||||||
parseExpression(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(";")
|
|
||||||
appendOut(";", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseArray() {
|
|
||||||
expect("[")
|
|
||||||
appendOut("[", false)
|
|
||||||
|
|
||||||
if !accept("]") {
|
|
||||||
parseExpression(true)
|
|
||||||
|
|
||||||
for accept(",") {
|
|
||||||
next()
|
|
||||||
appendOut(",", false)
|
|
||||||
parseExpression(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect("]")
|
|
||||||
appendOut("]", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseIf() {
|
|
||||||
expect("if")
|
|
||||||
appendOut("if (", false)
|
|
||||||
parseExpression(true)
|
|
||||||
appendOut(") then {", true)
|
|
||||||
expect("{")
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
|
|
||||||
if accept("else") {
|
|
||||||
next()
|
|
||||||
expect("{")
|
|
||||||
appendOut("} else {", true)
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
}
|
|
||||||
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseWhile() {
|
|
||||||
expect("while")
|
|
||||||
appendOut("while {", false)
|
|
||||||
parseExpression(true)
|
|
||||||
appendOut("} do {", true)
|
|
||||||
expect("{")
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
appendOut("};", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSwitch() {
|
|
||||||
expect("switch")
|
|
||||||
appendOut("switch (", false)
|
|
||||||
parseExpression(true)
|
|
||||||
appendOut(") do {", true)
|
|
||||||
expect("{")
|
|
||||||
parseSwitchBlock()
|
|
||||||
expect("}")
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSwitchBlock() {
|
|
||||||
if accept("}") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if accept("case") {
|
|
||||||
next()
|
|
||||||
appendOut("case ", false)
|
|
||||||
parseExpression(true)
|
|
||||||
expect(":")
|
|
||||||
appendOut(":", true)
|
|
||||||
|
|
||||||
if !accept("case") && !accept("}") && !accept("default") {
|
|
||||||
appendOut("{", true)
|
|
||||||
parseBlock()
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
} else if accept("default") {
|
|
||||||
next()
|
|
||||||
expect(":")
|
|
||||||
appendOut("default:", true)
|
|
||||||
|
|
||||||
if !accept("}") {
|
|
||||||
appendOut("{", true)
|
|
||||||
parseBlock()
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parseSwitchBlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFor() {
|
|
||||||
expect("for")
|
|
||||||
appendOut("for [{", false)
|
|
||||||
|
|
||||||
// var in first assignment is optional
|
|
||||||
if accept("var") {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
|
|
||||||
parseExpression(true)
|
|
||||||
expect(";")
|
|
||||||
appendOut("}, {", false)
|
|
||||||
parseExpression(true)
|
|
||||||
expect(";")
|
|
||||||
appendOut("}, {", false)
|
|
||||||
parseExpression(true)
|
|
||||||
appendOut("}] do {", true)
|
|
||||||
expect("{")
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseForeach() {
|
|
||||||
expect("foreach")
|
|
||||||
expr := parseExpression(false)
|
|
||||||
expect("{")
|
|
||||||
appendOut("{", true)
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
appendOut("} forEach ("+expr+");", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFunction() {
|
|
||||||
expect("func")
|
|
||||||
appendOut(get().token+" = {", true)
|
|
||||||
next()
|
|
||||||
expect("(")
|
|
||||||
parseFunctionParameter()
|
|
||||||
expect(")")
|
|
||||||
expect("{")
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFunctionParameter() {
|
|
||||||
// empty parameter list
|
|
||||||
if accept("{") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
i := int64(0)
|
|
||||||
|
|
||||||
for !accept(")") {
|
|
||||||
name := get().token
|
|
||||||
next()
|
|
||||||
appendOut(name+" = _this select "+strconv.FormatInt(i, 10)+";", true)
|
|
||||||
i++
|
|
||||||
|
|
||||||
if !accept(")") {
|
|
||||||
expect(",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseReturn() {
|
|
||||||
expect("return")
|
|
||||||
appendOut("return ", false)
|
|
||||||
parseExpression(true)
|
|
||||||
expect(";")
|
|
||||||
appendOut(";", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTryCatch() {
|
|
||||||
expect("try")
|
|
||||||
expect("{")
|
|
||||||
appendOut("try {", true)
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
expect("catch")
|
|
||||||
expect("{")
|
|
||||||
appendOut("} catch {", true)
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseExitWith() {
|
|
||||||
expect("exitwith")
|
|
||||||
expect("{")
|
|
||||||
appendOut("if (true) exitWith {", true)
|
|
||||||
parseBlock()
|
|
||||||
expect("}")
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseWaitUntil() {
|
|
||||||
expect("waituntil")
|
|
||||||
expect("(")
|
|
||||||
appendOut("waitUntil {", false)
|
|
||||||
parseExpression(true)
|
|
||||||
|
|
||||||
if accept(";") {
|
|
||||||
next()
|
|
||||||
appendOut(";", false)
|
|
||||||
parseExpression(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(")")
|
|
||||||
expect(";")
|
|
||||||
appendOut("};", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything that does not start with a keyword.
|
|
||||||
func parseStatement() {
|
|
||||||
// empty block
|
|
||||||
if accept("}") || accept("case") || accept("default") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// variable or function name
|
|
||||||
name := get().token
|
|
||||||
next()
|
|
||||||
|
|
||||||
if accept("=") {
|
|
||||||
appendOut(name, false)
|
|
||||||
parseAssignment()
|
|
||||||
} else {
|
|
||||||
parseFunctionCall(true, name)
|
|
||||||
expect(";")
|
|
||||||
appendOut(";", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !end() {
|
|
||||||
parseBlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAssignment() {
|
|
||||||
expect("=")
|
|
||||||
appendOut(" = ", false)
|
|
||||||
parseExpression(true)
|
|
||||||
expect(";")
|
|
||||||
appendOut(";", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFunctionCall(out bool, name string) string {
|
|
||||||
output := ""
|
|
||||||
|
|
||||||
expect("(")
|
|
||||||
leftParams, leftParamCount := parseParameter(false)
|
|
||||||
expect(")")
|
|
||||||
|
|
||||||
if accept("(") {
|
|
||||||
// buildin function
|
|
||||||
next()
|
|
||||||
rightParams, rightParamCount := parseParameter(false)
|
|
||||||
expect(")")
|
|
||||||
|
|
||||||
if leftParamCount > 1 {
|
|
||||||
leftParams = "[" + leftParams + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
if rightParamCount > 1 {
|
|
||||||
rightParams = "[" + rightParams + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftParamCount > 0 {
|
|
||||||
output = leftParams + " " + name + " " + rightParams
|
|
||||||
} else {
|
|
||||||
output = name + " " + rightParams
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output = "[" + leftParams + "] call " + name
|
|
||||||
}
|
|
||||||
|
|
||||||
if out {
|
|
||||||
appendOut(output, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseParameter(out bool) (string, int) {
|
|
||||||
output := ""
|
|
||||||
count := 0
|
|
||||||
|
|
||||||
for !accept(")") {
|
|
||||||
output += parseExpression(out)
|
|
||||||
count++
|
|
||||||
|
|
||||||
if !accept(")") {
|
|
||||||
expect(",")
|
|
||||||
output += ", "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if out {
|
|
||||||
appendOut(output, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output, count
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseExpression(out bool) string {
|
|
||||||
output := parseArith()
|
|
||||||
|
|
||||||
for accept("<") || accept(">") || accept("&") || accept("|") || accept("=") || accept("!") {
|
|
||||||
if accept("<") {
|
|
||||||
output += "<"
|
|
||||||
next()
|
|
||||||
} else if accept(">") {
|
|
||||||
output += ">"
|
|
||||||
next()
|
|
||||||
} else if accept("&") {
|
|
||||||
next()
|
|
||||||
expect("&")
|
|
||||||
output += "&&"
|
|
||||||
} else if accept("|") {
|
|
||||||
next()
|
|
||||||
expect("|")
|
|
||||||
output += "||"
|
|
||||||
} else if accept("=") {
|
|
||||||
output += "="
|
|
||||||
next()
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
expect("=")
|
|
||||||
output += "!="
|
|
||||||
}
|
|
||||||
|
|
||||||
if accept("=") {
|
|
||||||
output += "="
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
|
|
||||||
output += parseExpression(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if out {
|
|
||||||
appendOut(output, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseIdentifier() string {
|
|
||||||
output := ""
|
|
||||||
|
|
||||||
if seek("(") && !accept("!") && !accept("-") {
|
|
||||||
name := get().token
|
|
||||||
next()
|
|
||||||
output = "(" + parseFunctionCall(false, name) + ")"
|
|
||||||
} else if seek("[") {
|
|
||||||
output += "("+get().token
|
|
||||||
next()
|
|
||||||
expect("[")
|
|
||||||
output += " select ("+parseExpression(false)+"))"
|
|
||||||
expect("]")
|
|
||||||
} else if accept("!") || accept("-") {
|
|
||||||
output = get().token
|
|
||||||
next()
|
|
||||||
output += parseTerm()
|
|
||||||
} else {
|
|
||||||
output = get().token
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTerm() string {
|
|
||||||
if accept("(") {
|
|
||||||
expect("(")
|
|
||||||
output := "(" + parseExpression(false) + ")"
|
|
||||||
expect(")")
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseIdentifier()
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFactor() string {
|
|
||||||
output := parseTerm()
|
|
||||||
|
|
||||||
for accept("*") || accept("/") { // TODO: modulo?
|
|
||||||
if accept("*") {
|
|
||||||
output += "*"
|
|
||||||
} else {
|
|
||||||
output += "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
next()
|
|
||||||
output += parseExpression(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseArith() string {
|
|
||||||
output := parseFactor()
|
|
||||||
|
|
||||||
for accept("+") || accept("-") {
|
|
||||||
if accept("+") {
|
|
||||||
output += "+"
|
|
||||||
} else {
|
|
||||||
output += "-"
|
|
||||||
}
|
|
||||||
|
|
||||||
next()
|
|
||||||
output += parseExpression(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package asl
|
|
||||||
|
|
||||||
var tokens []Token
|
|
||||||
var tokenIndex int
|
|
||||||
var out string
|
|
||||||
var offset int
|
|
||||||
var pretty bool
|
|
||||||
|
|
||||||
// Initilizes the parser.
|
|
||||||
func initParser(token []Token, prettyPrinting bool) bool {
|
|
||||||
if len(token) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens = token
|
|
||||||
tokenIndex = 0
|
|
||||||
out = ""
|
|
||||||
offset = 0
|
|
||||||
pretty = prettyPrinting
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true, if current token matches expected one.
|
|
||||||
// Does not throw parse errors and checks if token is available.
|
|
||||||
func accept(token string) bool {
|
|
||||||
return tokenIndex < len(tokens) && tokenEqual(token, get())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hard version of "accept".
|
|
||||||
// Throws if current token does not match expected one.
|
|
||||||
func expect(token string) {
|
|
||||||
if !tokenEqual(token, get()) {
|
|
||||||
panic("Parse error, expected '" + token + "' but was '" + get().token + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true, if the next token matches expected one.
|
|
||||||
// Does not throw parse errors and checks if token is available.
|
|
||||||
func seek(token string) bool {
|
|
||||||
if tokenIndex+1 >= len(tokens) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenEqual(token, tokens[tokenIndex+1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increases token counter, so that the next token is compared.
|
|
||||||
func next() {
|
|
||||||
tokenIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns current token or throws, if no more tokens are available.
|
|
||||||
func get() Token {
|
|
||||||
if tokenIndex >= len(tokens) {
|
|
||||||
panic("No more tokens")
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens[tokenIndex]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the end of input code was reached.
|
|
||||||
func end() bool {
|
|
||||||
return tokenIndex == len(tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if two strings match.
|
|
||||||
func tokenEqual(a string, b Token) bool {
|
|
||||||
return a == b.token
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appends the output string to current SQF code output.
|
|
||||||
func appendOut(str string, newLine bool) {
|
|
||||||
out += str
|
|
||||||
|
|
||||||
if newLine && pretty {
|
|
||||||
out += "\r\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package asl_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"asl"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParserDeclaration(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/tokenizer_var.asl")
|
|
||||||
want := "x = 1;\narray = [1,2,3];\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserAssignment(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_assignment.asl")
|
|
||||||
want := "x = 1;\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserIf(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/tokenizer_if.asl")
|
|
||||||
want := "if (a<b) then {\n};\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserWhile(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/tokenizer_while.asl")
|
|
||||||
want := "while {true} do {\n};"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserFor(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/tokenizer_for.asl")
|
|
||||||
want := "for [{i=0}, {i<100}, {i=i+1}] do {\n};\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserForeach(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/tokenizer_foreach.asl")
|
|
||||||
want := "{\n} forEach (allUnits);\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserSwitch(t *testing.T) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserFunction(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/tokenizer_func.asl")
|
|
||||||
want := "TestFunction = {\nparam0 = _this select 0;\nparam1 = _this select 1;\nreturn true;\n};\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserAssignResult(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_assign_result.asl")
|
|
||||||
want := "x = ([1, 2, 3] call foo);\ny = ([1, 2, 3] call bar);\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserExpression(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_expression.asl")
|
|
||||||
want := "x = -(1+(2+3))/(6*(someVariable+99-100))-(20)+!anotherVariable+([] call foo);\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserExpression2(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_expression2.asl")
|
|
||||||
want := "x = true||(3>=4&&5<8);\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserFunctionCall(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_func_call.asl")
|
|
||||||
want := "myFunc = {\na = _this select 0;\nb = _this select 1;\nreturn a>b;\n};\n[1+3/4, 2-(66*22)/3-((123))] call myFunc;\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\"]);\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserOperator(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_operator.asl")
|
|
||||||
want := "if (x==y&&x!=y&&x<=y&&x>=y&&x<y&&x>y) then {\n};\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserTryCatch(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_try_catch.asl")
|
|
||||||
want := "try {\n} catch {\n};\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserNegationFunctionCall(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_negation.asl")
|
|
||||||
want := "x = !([] call foo);\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserExitWith(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_exitwith.asl")
|
|
||||||
want := "if (true) exitWith {\n};\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserWaitUntil(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_waituntil.asl")
|
|
||||||
want := "waitUntil {x=x+1;x<100};\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserArray(t *testing.T) {
|
|
||||||
got := getCompiled(t, "test/parser_array.asl")
|
|
||||||
want := "x = [1,2,3];\ny = (x select (1));\n"
|
|
||||||
|
|
||||||
equal(t, got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCompiled(t *testing.T, file string) string {
|
|
||||||
code, err := ioutil.ReadFile(file)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Error("Could not read test file: " + file)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens := asl.Tokenize(code)
|
|
||||||
|
|
||||||
return asl.Parse(tokens, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func equal(t *testing.T, got, want string) {
|
|
||||||
if got != want {
|
|
||||||
t.Error("Results do not equal, got:")
|
|
||||||
t.Log(got)
|
|
||||||
t.Log("expected:")
|
|
||||||
t.Log(want)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
241
src/main/asl.go
241
src/main/asl.go
@@ -1,29 +1,37 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"asl"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
|
||||||
"os"
|
"os"
|
||||||
|
"parser"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"tokenizer"
|
||||||
|
"types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "1.0.0"
|
const (
|
||||||
const extension = ".asl"
|
version = "1.2.0"
|
||||||
const sqfextension = ".sqf"
|
extension = ".asl"
|
||||||
|
sqfextension = ".sqf"
|
||||||
|
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 recursive bool = false
|
var (
|
||||||
var pretty bool = false
|
recursive bool = false
|
||||||
var exit bool = false
|
pretty bool = false
|
||||||
var aslFiles []ASLFile
|
exit bool = false
|
||||||
var inDir string
|
aslFiles []ASLFile
|
||||||
|
inDir string
|
||||||
|
)
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
fmt.Println("Usage: asl [-v|-r|-pretty|--help] <input directory> <output directory>\n")
|
fmt.Println("Usage: asl [-v|-r|-pretty|--help] <input directory> <output directory>\n")
|
||||||
@@ -35,109 +43,158 @@ 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" {
|
if flag == "-v" {
|
||||||
recursive = true
|
fmt.Println("asl version " + version)
|
||||||
} else if flag == "-pretty" {
|
exit = true
|
||||||
pretty = true
|
} else if flag == "-r" {
|
||||||
} else if flag == "--help" {
|
recursive = true
|
||||||
usage()
|
} else if flag == "-pretty" {
|
||||||
exit = true
|
pretty = true
|
||||||
}
|
} else if flag == "--help" {
|
||||||
|
usage()
|
||||||
return true
|
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 {
|
|
||||||
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+"/"+name))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !dir[i].IsDir() && strings.ToLower(filepath.Ext(name)) == extension {
|
if err != nil {
|
||||||
in := filepath.FromSlash(path+"/"+dir[i].Name())
|
fmt.Println("Error reading in directory!")
|
||||||
out := filepath.FromSlash("./"+path[len(inDir):len(path)])
|
return
|
||||||
newname := name[:len(name)-len(filepath.Ext(name))]
|
}
|
||||||
|
|
||||||
file := ASLFile{in, out, newname}
|
for i := 0; i < len(dir); i++ {
|
||||||
aslFiles = append(aslFiles, file)
|
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, 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+"/"+aslFiles[i].out+"/"+aslFiles[i].newname+sqfextension)
|
|
||||||
fmt.Println(aslFiles[i].in+" -> "+out)
|
// fire compile
|
||||||
code, err := ioutil.ReadFile(aslFiles[i].in)
|
for i := 0; i < len(aslFiles); i++ {
|
||||||
|
go compileFile(path, aslFiles[i], waiter)
|
||||||
if err != nil {
|
}
|
||||||
fmt.Println("Error reading file: "+aslFiles[i].in)
|
|
||||||
continue
|
// wait until all files are compiled
|
||||||
}
|
for i := 0; i < len(aslFiles); i++ {
|
||||||
|
<-waiter
|
||||||
token := asl.Tokenize(code)
|
}
|
||||||
sqf := asl.Parse(token, pretty)
|
|
||||||
|
|
||||||
os.MkdirAll(filepath.FromSlash(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() {
|
func main() {
|
||||||
args := os.Args
|
args := os.Args
|
||||||
|
|
||||||
// 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 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if exit {
|
||||||
|
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)
|
||||||
compile(out)
|
compile(out)
|
||||||
}
|
}
|
||||||
|
|||||||
568
src/parser/parser.go
Normal file
568
src/parser/parser.go
Normal file
@@ -0,0 +1,568 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"tokenizer"
|
||||||
|
"types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const new_line = "\r\n"
|
||||||
|
|
||||||
|
// Parses tokens, validates code to a specific degree
|
||||||
|
// and writes SQF code into desired location.
|
||||||
|
func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
|
||||||
|
if !c.initParser(token, prettyPrinting) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for c.tokenIndex < len(token) {
|
||||||
|
c.parseBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseBlock() {
|
||||||
|
if c.get().Preprocessor {
|
||||||
|
c.parsePreprocessor()
|
||||||
|
} else if c.accept("var") {
|
||||||
|
c.parseVar()
|
||||||
|
} else if c.accept("if") {
|
||||||
|
c.parseIf()
|
||||||
|
} else if c.accept("while") {
|
||||||
|
c.parseWhile()
|
||||||
|
} else if c.accept("switch") {
|
||||||
|
c.parseSwitch()
|
||||||
|
} else if c.accept("for") {
|
||||||
|
c.parseFor()
|
||||||
|
} else if c.accept("foreach") {
|
||||||
|
c.parseForeach()
|
||||||
|
} else if c.accept("func") {
|
||||||
|
c.parseFunction()
|
||||||
|
} else if c.accept("return") {
|
||||||
|
c.parseReturn()
|
||||||
|
} else if c.accept("try") {
|
||||||
|
c.parseTryCatch()
|
||||||
|
} else if c.accept("exitwith") {
|
||||||
|
c.parseExitWith()
|
||||||
|
} else if c.accept("waituntil") {
|
||||||
|
c.parseWaitUntil()
|
||||||
|
} else if c.accept("case") || c.accept("default") {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
c.parseStatement()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.end() && !c.accept("}") {
|
||||||
|
c.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()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseVar() {
|
||||||
|
c.expect("var")
|
||||||
|
c.appendOut(c.get().Token, false)
|
||||||
|
c.next()
|
||||||
|
|
||||||
|
if c.accept("=") {
|
||||||
|
c.next()
|
||||||
|
c.appendOut(" = ", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.expect(";")
|
||||||
|
c.appendOut(";", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseArray(out bool) string {
|
||||||
|
output := ""
|
||||||
|
c.expect("[")
|
||||||
|
output += "["
|
||||||
|
|
||||||
|
if !c.accept("]") {
|
||||||
|
output += c.parseExpression(false)
|
||||||
|
|
||||||
|
for c.accept(",") {
|
||||||
|
c.next()
|
||||||
|
output += "," + c.parseExpression(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.expect("]")
|
||||||
|
output += "]"
|
||||||
|
|
||||||
|
if out {
|
||||||
|
c.appendOut(output, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseIf() {
|
||||||
|
c.expect("if")
|
||||||
|
c.appendOut("if (", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.appendOut(") then {", true)
|
||||||
|
c.expect("{")
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
|
||||||
|
if c.accept("else") {
|
||||||
|
c.next()
|
||||||
|
c.expect("{")
|
||||||
|
c.appendOut("} else {", true)
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.appendOut("};", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseWhile() {
|
||||||
|
c.expect("while")
|
||||||
|
c.appendOut("while {", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.appendOut("} do {", true)
|
||||||
|
c.expect("{")
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
c.appendOut("};", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseSwitch() {
|
||||||
|
c.expect("switch")
|
||||||
|
c.appendOut("switch (", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.appendOut(") do {", true)
|
||||||
|
c.expect("{")
|
||||||
|
c.parseSwitchBlock()
|
||||||
|
c.expect("}")
|
||||||
|
c.appendOut("};", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseSwitchBlock() {
|
||||||
|
if c.accept("}") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.accept("case") {
|
||||||
|
c.next()
|
||||||
|
c.appendOut("case ", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.expect(":")
|
||||||
|
c.appendOut(":", true)
|
||||||
|
|
||||||
|
if !c.accept("case") && !c.accept("}") && !c.accept("default") {
|
||||||
|
c.appendOut("{", true)
|
||||||
|
c.parseBlock()
|
||||||
|
c.appendOut("};", true)
|
||||||
|
}
|
||||||
|
} else if c.accept("default") {
|
||||||
|
c.next()
|
||||||
|
c.expect(":")
|
||||||
|
c.appendOut("default:", true)
|
||||||
|
|
||||||
|
if !c.accept("}") {
|
||||||
|
c.appendOut("{", true)
|
||||||
|
c.parseBlock()
|
||||||
|
c.appendOut("};", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.parseSwitchBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseFor() {
|
||||||
|
c.expect("for")
|
||||||
|
c.appendOut("for [{", false)
|
||||||
|
|
||||||
|
// var in first assignment is optional
|
||||||
|
if c.accept("var") {
|
||||||
|
c.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.expect(";")
|
||||||
|
c.appendOut("}, {", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.expect(";")
|
||||||
|
c.appendOut("}, {", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.appendOut("}] do {", true)
|
||||||
|
c.expect("{")
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
c.appendOut("};", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseForeach() {
|
||||||
|
c.expect("foreach")
|
||||||
|
element := c.get().Token
|
||||||
|
c.next()
|
||||||
|
c.expect("=")
|
||||||
|
c.expect(">")
|
||||||
|
expr := c.parseExpression(false)
|
||||||
|
c.expect("{")
|
||||||
|
c.appendOut("{", true)
|
||||||
|
c.appendOut(element+" = _x;", true)
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
c.appendOut("} forEach ("+expr+");", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
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("(")
|
||||||
|
c.parseFunctionParameter()
|
||||||
|
c.expect(")")
|
||||||
|
c.expect("{")
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
c.appendOut("};", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseFunctionParameter() {
|
||||||
|
// empty parameter list
|
||||||
|
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)
|
||||||
|
} else {
|
||||||
|
c.appendOut("\""+name+"\"", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.accept(")") {
|
||||||
|
c.expect(",")
|
||||||
|
c.appendOut(",", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.appendOut("];", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseReturn() {
|
||||||
|
c.expect("return")
|
||||||
|
c.appendOut("return ", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.expect(";")
|
||||||
|
c.appendOut(";", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseTryCatch() {
|
||||||
|
c.expect("try")
|
||||||
|
c.expect("{")
|
||||||
|
c.appendOut("try {", true)
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
c.expect("catch")
|
||||||
|
c.expect("{")
|
||||||
|
c.appendOut("} catch {", true)
|
||||||
|
c.parseBlock()
|
||||||
|
c.expect("}")
|
||||||
|
c.appendOut("};", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseExitWith() {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything that does not start with a keyword.
|
||||||
|
func (c *Compiler) parseStatement() {
|
||||||
|
// empty block
|
||||||
|
if c.accept("}") || c.accept("case") || c.accept("default") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// variable or function name
|
||||||
|
name := c.get().Token
|
||||||
|
c.next()
|
||||||
|
|
||||||
|
if c.accept("=") {
|
||||||
|
c.appendOut(name, false)
|
||||||
|
c.parseAssignment()
|
||||||
|
} else {
|
||||||
|
c.parseFunctionCall(true, name)
|
||||||
|
c.expect(";")
|
||||||
|
c.appendOut(";", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.end() {
|
||||||
|
c.parseBlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseAssignment() {
|
||||||
|
c.expect("=")
|
||||||
|
c.appendOut(" = ", false)
|
||||||
|
c.parseExpression(true)
|
||||||
|
c.expect(";")
|
||||||
|
c.appendOut(";", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseFunctionCall(out bool, name string) string {
|
||||||
|
output := ""
|
||||||
|
|
||||||
|
c.expect("(")
|
||||||
|
paramsStr, paramCount := c.parseParameter(false)
|
||||||
|
c.expect(")")
|
||||||
|
|
||||||
|
// 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 = c.parseBinaryFunction(name, paramsStr, buildin, paramCount)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output = "[" + paramsStr + "] call " + name
|
||||||
|
}
|
||||||
|
|
||||||
|
if out {
|
||||||
|
c.appendOut(output, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
output := ""
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for !c.accept(")") {
|
||||||
|
expr := c.parseExpression(out)
|
||||||
|
output += expr
|
||||||
|
count++
|
||||||
|
|
||||||
|
if !c.accept(")") {
|
||||||
|
c.expect(",")
|
||||||
|
output += ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if out {
|
||||||
|
c.appendOut(output, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseExpression(out bool) string {
|
||||||
|
output := c.parseArith()
|
||||||
|
|
||||||
|
for c.accept("<") || c.accept(">") || c.accept("&") || c.accept("|") || c.accept("=") || c.accept("!") {
|
||||||
|
if c.accept("<") {
|
||||||
|
output += "<"
|
||||||
|
c.next()
|
||||||
|
} else if c.accept(">") {
|
||||||
|
output += ">"
|
||||||
|
c.next()
|
||||||
|
} else if c.accept("&") {
|
||||||
|
c.next()
|
||||||
|
c.expect("&")
|
||||||
|
output += "&&"
|
||||||
|
} else if c.accept("|") {
|
||||||
|
c.next()
|
||||||
|
c.expect("|")
|
||||||
|
output += "||"
|
||||||
|
} else if c.accept("=") {
|
||||||
|
output += "="
|
||||||
|
c.next()
|
||||||
|
} else {
|
||||||
|
c.next()
|
||||||
|
c.expect("=")
|
||||||
|
output += "!="
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.accept("=") {
|
||||||
|
output += "="
|
||||||
|
c.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
output += c.parseExpression(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if out {
|
||||||
|
c.appendOut(output, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseIdentifier() string {
|
||||||
|
output := ""
|
||||||
|
|
||||||
|
if c.accept("code") {
|
||||||
|
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)
|
||||||
|
} else if c.seek("[") {
|
||||||
|
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()
|
||||||
|
output += c.parseTerm()
|
||||||
|
} else {
|
||||||
|
output = c.get().Token
|
||||||
|
c.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseTerm() string {
|
||||||
|
if c.accept("(") {
|
||||||
|
c.expect("(")
|
||||||
|
output := "(" + c.parseExpression(false) + ")"
|
||||||
|
c.expect(")")
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.parseIdentifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseFactor() string {
|
||||||
|
output := c.parseTerm()
|
||||||
|
|
||||||
|
for c.accept("*") || c.accept("/") { // TODO: modulo?
|
||||||
|
if c.accept("*") {
|
||||||
|
output += "*"
|
||||||
|
} else {
|
||||||
|
output += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
c.next()
|
||||||
|
output += c.parseExpression(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) parseArith() string {
|
||||||
|
output := c.parseFactor()
|
||||||
|
|
||||||
|
for c.accept("+") || c.accept("-") {
|
||||||
|
if c.accept("+") {
|
||||||
|
output += "+"
|
||||||
|
} else {
|
||||||
|
output += "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
c.next()
|
||||||
|
output += c.parseExpression(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
89
src/parser/parser_helper.go
Normal file
89
src/parser/parser_helper.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"tokenizer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Compiler struct {
|
||||||
|
tokens []tokenizer.Token
|
||||||
|
tokenIndex int
|
||||||
|
out string
|
||||||
|
offset int
|
||||||
|
pretty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initilizes the parser.
|
||||||
|
func (c *Compiler) initParser(token []tokenizer.Token, prettyPrinting bool) bool {
|
||||||
|
if len(token) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
c.tokens = token
|
||||||
|
c.tokenIndex = 0
|
||||||
|
c.out = ""
|
||||||
|
c.offset = 0
|
||||||
|
c.pretty = prettyPrinting
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true, if current token matches expected one.
|
||||||
|
// Does not throw parse errors and checks if token is available.
|
||||||
|
func (c *Compiler) accept(token string) bool {
|
||||||
|
return c.tokenIndex < len(c.tokens) && c.tokenEqual(token, c.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hard version of "accept".
|
||||||
|
// Throws if current token does not match expected one.
|
||||||
|
func (c *Compiler) expect(token string) {
|
||||||
|
if !c.tokenEqual(token, c.get()) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true, if the next token matches expected one.
|
||||||
|
// Does not throw parse errors and checks if token is available.
|
||||||
|
func (c *Compiler) seek(token string) bool {
|
||||||
|
if c.tokenIndex+1 >= len(c.tokens) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.tokenEqual(token, c.tokens[c.tokenIndex+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increases token counter, so that the next token is compared.
|
||||||
|
func (c *Compiler) next() {
|
||||||
|
c.tokenIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns current token or throws, if no more tokens are available.
|
||||||
|
func (c *Compiler) get() tokenizer.Token {
|
||||||
|
if c.tokenIndex >= len(c.tokens) {
|
||||||
|
panic(errors.New("No more tokens"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.tokens[c.tokenIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the end of input code was reached.
|
||||||
|
func (c *Compiler) end() bool {
|
||||||
|
return c.tokenIndex == len(c.tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if two strings match.
|
||||||
|
func (c *Compiler) tokenEqual(a string, b tokenizer.Token) bool {
|
||||||
|
return a == b.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appends the output string to current SQF code output.
|
||||||
|
func (c *Compiler) appendOut(str string, newLine bool) {
|
||||||
|
c.out += str
|
||||||
|
|
||||||
|
if newLine && c.pretty {
|
||||||
|
c.out += "\r\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
220
src/parser/parser_test.go
Normal file
220
src/parser/parser_test.go
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
package parser_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"parser"
|
||||||
|
"testing"
|
||||||
|
"tokenizer"
|
||||||
|
"types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
types_file = "../../test/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParserDeclaration(t *testing.T) {
|
||||||
|
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")
|
||||||
|
want := "x = 1;\r\n"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserIf(t *testing.T) {
|
||||||
|
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};"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserFor(t *testing.T) {
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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 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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
want := "x = !([] call foo);\r\n"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserExitWith(t *testing.T) {
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
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")
|
||||||
|
want := "inline_code = {a = 1;b = 2;if (a<b) then {[] call foo;};};\r\n"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserPreprocessor(t *testing.T) {
|
||||||
|
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")
|
||||||
|
want := "x = [1,2,3]-[2,3];\r\n"
|
||||||
|
|
||||||
|
equal(t, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCompiled(t *testing.T, file string) string {
|
||||||
|
code, err := ioutil.ReadFile(file)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Could not read test file: " + file)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := tokenizer.Tokenize(code, false)
|
||||||
|
compiler := parser.Compiler{}
|
||||||
|
|
||||||
|
return compiler.Parse(tokens, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func equal(t *testing.T, got, want string) {
|
||||||
|
if got != want {
|
||||||
|
t.Error("Results do not equal, got:")
|
||||||
|
t.Log(got)
|
||||||
|
t.Log("expected:")
|
||||||
|
t.Log(want)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,64 +1,82 @@
|
|||||||
package asl
|
package tokenizer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
token string
|
Token string
|
||||||
|
Preprocessor bool
|
||||||
|
Line 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"}
|
||||||
|
|
||||||
var whitespace = []byte{' ', '\n', '\t', '\r'}
|
whitespace = []byte{' ', '\n', '\t', '\r'}
|
||||||
var identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
identifier = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
||||||
|
preprocessor = byte('#')
|
||||||
|
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 := "", false, false
|
token, mask, isstring, line, column := "", false, false, 0, 0
|
||||||
|
|
||||||
for i := range code {
|
for i := 0; i < len(code); i++ {
|
||||||
c := code[i]
|
c := code[i]
|
||||||
|
column++
|
||||||
|
|
||||||
|
if byteArrayContains(new_line, c) {
|
||||||
|
line++
|
||||||
|
column = 0
|
||||||
|
}
|
||||||
|
|
||||||
// string masks (backslash)
|
// string masks (backslash)
|
||||||
if c == '\\' && !mask {
|
if c == '\\' && !mask {
|
||||||
@@ -77,16 +95,19 @@ func Tokenize(code []byte) []Token {
|
|||||||
if isstring {
|
if isstring {
|
||||||
token += string(c)
|
token += string(c)
|
||||||
} else {
|
} else {
|
||||||
// delimeter, keyword or variable/expression
|
// preprocessor, delimeter, keyword or variable/expression
|
||||||
if byteArrayContains(delimiter, c) {
|
if c == preprocessor {
|
||||||
|
tokens = append(tokens, preprocessorLine(code, &i, line, column))
|
||||||
|
token = ""
|
||||||
|
} else if byteArrayContains(delimiter, c) {
|
||||||
if token != "" {
|
if token != "" {
|
||||||
tokens = append(tokens, Token{token})
|
tokens = append(tokens, Token{token, false, line, column})
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens = append(tokens, Token{string(c)})
|
tokens = append(tokens, Token{string(c), false, line, column})
|
||||||
token = ""
|
token = ""
|
||||||
} else if stringArrayContains(strings.ToLower(token)) && !isIdentifierCharacter(c) {
|
} else if stringArrayContains(strings.ToLower(token)) && !isIdentifierCharacter(c) {
|
||||||
tokens = append(tokens, Token{token})
|
tokens = append(tokens, Token{token, false, line, column})
|
||||||
token = ""
|
token = ""
|
||||||
} else if !byteArrayContains(whitespace, c) {
|
} else if !byteArrayContains(whitespace, c) {
|
||||||
token += string(c)
|
token += string(c)
|
||||||
@@ -99,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).
|
||||||
@@ -137,6 +180,35 @@ func removeComments(code []byte) []byte {
|
|||||||
return newcode[:j]
|
return newcode[:j]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the next character in code starting at i.
|
// Returns the next character in code starting at i.
|
||||||
// If no character is left, '0' will be returned.
|
// If no character is left, '0' will be returned.
|
||||||
func nextChar(code []byte, i int) byte {
|
func nextChar(code []byte, i int) byte {
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package asl
|
package tokenizer_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"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)
|
||||||
@@ -14,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)
|
||||||
@@ -22,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)
|
||||||
@@ -30,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)
|
||||||
@@ -38,23 +39,23 @@ 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", "allUnits", "{", "}"}
|
want := []string{"foreach", "unit", "=", ">", "allUnits", "{", "}"}
|
||||||
|
|
||||||
compareLength(t, &got, &want)
|
compareLength(t, &got, &want)
|
||||||
compareTokens(t, &got, &want)
|
compareTokens(t, &got, &want)
|
||||||
}
|
}
|
||||||
|
|
||||||
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", ";", "break", ";", "case", "2", ":", "x", "=", "2", ";", "break", ";", "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)
|
||||||
compareTokens(t, &got, &want)
|
compareTokens(t, &got, &want)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -62,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)
|
||||||
@@ -70,20 +71,45 @@ 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)
|
||||||
compareTokens(t, &got, &want)
|
compareTokens(t, &got, &want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareLength(t *testing.T, got *[]Token, want *[]string) {
|
func TestTokenizerInlineCode(t *testing.T) {
|
||||||
|
got := getTokens(t, "../../test/tokenizer_code.asl")
|
||||||
|
want := []string{"var", "x", "=", "code", "(", "\"var x = 5;\"", ")", ";"}
|
||||||
|
|
||||||
|
compareLength(t, &got, &want)
|
||||||
|
compareTokens(t, &got, &want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenizerPreprocessor(t *testing.T) {
|
||||||
|
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")
|
||||||
|
want := []string{"var", "x", "=", "\"Hello \\\"World\\\"\"", ";",
|
||||||
|
"var", "y", "=", "code", "(", "\"var z = \\\"Hello \\\\\"World\\\\\"\\\";\"", ")", ";"}
|
||||||
|
|
||||||
|
compareLength(t, &got, &want)
|
||||||
|
compareTokens(t, &got, &want)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareLength(t *testing.T, got *[]tokenizer.Token, want *[]string) {
|
||||||
if len(*got) != len(*want) {
|
if len(*got) != len(*want) {
|
||||||
t.Error("Length of tokens got and expected tokens not equal, was:")
|
t.Error("Length of tokens got and expected tokens not equal, was:")
|
||||||
gotlist, wantlist := "", ""
|
gotlist, wantlist := "", ""
|
||||||
|
|
||||||
for i := range *got {
|
for i := range *got {
|
||||||
gotlist += (*got)[i].token + " "
|
gotlist += (*got)[i].Token + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range *want {
|
for i := range *want {
|
||||||
@@ -97,15 +123,15 @@ func compareLength(t *testing.T, got *[]Token, want *[]string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareTokens(t *testing.T, got *[]Token, want *[]string) {
|
func compareTokens(t *testing.T, got *[]tokenizer.Token, want *[]string) {
|
||||||
for i := range *got {
|
for i := range *got {
|
||||||
if (*got)[i].token != (*want)[i] {
|
if (*got)[i].Token != (*want)[i] {
|
||||||
t.Error("Tokens do not match: " + (*got)[i].token + " != " + (*want)[i])
|
t.Error("Tokens do not match: " + (*got)[i].Token + " != " + (*want)[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTokens(t *testing.T, file string) []Token {
|
func getTokens(t *testing.T, file string) []tokenizer.Token {
|
||||||
code, err := ioutil.ReadFile(file)
|
code, err := ioutil.ReadFile(file)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -113,5 +139,5 @@ func getTokens(t *testing.T, file string) []Token {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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_code.asl
Normal file
1
test/parser_code.asl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var inline_code = code("var a = 1;var b = 2;if a < b {foo();}");
|
||||||
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];
|
||||||
3
test/parser_func_params.asl
Normal file
3
test/parser_func_params.asl
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
func myFunc(a = 1, b = 2) {
|
||||||
|
return a+b;
|
||||||
|
}
|
||||||
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
test/tokenizer_code.asl
Normal file
1
test/tokenizer_code.asl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var x = code("var x = 5;");
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
foreach allUnits {
|
foreach unit => allUnits {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|||||||
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\\"\";");
|
||||||
2
test/tokenizer_preprocessor.asl
Normal file
2
test/tokenizer_preprocessor.asl
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define HELLO_WORLD "Hello World!"
|
||||||
|
hint(HELLO_WORLD);
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
switch x {
|
switch x {
|
||||||
case 1:
|
case 1:
|
||||||
x = 1;
|
x = 1;
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
x = 2;
|
x = 2;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
x = 3;
|
x = 3;
|
||||||
}
|
}
|
||||||
|
|||||||
2198
test/types
Normal file
2198
test/types
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user