Adds support for astypedef files, so we can register the interface as application interface, even when it uses functions that are not supported by angelscript itself.
This commit is contained in:
parent
fac54acbc9
commit
220c6d0080
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"brackets": [
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
[
|
||||
"(",
|
||||
")"
|
||||
],
|
||||
[
|
||||
"[",
|
||||
"]"
|
||||
],
|
||||
[
|
||||
"{",
|
||||
"}"
|
||||
]
|
||||
],
|
||||
"indentationRules": {
|
||||
"increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$",
|
||||
"decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$"
|
||||
}
|
||||
}
|
|
@ -37,7 +37,10 @@ export function activate(context: ExtensionContext) {
|
|||
|
||||
// Options to control the language client
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: 'file', language: 'Angelscript' }],
|
||||
documentSelector: [
|
||||
{ scheme: 'file', language: 'Angelscript' },
|
||||
{ scheme: 'file', language: 'AngelscriptTypeDefinition' }
|
||||
],
|
||||
synchronize: {
|
||||
fileEvents: workspace.createFileSystemWatcher('**/.clientrc'),
|
||||
}
|
||||
|
|
17
package.json
17
package.json
|
@ -4,7 +4,7 @@
|
|||
"author": "Deukhoofd",
|
||||
"publisher": "Deukhoofd",
|
||||
"license": "MIT",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.p-epsilon.com/Deukhoofd/AngelscriptLanguageServer"
|
||||
|
@ -17,7 +17,8 @@
|
|||
"vscode": "^1.43.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onLanguage:Angelscript"
|
||||
"onLanguage:Angelscript",
|
||||
"onLanguage:AngelscriptTypeDefinition"
|
||||
],
|
||||
"main": "./client/out/extension",
|
||||
"contributes": {
|
||||
|
@ -45,6 +46,13 @@
|
|||
"*.as"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
},
|
||||
{
|
||||
"id": "AngelscriptTypeDefinition",
|
||||
"filenamePatterns": [
|
||||
"*.astypedef"
|
||||
],
|
||||
"configuration": "./astypedef-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
|
@ -52,6 +60,11 @@
|
|||
"language": "Angelscript",
|
||||
"scopeName": "source.angelscript",
|
||||
"path": "./syntaxes/as.tmGrammar"
|
||||
},
|
||||
{
|
||||
"language": "AngelscriptTypeDefinition",
|
||||
"scopeName": "source.angelscript",
|
||||
"path": "./syntaxes/astypedef.tmGrammar"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.17)
|
|||
cmake_policy(SET CMP0042 NEW)
|
||||
project(aslsp-native)
|
||||
|
||||
option(BUILD_TESTS "Whether or not to build the test executable" ON)
|
||||
set (CMAKE_CXX_STANDARD 20)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
|
@ -24,4 +25,13 @@ string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
|
|||
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
|
||||
|
||||
# define NPI_VERSION
|
||||
add_definitions(-DNAPI_VERSION=6)
|
||||
add_definitions(-DNAPI_VERSION=6)
|
||||
|
||||
if (BUILD_TESTS)
|
||||
file(GLOB_RECURSE TEST_FILES "tests/*.cpp" "tests/*.hpp" "extern/doctest.hpp")
|
||||
add_executable(aslsp-test ${TEST_FILES} ${SOURCE_FILES} ${AS_FILES})
|
||||
target_link_libraries(aslsp-test -lpthread)
|
||||
target_compile_definitions(aslsp-test PRIVATE TESTS_BUILD)
|
||||
endif(BUILD_TESTS)
|
||||
|
||||
ADD_DEFINITIONS(-D AS_USE_ACCESSORS=1)
|
||||
|
|
|
@ -222,13 +222,13 @@ enum asEObjTypeFlags
|
|||
enum asEBehaviours
|
||||
{
|
||||
// Value object memory management
|
||||
asBEHAVE_CONSTRUCT,
|
||||
asBEHAVE_LIST_CONSTRUCT,
|
||||
asBEHAVE_DESTRUCT,
|
||||
asBEHAVE_CONSTRUCT = 0,
|
||||
asBEHAVE_LIST_CONSTRUCT = 1,
|
||||
asBEHAVE_DESTRUCT = 2,
|
||||
|
||||
// Reference object memory management
|
||||
asBEHAVE_FACTORY,
|
||||
asBEHAVE_LIST_FACTORY,
|
||||
asBEHAVE_FACTORY = 3,
|
||||
asBEHAVE_LIST_FACTORY = 4,
|
||||
asBEHAVE_ADDREF,
|
||||
asBEHAVE_RELEASE,
|
||||
asBEHAVE_GET_WEAKREF_FLAG,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,20 +3,20 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import {
|
||||
createConnection,
|
||||
TextDocuments,
|
||||
Diagnostic,
|
||||
DiagnosticSeverity,
|
||||
ProposedFeatures,
|
||||
InitializeParams,
|
||||
DidChangeConfigurationNotification,
|
||||
TextDocumentSyncKind,
|
||||
InitializeResult,
|
||||
Position,
|
||||
createConnection,
|
||||
TextDocuments,
|
||||
Diagnostic,
|
||||
DiagnosticSeverity,
|
||||
ProposedFeatures,
|
||||
InitializeParams,
|
||||
DidChangeConfigurationNotification,
|
||||
TextDocumentSyncKind,
|
||||
InitializeResult,
|
||||
Position,
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import {
|
||||
TextDocument
|
||||
TextDocument
|
||||
} from 'vscode-languageserver-textdocument';
|
||||
|
||||
import * as NativeWrapper from './wrapper'
|
||||
|
@ -47,74 +47,77 @@ let hasWorkspaceFolderCapability: boolean = false;
|
|||
let hasDiagnosticRelatedInformationCapability: boolean = false;
|
||||
|
||||
function registerFiles(directoryName: string) {
|
||||
let files = fs.readdirSync(directoryName);
|
||||
files.forEach(function (file) {
|
||||
let f = fs.statSync(directoryName + path.sep + file);
|
||||
if (f.isDirectory()) {
|
||||
registerFiles(directoryName + path.sep + file)
|
||||
} else {
|
||||
if (path.extname(file) == ".as") {
|
||||
const filepath = directoryName + path.sep + file;
|
||||
var uri = "file://" + filepath;
|
||||
if (!allFiles.get(uri)) {
|
||||
let td = TextDocument.create(uri, "Angelscript", 0, fs.readFileSync(filepath, 'utf8'));
|
||||
allFiles.set(uri, td);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
let files = fs.readdirSync(directoryName);
|
||||
files.forEach(function (file) {
|
||||
let f = fs.statSync(directoryName + path.sep + file);
|
||||
if (f.isDirectory()) {
|
||||
registerFiles(directoryName + path.sep + file)
|
||||
} else {
|
||||
if (path.extname(file) == ".as" || path.extname(file) == ".astypedef") {
|
||||
const filepath = directoryName + path.sep + file;
|
||||
var uri = "file://" + filepath;
|
||||
if (!allFiles.get(uri)) {
|
||||
let td = TextDocument.create(uri, "Angelscript", 0, fs.readFileSync(filepath, 'utf8'));
|
||||
allFiles.set(uri, td);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connection.onInitialize((params: InitializeParams) => {
|
||||
let capabilities = params.capabilities;
|
||||
let capabilities = params.capabilities;
|
||||
|
||||
hasConfigurationCapability = !!(
|
||||
capabilities.workspace && !!capabilities.workspace.configuration
|
||||
);
|
||||
hasWorkspaceFolderCapability = !!(
|
||||
capabilities.workspace && !!capabilities.workspace.workspaceFolders
|
||||
);
|
||||
hasDiagnosticRelatedInformationCapability = !!(
|
||||
capabilities.textDocument &&
|
||||
capabilities.textDocument.publishDiagnostics &&
|
||||
capabilities.textDocument.publishDiagnostics.relatedInformation
|
||||
);
|
||||
hasConfigurationCapability = !!(
|
||||
capabilities.workspace && !!capabilities.workspace.configuration
|
||||
);
|
||||
hasWorkspaceFolderCapability = !!(
|
||||
capabilities.workspace && !!capabilities.workspace.workspaceFolders
|
||||
);
|
||||
hasDiagnosticRelatedInformationCapability = !!(
|
||||
capabilities.textDocument &&
|
||||
capabilities.textDocument.publishDiagnostics &&
|
||||
capabilities.textDocument.publishDiagnostics.relatedInformation
|
||||
);
|
||||
|
||||
const result: InitializeResult = {
|
||||
capabilities: {
|
||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||
}
|
||||
};
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
result.capabilities.workspace = {
|
||||
workspaceFolders: {
|
||||
supported: true
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
const result: InitializeResult = {
|
||||
capabilities: {
|
||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||
}
|
||||
};
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
result.capabilities.workspace = {
|
||||
workspaceFolders: {
|
||||
supported: true
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
connection.onInitialized(() => {
|
||||
if (hasConfigurationCapability) {
|
||||
// Register for all configuration changes.
|
||||
connection.client.register(DidChangeConfigurationNotification.type, undefined);
|
||||
}
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
connection.workspace.
|
||||
connection.workspace.onDidChangeWorkspaceFolders(_event => {
|
||||
connection.console.log('Workspace folder change event received.');
|
||||
});
|
||||
}
|
||||
let isInitialized =false;
|
||||
|
||||
connection.workspace.getWorkspaceFolders().then((ws) => {
|
||||
ws?.forEach(element => {
|
||||
registerFiles(element.uri.substr(7))
|
||||
});
|
||||
database.reset();
|
||||
allFiles.forEach(loadScript);
|
||||
validateBuild();
|
||||
})
|
||||
connection.onInitialized(() => {
|
||||
if (hasConfigurationCapability) {
|
||||
// Register for all configuration changes.
|
||||
connection.client.register(DidChangeConfigurationNotification.type, undefined);
|
||||
}
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
connection.workspace.connection.workspace.onDidChangeWorkspaceFolders(_event => {
|
||||
connection.console.log('Workspace folder change event received.');
|
||||
});
|
||||
}
|
||||
|
||||
connection.workspace.getWorkspaceFolders().then((ws) => {
|
||||
ws?.forEach(element => {
|
||||
registerFiles(element.uri.substr(7))
|
||||
});
|
||||
database.reset();
|
||||
allFiles.forEach(loadScript);
|
||||
validateBuild().then(() => {
|
||||
isInitialized = true;
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
@ -124,111 +127,117 @@ connection.onDidChangeConfiguration(change => {
|
|||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent(change => {
|
||||
if (change.document.languageId != "Angelscript")
|
||||
return;
|
||||
var current = allFiles.get(change.document.uri);
|
||||
if (current != null && current != change.document){
|
||||
if (current.version == 0){
|
||||
allFiles.set(change.document.uri, change.document);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (current == null){
|
||||
allFiles.set(change.document.uri, change.document);
|
||||
}
|
||||
if (!isInitialized){
|
||||
return;
|
||||
}
|
||||
if (change.document.languageId != "Angelscript" && change.document.languageId != "AngelscriptTypeDefinition")
|
||||
return;
|
||||
var current = allFiles.get(change.document.uri);
|
||||
if (current != null && current != change.document) {
|
||||
if (current.version == 0) {
|
||||
allFiles.set(change.document.uri, change.document);
|
||||
return;
|
||||
}
|
||||
} else if (current == null) {
|
||||
allFiles.set(change.document.uri, change.document);
|
||||
}
|
||||
|
||||
database.reset();
|
||||
allFiles.forEach(loadScript);
|
||||
validateBuild();
|
||||
database.reset();
|
||||
allFiles.forEach(loadScript);
|
||||
validateBuild();
|
||||
});
|
||||
|
||||
function convertSeverity(type: NativeWrapper.MessageType): DiagnosticSeverity {
|
||||
switch (type) {
|
||||
case NativeWrapper.MessageType.Information:
|
||||
return DiagnosticSeverity.Information;
|
||||
case NativeWrapper.MessageType.Warning:
|
||||
return DiagnosticSeverity.Warning;
|
||||
case NativeWrapper.MessageType.Error:
|
||||
return DiagnosticSeverity.Error;
|
||||
}
|
||||
switch (type) {
|
||||
case NativeWrapper.MessageType.Information:
|
||||
return DiagnosticSeverity.Information;
|
||||
case NativeWrapper.MessageType.Warning:
|
||||
return DiagnosticSeverity.Warning;
|
||||
case NativeWrapper.MessageType.Error:
|
||||
return DiagnosticSeverity.Error;
|
||||
}
|
||||
}
|
||||
|
||||
function loadScript(textDocument: TextDocument): void {
|
||||
if (textDocument.languageId != "Angelscript")
|
||||
return;
|
||||
if (!fs.existsSync(textDocument.uri.substr(7))){
|
||||
allFiles.delete(textDocument.uri);
|
||||
return;
|
||||
}
|
||||
let text = textDocument.getText();
|
||||
database.loadScript(textDocument.uri, text);
|
||||
if (textDocument.languageId != "Angelscript" && textDocument.languageId != "AngelscriptTypeDefinition")
|
||||
return;
|
||||
if (!fs.existsSync(textDocument.uri.substr(7))) {
|
||||
allFiles.delete(textDocument.uri);
|
||||
return;
|
||||
}
|
||||
let text = textDocument.getText();
|
||||
if (path.extname(textDocument.uri) == ".astypedef") {
|
||||
database.loadTypeDef(textDocument.uri, text);
|
||||
} else {
|
||||
database.loadScript(textDocument.uri, text);
|
||||
}
|
||||
}
|
||||
|
||||
async function validateBuild(): Promise<void> {
|
||||
var r = database.build();
|
||||
if (r < -1) {
|
||||
console.log(r.toString());
|
||||
}
|
||||
console.log("Building");
|
||||
console.log("Building");
|
||||
var r = database.build();
|
||||
if (r <= -1) {
|
||||
console.log(r.toString());
|
||||
}
|
||||
|
||||
var messages = database.messages();
|
||||
let diagnostics: Map<string, Diagnostic[]> = new Map<string, Diagnostic[]>();
|
||||
var messages = database.messages();
|
||||
let diagnostics: Map<string, Diagnostic[]> = new Map<string, Diagnostic[]>();
|
||||
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
let msg = messages[i];
|
||||
if (msg.type == NativeWrapper.MessageType.Information)
|
||||
continue;
|
||||
if (msg.section == ""){
|
||||
if (msg.type == NativeWrapper.MessageType.Error){
|
||||
connection.window.showErrorMessage(msg.message);
|
||||
}
|
||||
else if (msg.type == NativeWrapper.MessageType.Warning){
|
||||
connection.window.showWarningMessage(msg.message);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let startPos = Position.create(msg.row - 1, msg.col - 1);
|
||||
if (startPos.line < 0) startPos.line = 0;
|
||||
if (startPos.character < 0) startPos.character = 0;
|
||||
let textDocument = documents.get(msg.section);
|
||||
if (textDocument == undefined)
|
||||
continue;
|
||||
let offset = textDocument.offsetAt(startPos);
|
||||
let diagnostic: Diagnostic = {
|
||||
severity: convertSeverity(msg.type),
|
||||
range: {
|
||||
start: startPos,
|
||||
end: textDocument.positionAt(offset + 1)
|
||||
},
|
||||
message: msg.message,
|
||||
source: 'Angelscript'
|
||||
};
|
||||
if (msg.type == NativeWrapper.MessageType.Error){
|
||||
connection.console.error(`[${msg.section}:${msg.row - 1},${msg.col - 1}] ${msg.message}`)
|
||||
}
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
let msg = messages[i];
|
||||
if (msg.type == NativeWrapper.MessageType.Information)
|
||||
continue;
|
||||
console.log('[ ' + msg.section + ' ] ' + msg.message);
|
||||
if (msg.section == "") {
|
||||
if (msg.type == NativeWrapper.MessageType.Error) {
|
||||
connection.window.showErrorMessage(msg.message);
|
||||
} else if (msg.type == NativeWrapper.MessageType.Warning) {
|
||||
connection.window.showWarningMessage(msg.message);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let startPos = Position.create(msg.row - 1, msg.col - 1);
|
||||
if (startPos.line < 0) startPos.line = 0;
|
||||
if (startPos.character < 0) startPos.character = 0;
|
||||
let textDocument = documents.get(msg.section);
|
||||
if (textDocument == undefined)
|
||||
continue;
|
||||
let offset = textDocument.offsetAt(startPos);
|
||||
let diagnostic: Diagnostic = {
|
||||
severity: convertSeverity(msg.type),
|
||||
range: {
|
||||
start: startPos,
|
||||
end: textDocument.positionAt(offset + 1)
|
||||
},
|
||||
message: msg.message,
|
||||
source: 'Angelscript'
|
||||
};
|
||||
if (msg.type == NativeWrapper.MessageType.Error) {
|
||||
connection.console.error(`[${msg.section}:${msg.row - 1},${msg.col - 1}] ${msg.message}`)
|
||||
}
|
||||
|
||||
var diag = diagnostics.get(msg.section);
|
||||
if (diag) {
|
||||
diag.push(diagnostic)
|
||||
}
|
||||
else {
|
||||
diagnostics.set(msg.section, [diagnostic]);
|
||||
}
|
||||
}
|
||||
var diag = diagnostics.get(msg.section);
|
||||
if (diag) {
|
||||
diag.push(diagnostic)
|
||||
} else {
|
||||
diagnostics.set(msg.section, [diagnostic]);
|
||||
}
|
||||
}
|
||||
|
||||
documents.all().forEach(element => {
|
||||
let v = diagnostics.get(element.uri);
|
||||
if (v)
|
||||
connection.sendDiagnostics({ uri: element.uri, diagnostics: v });
|
||||
else
|
||||
connection.sendDiagnostics({ uri: element.uri, diagnostics: [] });
|
||||
});
|
||||
// Send the computed diagnostics to VSCode.
|
||||
documents.all().forEach(element => {
|
||||
let v = diagnostics.get(element.uri);
|
||||
if (v)
|
||||
connection.sendDiagnostics({uri: element.uri, diagnostics: v});
|
||||
else
|
||||
connection.sendDiagnostics({uri: element.uri, diagnostics: []});
|
||||
});
|
||||
console.log("Finished building");
|
||||
// Send the computed diagnostics to VSCode.
|
||||
}
|
||||
|
||||
connection.onDidChangeWatchedFiles(_change => {
|
||||
// Monitored files have change in VSCode
|
||||
connection.console.log('We received an file change event');
|
||||
// Monitored files have change in VSCode
|
||||
connection.console.log('We received an file change event');
|
||||
});
|
||||
|
||||
// Make the text document manager listen on the connection
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
#include "Parser.hpp"
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <unordered_set>
|
||||
#include "TypeDefResult.hpp"
|
||||
#include "angelscript.h"
|
||||
|
||||
namespace ASTypeDefParser {
|
||||
std::string trim(const std::string& str) {
|
||||
size_t first = str.find_first_not_of(" \r\n\t");
|
||||
if (std::string::npos == first) {
|
||||
return str;
|
||||
}
|
||||
size_t last = str.find_last_not_of(" \r\n\t");
|
||||
return str.substr(first, (last - first + 1));
|
||||
}
|
||||
|
||||
static std::string ReadUpTo(const std::string& data, size_t& index, const std::unordered_set<char>& chars,
|
||||
char& lastChar) {
|
||||
std::stringstream s;
|
||||
while (index < data.size()) {
|
||||
lastChar = data.at(index);
|
||||
if (lastChar == ' ' || lastChar == '\t' || lastChar == '\n' || lastChar == '\r') {
|
||||
index++;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (index < data.size()) {
|
||||
lastChar = data.at(index);
|
||||
if (chars.contains(lastChar)) {
|
||||
break;
|
||||
}
|
||||
s << lastChar;
|
||||
index++;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
#define Assert(expression) \
|
||||
if (!(expression)) { \
|
||||
throw std::logic_error("Failed assertion: " #expression); \
|
||||
}
|
||||
|
||||
std::regex templateTypeCleaner("class ");
|
||||
std::regex propertyCleaner(R"((.*?)\s*(\w+)\s*\{\s*(get)?\s*(const)?;*\s*(set)?\s*(const)?;*\s*\})");
|
||||
|
||||
static void ParseType(const std::string& type, TypeDefResult& result, const std::string& data, size_t& index) {
|
||||
char end;
|
||||
auto def = trim(ReadUpTo(data, index, {'{', ';'}, end));
|
||||
|
||||
long flags;
|
||||
if (type == "type"){
|
||||
flags = asOBJ_REF | asOBJ_NOCOUNT;
|
||||
}
|
||||
else if (type == "valuetype"){
|
||||
flags = asOBJ_VALUE;
|
||||
}
|
||||
|
||||
std::string className = def;
|
||||
if (def.find('<') != std::string::npos) {
|
||||
flags |= asOBJ_TEMPLATE;
|
||||
className = std::regex_replace(className, templateTypeCleaner, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> definitions;
|
||||
std::vector<std::tuple<int, std::string>> behaviours;
|
||||
if (end == '{') {
|
||||
index++;
|
||||
while (index <= data.size()) {
|
||||
if (data.at(index) == '}') {
|
||||
break;
|
||||
}
|
||||
auto innerDef = trim(ReadUpTo(data, index, {';'}, end));
|
||||
|
||||
index++;
|
||||
if (innerDef.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (innerDef == "}") {
|
||||
break;
|
||||
}
|
||||
|
||||
if (innerDef.find("behave") == 0){
|
||||
char c;
|
||||
size_t pos = 0;
|
||||
ReadUpTo(innerDef, pos, {'b'}, c);
|
||||
ReadUpTo(innerDef, pos, {' '}, c);
|
||||
pos++;
|
||||
auto behaviour = ReadUpTo(innerDef, pos, {' '}, c);
|
||||
auto i = std::stoi(behaviour);
|
||||
auto d = ReadUpTo(innerDef, pos, {';'}, c);
|
||||
behaviours.emplace_back(i, d);
|
||||
}
|
||||
// Ugly hack for properties.
|
||||
else if (innerDef.find('{') != std::string::npos) {
|
||||
index--;
|
||||
innerDef += ReadUpTo(data, index, {'}'}, end) + end;
|
||||
index++;
|
||||
std::smatch m;
|
||||
if (std::regex_search(innerDef, m, propertyCleaner)) {
|
||||
auto ret = m[1].str();
|
||||
auto name = m[2].str();
|
||||
if (m.size() > 3) {
|
||||
if (m[3].str() == "get") {
|
||||
bool isConst = m.size() > 4 && m[4].str() == "const";
|
||||
auto decl = ret + " get_" + name + "() ";
|
||||
if (isConst) {
|
||||
decl += "const ";
|
||||
}
|
||||
decl += "property";
|
||||
definitions.push_back(decl);
|
||||
}
|
||||
}
|
||||
if (m.size() > 5) {
|
||||
if (m[5].str() == "set") {
|
||||
bool isConst = m.size() > 6 && m[6].str() == "const";
|
||||
auto decl = "void set_" + name + "(" + ret + " value) ";
|
||||
if (isConst) {
|
||||
decl += "const ";
|
||||
}
|
||||
decl += "property";
|
||||
definitions.push_back(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
definitions.push_back(innerDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
result.StoreType(type, def, className, flags, definitions, behaviours);
|
||||
}
|
||||
static void ParseEnum(TypeDefResult& result, const std::string& data, size_t& index) {
|
||||
char end;
|
||||
auto def = trim(ReadUpTo(data, index, {'{'}, end));
|
||||
index++;
|
||||
std::vector<std::tuple<std::string, int>> values;
|
||||
auto block = ReadUpTo(data, index, {'}'}, end);
|
||||
size_t innerIndex = 0;
|
||||
int enumValue = 0;
|
||||
while (true) {
|
||||
if (innerIndex >= block.size()) {
|
||||
break;
|
||||
}
|
||||
auto statement = trim(ReadUpTo(block, innerIndex, {',', '}'}, end));
|
||||
innerIndex++;
|
||||
if (statement.empty()) {
|
||||
continue;
|
||||
}
|
||||
size_t i = 0;
|
||||
char statementEnd;
|
||||
auto first = trim(ReadUpTo(statement, i, {'='}, statementEnd));
|
||||
if (statementEnd == '=') {
|
||||
i++;
|
||||
auto val = trim(ReadUpTo(statement, i, {}, statementEnd));
|
||||
auto num = std::stoi(val);
|
||||
enumValue = num;
|
||||
values.emplace_back(first, enumValue);
|
||||
} else {
|
||||
values.emplace_back(first, enumValue);
|
||||
}
|
||||
enumValue++;
|
||||
if (end == '}') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.StoreEnum(def, values);
|
||||
}
|
||||
|
||||
static void ParseFunc(TypeDefResult& result, const std::string& data, size_t& index) {
|
||||
char end;
|
||||
auto statement = ReadUpTo(data, index, {';'}, end);
|
||||
result.StoreFunc(statement);
|
||||
}
|
||||
|
||||
TypeDefResult Parser::ParseAndRegister(TypeDefResult& result, const std::string& data) {
|
||||
size_t index = 0;
|
||||
while (true) {
|
||||
if (index >= data.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto c = data[index];
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
std::stringstream identifier;
|
||||
while (c >= 'a' && c <= 'z') {
|
||||
identifier << c;
|
||||
|
||||
index++;
|
||||
if (index >= data.size()) {
|
||||
break;
|
||||
}
|
||||
c = data[index];
|
||||
}
|
||||
auto str = identifier.str();
|
||||
if (str == "type" || str == "valuetype") {
|
||||
index++;
|
||||
ParseType(str, result, data, index);
|
||||
} else if (str == "enum") {
|
||||
index++;
|
||||
ParseEnum(result, data, index);
|
||||
} else if (str == "func") {
|
||||
index++;
|
||||
ParseFunc(result, data, index);
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef ASLSP_NATIVE_PARSER_HPP
|
||||
#define ASLSP_NATIVE_PARSER_HPP
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "TypeDefResult.hpp"
|
||||
#include "angelscript.h"
|
||||
|
||||
namespace ASTypeDefParser {
|
||||
class Parser {
|
||||
public:
|
||||
static TypeDefResult ParseAndRegister(TypeDefResult& result, const std::string& data);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // ASLSP_NATIVE_PARSER_HPP
|
|
@ -0,0 +1,131 @@
|
|||
#ifndef ASLSP_NATIVE_TYPEDEFRESULT_HPP
|
||||
#define ASLSP_NATIVE_TYPEDEFRESULT_HPP
|
||||
#include <angelscript.h>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ASTypeDefParser {
|
||||
class TypeDefResult {
|
||||
struct Type {
|
||||
Type(std::string objectType, std::string classDef, std::string className, int flags,
|
||||
std::vector<std::string> definitions, std::vector<std::tuple<int, std::string>> behaviours)
|
||||
: ClassDef(std::move(classDef)), ClassName(std::move(className)), Definitions(std::move(definitions)),
|
||||
Flags(flags), ObjectType(std::move(objectType)), Behaviours(behaviours) {}
|
||||
|
||||
std::string ObjectType;
|
||||
std::string ClassDef;
|
||||
std::string ClassName;
|
||||
long Flags;
|
||||
std::vector<std::string> Definitions;
|
||||
std::vector<std::tuple<int, std::string>> Behaviours;
|
||||
};
|
||||
|
||||
struct Enum {
|
||||
Enum(std::string name, std::vector<std::tuple<std::string, int>> values)
|
||||
: Name(std::move(name)), Values(std::move(values)) {}
|
||||
std::string Name;
|
||||
std::vector<std::tuple<std::string, int>> Values;
|
||||
};
|
||||
|
||||
std::vector<Type> _types;
|
||||
std::vector<Enum> _enums;
|
||||
std::vector<std::string> _functions;
|
||||
|
||||
public:
|
||||
void StoreType(const std::string& objType, const std::string& classDef, const std::string& className,
|
||||
long flags, const std::vector<std::string>& definitions,
|
||||
const std::vector<std::tuple<int, std::string>>& behaviours) {
|
||||
_types.emplace_back(objType, classDef, className, flags, definitions, behaviours);
|
||||
}
|
||||
|
||||
void StoreEnum(const std::string& name, const std::vector<std::tuple<std::string, int>>& values) {
|
||||
_enums.emplace_back(name, values);
|
||||
}
|
||||
|
||||
void StoreFunc(const std::string& function) { _functions.push_back(function); }
|
||||
|
||||
void Clear() {
|
||||
_types.clear();
|
||||
_enums.clear();
|
||||
_functions.clear();
|
||||
}
|
||||
|
||||
void RegisterTypes(asIScriptEngine* engine) {
|
||||
for (auto& t : _types) {
|
||||
try {
|
||||
auto size = 0;
|
||||
if (t.ObjectType == "valuetype") {
|
||||
size = 1;
|
||||
}
|
||||
engine->RegisterObjectType(t.ClassDef.c_str(), size, t.Flags);
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
}
|
||||
for (auto& t : _enums) {
|
||||
try {
|
||||
engine->RegisterEnum(t.Name.c_str());
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// As opposed to normal functions, behaviours can be called from the builder. If we pass a default value,
|
||||
// this causes a segfault. As such, we have an empty function for them.
|
||||
static void BehaviourPlaceHolder(void*){}
|
||||
|
||||
static void RegisterTypeImplementation(asIScriptEngine* engine, Type& t) {
|
||||
for (auto& def : t.Definitions) {
|
||||
auto i = engine->RegisterObjectMethod(t.ClassName.c_str(), def.c_str(), {}, asCALL_THISCALL);
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto& def : t.Behaviours) {
|
||||
auto call = asCALL_CDECL;
|
||||
if (std::get<0>(def) == 0 || std::get<0>(def) == 2) {
|
||||
call = asCALL_CDECL_OBJLAST;
|
||||
}
|
||||
|
||||
auto i =
|
||||
engine->RegisterObjectBehaviour(t.ClassName.c_str(), static_cast<asEBehaviours>(std::get<0>(def)),
|
||||
std::get<1>(def).c_str(), asFUNCTION(BehaviourPlaceHolder), call);
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void RegisterImplementation(asIScriptEngine* engine) {
|
||||
for (auto& t : _types) {
|
||||
if ((t.Flags & asOBJ_TEMPLATE) == 0) {
|
||||
continue;
|
||||
}
|
||||
RegisterTypeImplementation(engine, t);
|
||||
}
|
||||
for (auto& t : _types) {
|
||||
if ((t.Flags & asOBJ_TEMPLATE) != 0) {
|
||||
continue;
|
||||
}
|
||||
RegisterTypeImplementation(engine, t);
|
||||
}
|
||||
for (auto& t : _enums) {
|
||||
for (auto& v : t.Values) {
|
||||
engine->RegisterEnumValue(t.Name.c_str(), std::get<0>(v).c_str(), std::get<1>(v));
|
||||
}
|
||||
}
|
||||
for (auto& f : _functions) {
|
||||
if (engine->GetGlobalFunctionByDecl(f.c_str()) != nullptr) {
|
||||
continue;
|
||||
}
|
||||
engine->RegisterGlobalFunction(f.c_str(), {}, asCALL_CDECL);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // ASLSP_NATIVE_TYPEDEFRESULT_HPP
|
|
@ -1,3 +1,4 @@
|
|||
#if !TESTS_BUILD
|
||||
#include <napi.h>
|
||||
#include "Database.hpp"
|
||||
|
||||
|
@ -5,4 +6,5 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
|
|||
return Database::Init(env, exports);
|
||||
}
|
||||
|
||||
NODE_API_MODULE(aslsp, InitAll)
|
||||
NODE_API_MODULE(aslsp, InitAll)
|
||||
#endif
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
#if !TESTS_BUILD
|
||||
#include "Database.hpp"
|
||||
#include <sstream>
|
||||
#include "../angelscript/extensions/scriptarray/scriptarray.h"
|
||||
#include "../angelscript/extensions/scriptstdstring/scriptstdstring.h"
|
||||
#include "ASTypeDefParser/Parser.hpp"
|
||||
|
||||
Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
|
||||
Napi::Function func =
|
||||
DefineClass(env, "Database",
|
||||
{InstanceMethod("reset", &Database::Reset), InstanceMethod("loadScript", &Database::LoadScript),
|
||||
InstanceMethod("build", &Database::Build), InstanceMethod("messages", &Database::GetMessages),
|
||||
{InstanceMethod("reset", &Database::Reset), InstanceMethod("loadTypeDef", &Database::LoadTypeDef),
|
||||
InstanceMethod("loadScript", &Database::LoadScript), InstanceMethod("build", &Database::Build),
|
||||
InstanceMethod("messages", &Database::GetMessages),
|
||||
InstanceMethod("setEngineProperty", &Database::SetEngineProperty)});
|
||||
|
||||
auto* constructor = new Napi::FunctionReference();
|
||||
|
@ -21,13 +25,24 @@ void Database::MessageCallback(const asSMessageInfo* msg) {
|
|||
_messages.push_back(new Diagnostic(msg->section, msg->row, msg->col, msg->type, msg->message));
|
||||
}
|
||||
|
||||
Database::Database(const Napi::CallbackInfo& info) : ObjectWrap(info) {
|
||||
void Database::SetupEngine() {
|
||||
_engine = asCreateScriptEngine();
|
||||
_builder = {};
|
||||
_engine->SetMessageCallback(asMETHOD(Database, MessageCallback), this, asCALL_THISCALL);
|
||||
|
||||
_engine->SetEngineProperty(asEP_DISALLOW_EMPTY_LIST_ELEMENTS, true);
|
||||
_engine->SetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE, false);
|
||||
_engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true);
|
||||
_engine->SetEngineProperty(asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT, true);
|
||||
_engine->SetEngineProperty(asEP_AUTO_GARBAGE_COLLECT, false);
|
||||
_engine->SetEngineProperty(asEP_REQUIRE_ENUM_SCOPE, true);
|
||||
_engine->SetEngineProperty(asEP_PROPERTY_ACCESSOR_MODE, 2);
|
||||
_engine->SetEngineProperty(asEP_COMPILER_WARNINGS, 2);
|
||||
RegisterStdString(_engine);
|
||||
RegisterScriptArray(_engine, true);
|
||||
_builder.StartNewModule(_engine, "Module");
|
||||
}
|
||||
|
||||
Database::Database(const Napi::CallbackInfo& info) : ObjectWrap(info) {
|
||||
Reset(info);
|
||||
}
|
||||
|
||||
|
@ -39,8 +54,13 @@ void Database::SetEngineProperty(const Napi::CallbackInfo& info) {
|
|||
}
|
||||
|
||||
void Database::Reset(const Napi::CallbackInfo&) {
|
||||
_engine->DiscardModule("Module");
|
||||
_builder.StartNewModule(_engine, "Module");
|
||||
std::lock_guard<std::mutex> lck(_lock);
|
||||
if (_engine != nullptr){
|
||||
_engine->DiscardModule("Module");
|
||||
_engine->Release();
|
||||
}
|
||||
SetupEngine();
|
||||
_result.Clear();
|
||||
}
|
||||
|
||||
void Database::LoadScript(const Napi::CallbackInfo& info) {
|
||||
|
@ -51,8 +71,21 @@ void Database::LoadScript(const Napi::CallbackInfo& info) {
|
|||
|
||||
_builder.AddSectionFromMemory(name.c_str(), script.c_str());
|
||||
}
|
||||
|
||||
void Database::LoadTypeDef(const Napi::CallbackInfo& info) {
|
||||
if (info.Length() < 2)
|
||||
throw std::logic_error("Not enough arguments");
|
||||
auto name = info[0].As<Napi::String>().Utf8Value();
|
||||
auto script = info[1].As<Napi::String>().Utf8Value();
|
||||
|
||||
ASTypeDefParser::Parser::ParseAndRegister(_result, script);
|
||||
}
|
||||
|
||||
Napi::Value Database::Build(const Napi::CallbackInfo& info) {
|
||||
std::lock_guard<std::mutex> lck(_lock);
|
||||
_messages.clear();
|
||||
_result.RegisterTypes(_engine);
|
||||
_result.RegisterImplementation(_engine);
|
||||
_builder.BuildModule();
|
||||
return Napi::Number::New(info.Env(), 0);
|
||||
}
|
||||
|
@ -70,3 +103,4 @@ Napi::Value Database::GetMessages(const Napi::CallbackInfo& info) {
|
|||
|
||||
return messages;
|
||||
}
|
||||
#endif
|
|
@ -1,17 +1,21 @@
|
|||
#ifndef AS_LSP_NATIVE_DATABASE_HPP
|
||||
#define AS_LSP_NATIVE_DATABASE_HPP
|
||||
#if !TESTS_BUILD
|
||||
#include "../../node_modules/node-addon-api/napi.h"
|
||||
#include "../angelscript/extensions/scriptbuilder/scriptbuilder.h"
|
||||
#include "../angelscript/extensions/scriptstdstring/scriptstdstring.h"
|
||||
#include "../angelscript/include/angelscript.h"
|
||||
#include "ASTypeDefParser/TypeDefResult.hpp"
|
||||
#include "Diagnostic.hpp"
|
||||
|
||||
class Database : public Napi::ObjectWrap<Database> {
|
||||
private:
|
||||
asIScriptEngine* _engine;
|
||||
asIScriptEngine* _engine = nullptr;
|
||||
CScriptBuilder _builder;
|
||||
ASTypeDefParser::TypeDefResult _result;
|
||||
std::vector<const Diagnostic*> _messages;
|
||||
std::mutex _lock;
|
||||
|
||||
void SetupEngine();
|
||||
void MessageCallback(const asSMessageInfo* msg);
|
||||
|
||||
public:
|
||||
|
@ -21,8 +25,10 @@ public:
|
|||
void SetEngineProperty(const Napi::CallbackInfo& info);
|
||||
void Reset(const Napi::CallbackInfo& info);
|
||||
void LoadScript(const Napi::CallbackInfo& info);
|
||||
void LoadTypeDef(const Napi::CallbackInfo& info);
|
||||
Napi::Value Build(const Napi::CallbackInfo& info);
|
||||
Napi::Value GetMessages(const Napi::CallbackInfo& info);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // AS_LSP_NATIVE_DATABASE_HPP
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "../../extern/doctest.hpp"
|
||||
#include "../../src/ASTypeDefParser/Parser.hpp"
|
||||
|
||||
TEST_CASE("Register Empty Type") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, "type foo{}");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
REQUIRE(engine->GetTypeInfoByName("foo") != nullptr);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
void MessageCallback(const asSMessageInfo *msg, void *param)
|
||||
{
|
||||
const char *type = "ERR ";
|
||||
if( msg->type == asMSGTYPE_WARNING )
|
||||
type = "WARN";
|
||||
else if( msg->type == asMSGTYPE_INFORMATION )
|
||||
type = "INFO";
|
||||
printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
|
||||
}
|
||||
|
||||
TEST_CASE("Register Empty Value Type") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, "valuetype foo{}");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
REQUIRE(engine->GetTypeInfoByName("foo") != nullptr);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Empty Type With Semicolon") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, "type foo;");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
REQUIRE(engine->GetTypeInfoByName("foo") != nullptr);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Multiple Empty Types") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, "type foo{} type bar{}");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
REQUIRE(engine->GetTypeInfoByName("foo") != nullptr);
|
||||
REQUIRE(engine->GetTypeInfoByName("bar") != nullptr);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Method") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar(int a, int b);
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(method->GetParamCount(), 2);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "int Bar(int a, int b)");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Multiple Methods") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar(int a, int b);
|
||||
int Zet(int a, int b);
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(method->GetParamCount(), 2);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "int Bar(int a, int b)");
|
||||
method = type->GetMethodByName("Zet");
|
||||
REQUIRE(method != nullptr);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Generic Type With Method") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo<class T> {
|
||||
T@ Bar(int a, int b);
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE(type == engine->GetTypeInfoByDecl("foo<T>"));
|
||||
auto method = type->GetMethodByName("Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(method->GetParamCount(), 2);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "T@ Bar(int a, int b)");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Generic Type With Multiple implementations") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo<class T> {
|
||||
T@ Bar(int a, int b);
|
||||
}
|
||||
|
||||
type a {
|
||||
foo<int> GetFoo();
|
||||
}
|
||||
|
||||
type b {
|
||||
foo<int> GetBar();
|
||||
}
|
||||
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE(type == engine->GetTypeInfoByDecl("foo<T>"));
|
||||
auto method = type->GetMethodByName("Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(method->GetParamCount(), 2);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "T@ Bar(int a, int b)");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Register Type With Get Property") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar { get; };
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("get_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "int get_Bar()");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Get Const Property") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar { get const; };
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("get_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "int get_Bar() const");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Set Property") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar { set; };
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("set_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "void set_Bar(int value)");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Set Const Property") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar { set const; };
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("set_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "void set_Bar(int value) const");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Set Property") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar { set; };
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("set_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "void set_Bar(int value)");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Getter and Setter Property") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar { get; set; };
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("get_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "int get_Bar()");
|
||||
method = type->GetMethodByName("set_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "void set_Bar(int value)");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Multiple Getter and Setter Properties") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type foo {
|
||||
int Bar { get; set; };
|
||||
int Zet { get; set; };
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
auto method = type->GetMethodByName("get_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "int get_Bar()");
|
||||
method = type->GetMethodByName("set_Bar");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "void set_Bar(int value)");
|
||||
method = type->GetMethodByName("get_Zet");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "int get_Zet()");
|
||||
method = type->GetMethodByName("set_Zet");
|
||||
REQUIRE(method != nullptr);
|
||||
REQUIRE_EQ(std::string(method->GetDeclaration(false, false, true)), "void set_Zet(int value)");
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Empty Enum") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
enum foo {
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE(type->GetTypeId() % asOBJ_ENUM != 0);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Enum with value") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
enum foo {
|
||||
One
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE(type->GetTypeId() % asOBJ_ENUM != 0);
|
||||
int i = -1;
|
||||
REQUIRE_EQ(std::string(type->GetEnumValueByIndex(0, &i)), "One");
|
||||
REQUIRE_EQ(i, 0);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Enum with multiple values") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
enum foo {
|
||||
One,
|
||||
Two
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE(type->GetTypeId() % asOBJ_ENUM != 0);
|
||||
int i = -1;
|
||||
REQUIRE_EQ(std::string(type->GetEnumValueByIndex(0, &i)), "One");
|
||||
REQUIRE_EQ(i, 0);
|
||||
REQUIRE_EQ(std::string(type->GetEnumValueByIndex(1, &i)), "Two");
|
||||
REQUIRE_EQ(i, 1);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Enum with explicit value") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
enum foo {
|
||||
One = 5
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE(type->GetTypeId() % asOBJ_ENUM != 0);
|
||||
int i = -1;
|
||||
REQUIRE_EQ(std::string(type->GetEnumValueByIndex(0, &i)), "One");
|
||||
REQUIRE_EQ(i, 5);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Enum with multiple explicit values") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
enum foo {
|
||||
One = 5,
|
||||
Two = 1,
|
||||
Three = -1000,
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("foo");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE(type->GetTypeId() % asOBJ_ENUM != 0);
|
||||
int i = -1;
|
||||
REQUIRE_EQ(std::string(type->GetEnumValueByIndex(0, &i)), "One");
|
||||
REQUIRE_EQ(i, 5);
|
||||
REQUIRE_EQ(std::string(type->GetEnumValueByIndex(1, &i)), "Two");
|
||||
REQUIRE_EQ(i, 1);
|
||||
REQUIRE_EQ(std::string(type->GetEnumValueByIndex(2, &i)), "Three");
|
||||
REQUIRE_EQ(i, -1000);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Global Function") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
func void print(int i);
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto func = engine->GetGlobalFunctionByDecl("void print(int i)");
|
||||
REQUIRE(func != nullptr);
|
||||
engine->Release();
|
||||
}
|
||||
|
||||
TEST_CASE("Register Type With Behaviour") {
|
||||
asIScriptEngine* engine = asCreateScriptEngine();
|
||||
engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);
|
||||
|
||||
ASTypeDefParser::TypeDefResult res;
|
||||
ASTypeDefParser::Parser::ParseAndRegister(res, R"(
|
||||
type fooClass {
|
||||
behave 4 fooClass@ f(int &in) {repeat int};
|
||||
}
|
||||
)");
|
||||
res.RegisterTypes(engine);
|
||||
res.RegisterImplementation(engine);
|
||||
auto type = engine->GetTypeInfoByName("fooClass");
|
||||
REQUIRE(type != nullptr);
|
||||
REQUIRE_EQ(type->GetBehaviourCount(), 1);
|
||||
asEBehaviours b;
|
||||
REQUIRE_EQ(std::string(type->GetBehaviourByIndex(0, &b)->GetDeclaration()), "fooClass@ $list(int&in) { repeat int }");
|
||||
|
||||
engine->Release();
|
||||
}
|
|
@ -51,6 +51,7 @@ export interface Message {
|
|||
export interface ScriptDatabase {
|
||||
setEngineProperty(property: ASEngineProperty, value: number): void;
|
||||
reset(): void;
|
||||
loadTypeDef(name: string, script: string): void;
|
||||
loadScript(name: string, script: string): void;
|
||||
build(): number;
|
||||
messages(): Message[];
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>comment</key>
|
||||
<string>Made by Deukhoofd</string>
|
||||
<key>fileTypes</key>
|
||||
<array>
|
||||
<string>as</string>
|
||||
</array>
|
||||
<key>firstLineMatch</key>
|
||||
<string>-\*- C\+\+ -\*-</string>
|
||||
<key>foldingStartMarker</key>
|
||||
<string>(?x)
|
||||
/\*\*(?!\*)
|
||||
|^(?![^{]*?//|[^{]*?/\*(?!.*?\*/.*?\{)).*?\{\s*($|//|/\*(?!.*?\*/.*\S))
|
||||
</string>
|
||||
<key>foldingStopMarker</key>
|
||||
<string>(?<!\*)\*\*/|^\s*\}</string>
|
||||
<key>keyEquivalent</key>
|
||||
<string>^~C</string>
|
||||
<key>name</key>
|
||||
<string>AngelScript</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<!-- <dict>
|
||||
<key>include</key>
|
||||
<string>source.c</string>
|
||||
</dict> -->
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.comment.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>(//).*$\n?</string>
|
||||
<key>name</key>
|
||||
<string>comment.line.double-slash.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>/\*</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.comment.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>\*/</string>
|
||||
<key>name</key>
|
||||
<string>comment.block.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"""</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>"""</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>"</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>"</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.angelscript</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escape.angelscript</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string>'</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>'</string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.single.angelscript</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escape.angelscript</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>(~|!|&&|\|\|)</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.logical.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>[-!%&><@*+=/?:]</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.symbolic.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(for|in|break|continue|while|do|return|if|else|case|switch|namespace)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(is|cast)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(or|and|xor|not)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.logical.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(get|in|inout|out|override|set|private|public|const|default|final|shared|mixin)\b</string>
|
||||
<key>name</key>
|
||||
<string>storage.modifier.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(type|enum|func|void|bool|int|int8|int16|int32|int64|uint|uint8|uint16|uint32|uint64|string|ref|array|double|float|auto|dictionary)\b</string>
|
||||
<key>name</key>
|
||||
<string>storage.type.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>[A-Za-z][A-Za-z0-9]+@</string>
|
||||
<key>name</key>
|
||||
<string>storage.type.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(null|true|false)\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.language.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(this|super)\b</string>
|
||||
<key>name</key>
|
||||
<string>variable.language.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(import|from)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.import.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.numeric.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>^\s*\#([a-zA-Z_0-9]*)?</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.import.angelscript</string>
|
||||
</dict>
|
||||
<!-- This became too much. -->
|
||||
<!--
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>(@[a-zA-Z_]+[0-9a-zA-Z_]*)</string>
|
||||
<key>name</key>
|
||||
<string>variable.other.pointer.angelscript</string>
|
||||
</dict>
|
||||
-->
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>markup.heading.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>^\s*\[(.*)\]\s*?</string>
|
||||
<key>name</key>
|
||||
<string>meta.metadata.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\.[a-zA-Z_][a-zA-Z_0-9]*\b(?!\s*\()</string>
|
||||
<key>name</key>
|
||||
<string>variable.other.dot-access.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>storage.type.class.angelscript</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.name.type.class.angelscript</string>
|
||||
</dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.other.inherited-class.angelscript</string>
|
||||
</dict>
|
||||
<key>5</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.other.inherited-class.angelscript</string>
|
||||
</dict>
|
||||
<key>7</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.other.inherited-class.angelscript</string>
|
||||
</dict>
|
||||
<key>9</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.other.inherited-class.angelscript</string>
|
||||
</dict>
|
||||
<key>11</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.other.inherited-class.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>\b(class|interface)\s+([a-zA-Z_0-9]*)(?:\s*:\s*([a-zA-Z_0-9]*)(\s*,\s*([a-zA-Z_0-9]*))?(\s*,\s*([a-zA-Z_0-9]*))?(\s*,\s*([a-zA-Z_0-9]*))?(\s*,\s*([a-zA-Z_0-9]*))?)?</string>
|
||||
<key>name</key>
|
||||
<string>meta.class.angelscript</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>meta.function-call.angelscript</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>(\b|\.)([a-zA-Z_][a-zA-Z_0-9]*)\b(\s*\()</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b([A-Z][A-Z0-9_]+)\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.other.angelscript</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>source.angelscript</string>
|
||||
<key>uuid</key>
|
||||
<string>69E25C06-FA48-4207-BF35-11353888A8F6</string>
|
||||
</dict>
|
||||
</plist>
|
Loading…
Reference in New Issue