♻️ 重构`tree-sitter-tsf`

This commit is contained in:
csh 2025-11-11 16:54:48 +08:00
parent 5eba41429d
commit 7e0b69c354
37 changed files with 1117419 additions and 376895 deletions

View File

@ -0,0 +1,306 @@
# 完整的 TSLX 块匹配规则
## 基本原则
- 在 `<?tslx>` 之后进入 `tslx_block`,默认所有内容都是文本(`tslx_content`
- Scanner 通过 lookahead 判断 `<?tsl``<?=` 的行为
---
## 匹配规则
### 规则 1配对的 `<?tsl ... ?>`
**条件**:遇到 `<?tsl` 且能找到对应的 `?>`
**行为**:匹配为 `tsl_statement_block`
**Token 序列**`tsl_statement_start_tag` → `statement...``tsl_statement_end_tag`
**结果**`tslx_block` 继续
---
### 规则 2配对的 `<?= ... ?>`
**条件**:遇到 `<?=` 且能找到对应的 `?>`
**行为**:匹配为 `tsl_expression_block`
**Token 序列**`tsl_expression_start_tag` → `expression``tsl_expression_end_tag`
**结果**`tslx_block` 继续
---
### 规则 3未配对的 `<?tsl`(关键特性)
**条件**:遇到 `<?tsl` 但找不到对应的 `?>`
**行为**
- Scanner 通过 lookahead 判断后面没有 `?>`
- **`<?tsl` 本身被识别为 `tslx_end_tag`**(不是生成额外的 token
- `tslx_block` 结束
- Parser 跳出 `tslx_block`,继续解析后续内容(如 `a := 1;`
**Token 序列**
```
tslx_end_tag: "<?tsl" ← 这个 token 的文本内容就是 "<?tsl"
```
**结果**`tslx_block` 结束,后续内容由 `root` 的其他规则处理
---
### 规则 4未配对的 `<?=`(错误情况)
**条件**:遇到 `<?=` 但找不到对应的 `?>`
**行为**
- Scanner 返回 `tsl_expression_start_tag`
- Parser 期望 `expression``tsl_expression_end_tag`
- 找不到 `?>` 导致**语法错误**
**结果**Parser 报错
---
### 规则 5到达 EOF
**条件**:到达文件末尾,且之前没有遇到未配对的 `<?tsl`
**行为**:在 EOF 位置生成 `tslx_end_tag`
**结果**`tslx_block` 正常结束
---
## Scanner Lookahead 策略
Scanner 需要判断 `<?tsl``<?=` 后面是否有匹配的 `?>`
### 推荐策略(需选择其一):
**策略 A扫描到换行或分号**
```
<?tsl ← 向前扫描
a := 1; ← 遇到 ; 前没有 ?>,判定为未配对
```
- 优点:快速判断,符合单行语句的直觉
- 缺点:多行语句可能误判
**策略 B扫描到下一个 `<?` 或 EOF**
```
<?tsl ← 向前扫描
a := 1;
<?tsl ... ← 遇到下一个 <? 前没有 ?>,判定为未配对
```
- 优点:支持多行语句
- 缺点:扫描距离可能较长
**策略 C扫描固定距离如 1000 字符)**
```
<?tsl ← 向前看 N 个字符
... ← 如果在范围内找不到 ?>,判定为未配对
```
- 优点:性能可控
- 缺点:可能误判超长的配对块
**当前实现**_[填入你选择的策略]_
---
## 示例解析
### 示例 1未配对的 `<?tsl`
#### 输入:
```typescript
aaaa
<?tsl
a := 1;
```
#### Token 序列:
```
1. tslx_tag "<?tslx>"
2. tslx_content "\naaaa\n"
3. tslx_end_tag "<?tsl" ← <?tsl 本身就是这个 token
4. identifier "a"
5. := ":="
6. number "1"
7. ; ";"
```
#### AST 结构:
```
root
├── tslx_block
│ ├── tslx_tag: "<?tslx>"
│ ├── tslx_content: "\naaaa\n"
│ └── tslx_end_tag: "<?tsl"
└── var_declaration
├── name: "a"
└── value: 1
```
#### 说明:
- `<?tsl` 触发 `tslx_block` 结束
- `a := 1;``tslx_block` **外部**,由 `root``var_declaration` 匹配
---
### 示例 2全部配对 + EOF
#### 输入:
```typescript
aaaa
bbb
```
#### Token 序列:
```
1. tslx_tag "<?tslx>"
2. tslx_content "\naaaa\n"
3. tsl_statement_start_tag "<?tsl"
4. identifier "echo"
5. number "1"
6. ; ";"
7. tsl_statement_end_tag "?>"
8. tslx_content "\nbbb\n"
9. tslx_end_tag "" ← EOF 位置生成
```
#### AST 结构:
```
root
└── tslx_block
├── tslx_tag: "<?tslx>"
├── tslx_content: "\naaaa\n"
├── tsl_statement_block
│ └── expression_statement
│ └── call_expression: echo(1)
├── tslx_content: "\nbbb\n"
└── tslx_end_tag: (EOF)
```
---
### 示例 3未配对的 `<?=`(错误)
#### 输入:
```typescript
<?=
a + 1
```
#### Token 序列:
```
1. tslx_tag "<?tslx>"
2. tslx_content "\n"
3. tsl_expression_start_tag "<?="
4. identifier "a"
5. + "+"
6. number "1"
[ERROR] 期望 tsl_expression_end_tag (?>),但遇到 EOF
```
#### 结果:
**语法错误**`<?=` 必须有匹配的 `?>`
---
### 示例 4混合使用
#### 输入:
```typescript
text1
text2
<?= 1 + 1 ?>
text3
<?tsl
var x := 1;
```
#### Token 序列:
```
1. tslx_tag "<?tslx>"
2. tslx_content "\ntext1\n"
3. tsl_statement_start_tag "<?tsl"
4. ... (echo "hello")
5. tsl_statement_end_tag "?>"
6. tslx_content "\ntext2\n"
7. tsl_expression_start_tag "<?="
8. ... (1 + 1)
9. tsl_expression_end_tag "?>"
10. tslx_content "\ntext3\n"
11. tslx_end_tag "<?tsl" ← 未配对,结束 tslx_block
12. identifier "var"
13. ... (x := 1)
```
#### AST 结构:
```
root
├── tslx_block
│ ├── tslx_tag
│ ├── tslx_content: "\ntext1\n"
│ ├── tsl_statement_block: echo("hello")
│ ├── tslx_content: "\ntext2\n"
│ ├── tsl_expression_block: 1 + 1
│ ├── tslx_content: "\ntext3\n"
│ └── tslx_end_tag: "<?tsl"
└── var_declaration: x := 1
```
---
## Grammar.js 修改建议
### 当前定义:
```javascript
tslx_block: ($) =>
prec.right(
seq(
$.tslx_tag,
repeat(
choice(
$.tslx_content,
$._tsl_statement_block,
$._tsl_expression_block,
$.tslx_end_tag // ❌ 问题:允许 end_tag 后继续匹配
),
),
),
),
```
### 建议修改:
```javascript
tslx_block: ($) =>
prec.right(
seq(
$.tslx_tag,
repeat(
choice(
$.tslx_content,
$._tsl_statement_block,
$._tsl_expression_block,
),
),
$.tslx_end_tag // ✅ 移到 repeat 外面,作为结束标记
),
),
```
**理由**
- `tslx_end_tag``tslx_block` 的**终结符**,不应该在循环体内
- 这样确保 `tslx_end_tag` 后不会再匹配 `tslx_content` 等内容
---
## 核心要点总结
1. ✅ `<?tsl` 有**双重身份**
- 有 `?>``tsl_statement_start_tag`
- 无 `?>``tslx_end_tag`
2. ✅ `<?=` **必须配对**,否则语法错误
3. ✅ `<?tsl` 作为 `tslx_end_tag` 时,其文本内容就是 `"<?tsl"`
4. ✅ Scanner 通过 **lookahead** 判断是否有匹配的 `?>`
5. ✅ `tslx_end_tag` 在 Grammar 中应该是 `tslx_block` 的**终结符**
6. ✅ EOF 时自动生成 `tslx_end_tag` 来正常结束 `tslx_block`

View File

@ -0,0 +1,30 @@
{
"targets": [
{
"target_name": "tree-sitter-tsf_binding",
"dependencies": [
"<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except"
],
"include_dirs": [
"src"
],
"sources": [
"bindings/node/binding.cc",
"src/parser.c",
"src/scanner.c"
],
"conditions": [
["OS!='win'", {
"cflags_c": [
"-std=c11"
]
}, {
"cflags_c": [
"/std:c11",
"/utf-8"
]
}]
]
}
]
}

View File

@ -0,0 +1,15 @@
#include <napi.h>
typedef struct TSLanguage TSLanguage;
extern "C" TSLanguage *tree_sitter_tsf();
// "tree_sitter_your_language_binding" is the symbol that Node.js looks for.
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports["name"] = Napi::String::New(env, "tsf");
auto language = Napi::External<TSLanguage>::New(env, tree_sitter_tsf());
exports["language"] = language;
return exports;
}
NODE_API_MODULE(tree_sitter_tsf_binding, Init)

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,437 @@
#include <wctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "tree_sitter/parser.h"
/* #define DEBUG 1 */
#if DEBUG
#define LOG(...) fprintf(stderr, "[SCANNER] " __VA_ARGS__)
#else
#define LOG(...)
#endif
enum TokenType
{
TSLX_CONTENT,
TSL_STATEMENT_START_TAG,
TSL_STATEMENT_END_TAG,
TSL_EXPRESSION_START_TAG,
TSL_EXPRESSION_END_TAG,
TSLX_END_TAG
};
enum State
{
STATE_ROOT,
STATE_TSLX,
STATE_TSL_STATEMENT,
STATE_TSL_EXPRESSION
};
typedef struct
{
enum State* states;
size_t capacity;
size_t size;
} StateStack;
static void stack_init(StateStack* stack)
{
stack->capacity = 8;
stack->size = 0;
stack->states = malloc(stack->capacity * sizeof(enum State));
if (stack->states)
{
stack->states[0] = STATE_ROOT;
stack->size = 1;
}
}
static void stack_free(StateStack* stack)
{
if (stack->states)
{
free(stack->states);
stack->states = NULL;
}
stack->size = 0;
stack->capacity = 0;
}
static void stack_push(StateStack* stack, enum State state)
{
if (stack->size >= stack->capacity)
{
stack->capacity *= 2;
stack->states = realloc(stack->states, stack->capacity * sizeof(enum State));
}
if (stack->states)
{
stack->states[stack->size++] = state;
}
}
static enum State stack_pop(StateStack* stack)
{
if (stack->size > 1)
{
return stack->states[--stack->size];
}
return STATE_ROOT;
}
static enum State stack_top(const StateStack* stack)
{
return stack->size > 0 ? stack->states[stack->size - 1] : STATE_ROOT;
}
void* tree_sitter_tsf_external_scanner_create()
{
StateStack* stack = malloc(sizeof(StateStack));
if (stack)
{
stack_init(stack);
}
LOG("Scanner created\n");
return stack;
}
void tree_sitter_tsf_external_scanner_destroy(void* payload)
{
StateStack* stack = (StateStack*)payload;
if (stack)
{
stack_free(stack);
free(stack);
}
LOG("Scanner destroyed\n");
}
unsigned tree_sitter_tsf_external_scanner_serialize(void* payload, char* buffer)
{
StateStack* stack = (StateStack*)payload;
if (!stack || stack->size == 0)
return 0;
size_t bytes_to_copy = stack->size * sizeof(enum State);
if (bytes_to_copy > TREE_SITTER_SERIALIZATION_BUFFER_SIZE)
{
bytes_to_copy = TREE_SITTER_SERIALIZATION_BUFFER_SIZE;
}
memcpy(buffer, stack->states, bytes_to_copy);
return bytes_to_copy;
}
void tree_sitter_tsf_external_scanner_deserialize(void* payload, const char* buffer, unsigned length)
{
StateStack* stack = (StateStack*)payload;
if (!stack)
return;
stack_free(stack);
stack_init(stack);
if (length > 0)
{
size_t count = length / sizeof(enum State);
for (size_t i = 0; i < count && i < stack->capacity; i++)
{
enum State state;
memcpy(&state, buffer + i * sizeof(enum State), sizeof(enum State));
if (i == 0)
{
stack->states[0] = state;
}
else
{
stack_push(stack, state);
}
}
}
}
static void skip_whitespace(TSLexer* lexer)
{
while (iswspace(lexer->lookahead))
{
lexer->advance(lexer, true);
}
}
static bool lookahead_for_close_tag(TSLexer* lexer)
{
LOG(" [lookahead_for_close_tag] Starting lookahead\n");
while (lexer->lookahead != '\0')
{
if (lexer->lookahead == '?')
{
lexer->advance(lexer, false);
if (lexer->lookahead == '>')
{
LOG(" [lookahead_for_close_tag] Found ?>, returning true\n");
return true;
}
}
else if (lexer->lookahead == '<')
{
lexer->advance(lexer, false);
if (lexer->lookahead == '?')
{
LOG(" [lookahead_for_close_tag] Found next <?, returning false\n");
return false;
}
}
else
{
lexer->advance(lexer, false);
}
}
LOG(" [lookahead_for_close_tag] Reached EOF, returning false\n");
return false;
}
static int peek_special_tag(TSLexer* lexer)
{
if (lexer->lookahead != '<')
return 0;
lexer->advance(lexer, false);
if (lexer->lookahead != '?')
return 0;
lexer->advance(lexer, false);
if (lexer->lookahead == '=')
{
LOG(" [peek_special_tag] Found <?=\n");
return 1;
}
if (towlower(lexer->lookahead) == 't')
{
lexer->advance(lexer, false);
if (towlower(lexer->lookahead) == 's')
{
lexer->advance(lexer, false);
if (towlower(lexer->lookahead) == 'l')
{
lexer->advance(lexer, false);
if (!iswalnum(lexer->lookahead) && lexer->lookahead != '_')
{
LOG(" [peek_special_tag] Found <?tsl\n");
return 2;
}
}
}
}
return 0;
}
static bool scan_tslx_content(TSLexer* lexer)
{
bool has_content = false;
LOG(" [scan_tslx_content] Starting scan\n");
while (lexer->lookahead != '\0')
{
if (lexer->lookahead == '<')
{
lexer->mark_end(lexer);
int tag_type = peek_special_tag(lexer);
if (tag_type > 0)
{
LOG(" [scan_tslx_content] Found special tag, stopping. has_content=%d\n", has_content);
return has_content;
}
has_content = true;
}
else
{
lexer->advance(lexer, false);
has_content = true;
}
}
LOG(" [scan_tslx_content] Reached EOF, has_content=%d\n", has_content);
return has_content;
}
bool tree_sitter_tsf_external_scanner_scan(void* payload, TSLexer* lexer, const bool* valid_symbols)
{
StateStack* stack = (StateStack*)payload;
if (!stack)
{
LOG("ERROR: stack is NULL\n");
return false;
}
enum State current_state = stack_top(stack);
LOG("\n=== SCAN START ===\n");
LOG("Current state: %d (0=ROOT, 1=TSLX, 2=TSL_STATEMENT, 3=TSL_EXPRESSION)\n", current_state);
LOG("Current char: '%c' (0x%02x)\n", lexer->lookahead, lexer->lookahead);
LOG("Valid symbols: CONTENT=%d, TSL_START=%d, TSL_END=%d, EXPR_START=%d, EXPR_END=%d, TSLX_END=%d\n",
valid_symbols[TSLX_CONTENT],
valid_symbols[TSL_STATEMENT_START_TAG],
valid_symbols[TSL_STATEMENT_END_TAG],
valid_symbols[TSL_EXPRESSION_START_TAG],
valid_symbols[TSL_EXPRESSION_END_TAG],
valid_symbols[TSLX_END_TAG]);
// ⚠️ 关键修复:通过 valid_symbols 推断状态切换
// 如果当前在 ROOT 状态,但 parser 期望 TSLX 内部的 token说明刚匹配完 <?tslx>
if (current_state == STATE_ROOT &&
(valid_symbols[TSLX_CONTENT] ||
valid_symbols[TSL_STATEMENT_START_TAG] ||
valid_symbols[TSL_EXPRESSION_START_TAG] ||
valid_symbols[TSLX_END_TAG]))
{
LOG("State transition: ROOT -> TSLX (inferred from valid_symbols)\n");
stack_push(stack, STATE_TSLX);
current_state = STATE_TSLX;
}
if (current_state == STATE_TSL_STATEMENT || current_state == STATE_TSL_EXPRESSION)
{
skip_whitespace(lexer);
LOG("After skip whitespace: '%c' (0x%02x)\n", lexer->lookahead, lexer->lookahead);
}
if (current_state == STATE_TSLX)
{
LOG("In STATE_TSLX\n");
if (lexer->lookahead == '\0' && valid_symbols[TSLX_END_TAG])
{
LOG("Found EOF, returning TSLX_END_TAG\n");
lexer->mark_end(lexer);
lexer->result_symbol = TSLX_END_TAG;
stack_pop(stack);
return true;
}
if (lexer->lookahead == '<')
{
LOG("Found '<', checking for tag\n");
lexer->advance(lexer, false);
if (lexer->lookahead == '?')
{
LOG("Found '<?', checking tag type\n");
lexer->advance(lexer, false);
if (lexer->lookahead == '=' && valid_symbols[TSL_EXPRESSION_START_TAG])
{
LOG("Found '<?=', returning TSL_EXPRESSION_START_TAG\n");
lexer->advance(lexer, false);
lexer->mark_end(lexer);
lexer->result_symbol = TSL_EXPRESSION_START_TAG;
stack_push(stack, STATE_TSL_EXPRESSION);
return true;
}
if (towlower(lexer->lookahead) == 't')
{
LOG("Found 't', checking for 'tsl'\n");
lexer->advance(lexer, false);
if (towlower(lexer->lookahead) == 's')
{
lexer->advance(lexer, false);
if (towlower(lexer->lookahead) == 'l')
{
lexer->advance(lexer, false);
LOG("Found 'tsl', next char: '%c' (0x%02x)\n", lexer->lookahead, lexer->lookahead);
if (!iswalnum(lexer->lookahead) && lexer->lookahead != '_')
{
LOG("Valid <?tsl delimiter, performing lookahead\n");
lexer->mark_end(lexer);
bool has_close_tag = lookahead_for_close_tag(lexer);
LOG("Lookahead result: has_close_tag = %d\n", has_close_tag);
if (has_close_tag && valid_symbols[TSL_STATEMENT_START_TAG])
{
LOG("Returning TSL_STATEMENT_START_TAG\n");
lexer->result_symbol = TSL_STATEMENT_START_TAG;
stack_push(stack, STATE_TSL_STATEMENT);
return true;
}
else if (!has_close_tag && valid_symbols[TSLX_END_TAG])
{
LOG("Returning TSLX_END_TAG (<?tsl without ?>)\n");
lexer->result_symbol = TSLX_END_TAG;
stack_pop(stack);
return true;
}
}
}
}
}
}
}
if (valid_symbols[TSLX_CONTENT])
{
LOG("Trying to scan TSLX_CONTENT\n");
if (scan_tslx_content(lexer))
{
LOG("Successfully scanned TSLX_CONTENT\n");
lexer->result_symbol = TSLX_CONTENT;
return true;
}
LOG("Failed to scan TSLX_CONTENT\n");
}
}
if (current_state == STATE_TSL_STATEMENT)
{
LOG("In STATE_TSL_STATEMENT\n");
if (lexer->lookahead == '?' && valid_symbols[TSL_STATEMENT_END_TAG])
{
lexer->advance(lexer, false);
if (lexer->lookahead == '>')
{
LOG("Found '?>', returning TSL_STATEMENT_END_TAG\n");
lexer->advance(lexer, false);
lexer->mark_end(lexer);
lexer->result_symbol = TSL_STATEMENT_END_TAG;
stack_pop(stack);
return true;
}
}
}
if (current_state == STATE_TSL_EXPRESSION)
{
LOG("In STATE_TSL_EXPRESSION\n");
if (lexer->lookahead == '?' && valid_symbols[TSL_EXPRESSION_END_TAG])
{
lexer->advance(lexer, false);
if (lexer->lookahead == '>')
{
LOG("Found '?>', returning TSL_EXPRESSION_END_TAG\n");
lexer->advance(lexer, false);
lexer->mark_end(lexer);
lexer->result_symbol = TSL_EXPRESSION_END_TAG;
stack_pop(stack);
return true;
}
}
}
LOG("Returning false (no match)\n");
return false;
}

View File

@ -0,0 +1,725 @@
type IDS_AuditExpr = class
class function FormulaeCount(data, precision);
class function FormulaeConvert(v);
class function GetReportInfo(data);
class function analysisFormulae(Formulae); //解析勾稽表达式
class function specialStrReplace(str, r_type); //针对部分特殊字符进行临时转换 r_type: 1. 转化为特殊串 2. 转化为原字符串
class function encryptFormulae(s); //base64编码
class function decryptFormulae(s); //base64解码
class function XBRLInsert(Formulae, FundType, Classify, isencrypt); //勾稽条目插入
end;
class function IDS_AuditExpr.FormulaeCount(data, precision);
begin
if not precision then
precision := 0;
if ansipos("#替换文本#", data) then // 将替换标志去除
data := ansireplaceText(data, "#替换文本#", "");
sysparams["LResult"] := "";
sysparams["RResult"] := "";
sysparams["chaE"] := "";
fStr := "##0"$(precision>0?"."$DupeString('0', precision):"");
data := ansireplaceText(data, "≠", "<>"); // eval不支持 ≠ , 需要转为<> 才能进行计算
WCZ := GetSysParam("WuChaZhi");
if data like "\\) or \\(" or data like ":true" or data like "nil" or data like ' in ' or data like ' without ' or data like "\\) and \\("
or data like '<>' or data like "funcOfIf" or data like "ifThen" or data like "isNull" or data like " and "
or ansipos("公募基金代码", data) or ansipos("公募基金简称(", data) or ansipos("公募基金全称", data) or ansipos("textMatch", data) or ansipos("getMonthSpan", data)
or ansipos("fundInfo", data) or ansipos(" or ", data) or ansipos("基金存续起始日", data) then
begin
//修复 XXX=nil xxx<>nil xxx为字符串时eval执行报错问题 -by fangf
if data like '<>nil' and not (data like "\\) and \\(") then
begin
if str2array(data,"<>")[0]='0' or ifstring(str2array(data,"<>")[0]) then
begin
if str2array(data,"<>")[0]='nil' then data:='nil<>nil';
else if data like "\\) or \\(" then //修复(XXX<>nil) or (XXX<>nil) 勾稽不生效问题 ex-wanzl
begin
data:= replaceText(data, ")", "");
data:= replaceText(data, "(", "");
end
else
data:="'"$tostring(str2array(data,"<>")[0])$"'"$'<>nil';
end
end
if data like '=nil' and not (data like "\\) and \\(") and not (data like "\\) or \\(") then
begin
if str2array(data,"=")[0]='0' or ifstring(str2array(data,"=")[0]) then
begin
if str2array(data,"=")[0]='nil' then data:='nil=nil';
else
data:="'"$tostring(str2array(data,"=")[0])$"'"$'=nil';
end
end
//用于处理<> 条件下左右两边都不等于nil的情况下勾稽不到的情况 -by fangf
if data like '<>' and not (data like "\\) and \\(") and not (data like "\\) or \\(") and not (data like "ifThen") then
begin
if not ( str2array(data,"<>")[0] like 'nil') and not (str2array(data,"<>")[1] like 'nil') then
begin
try
StrToCurr(str2array(data,"<>")[0]);
except
LSTR:=1;
end;
try
StrToCurr(str2array(data,"<>")[1]);
except
RSTR:=1;
end;
IF LSTR AND RSTR THEN
begin
if not (str2array(data,"<>")[0]=str2array(data,"<>")[1]) then
return 1;
return data;
end
end
end
if data like "ifThen" then
begin
try
paramList := ansireplaceText(ansireplaceText(data, "ifThen(", ""), ")", "");
paramList := str2array(paramList, ",");
for paramNum:=0 to length(paramList)-1 do
begin
if ParseRegExpr("^([\\d\\.+\\-\\*\\/\\)\\(]+)([=<>]{1,2})([\\d\\.+\\-\\*\\/\\)\\(]+)$",trim(paramList[paramNum]),"",res,MPos,Mlen)=1 then
begin
leftS := eval(&res[0][1]);
mark := res[0][2];
rightS := eval(&res[0][3]);
replaceS := leftS$mark$rightS;
data := replaceText(data, paramList[paramNum] ,replaceS);
end
else
continue;
end;
except
end;
end;
if data like "without" then //用于处理集合1不存在于集合2 ex-wanzl
begin
data := replaceText(data, "array", "");
tmp := replaceText(replaceText(data, ")", ""), "(", "");
tmp := str2array(tmp, " without ");
if length(tmp)=2 then
begin
tmp1 := str2array(tmp[0], ",");
tmp2 := str2array(tmp[1], ",");
arr := array();
for i := 0 to length(tmp1) do
begin
if tmp1[i] in tmp2 then
arr[length(arr)] := tmp1[i] ;
end
if istable(arr) then
begin
tmp3 := "【不符合项:"$array2str(DistinctStr(arr), ",")$"】" ;
return replaceText(data, "array", "")$tmp3;
end
else return 1;
end
else
tmp2 := "";
return replaceText(data, "array", "")$tmp2;
end
else if eval(&data)=1 then
return 1;
else if data like " in " then //用于处理集合函数公式
begin
data := replaceText(data, "array", "");
tmp2 := "以下项目存在差异,请检查。【";
dataRe := str2array(data, " and ");
for j:=0 to length(dataRe)-1 do
begin
tmp := replaceText(replaceText(dataRe[j], ")", ""), "(", "");
tmp := str2array(tmp, " in ");
if length(tmp)=2 then
begin
chayi := str2array(tmp[0], ",") minus str2array(tmp[1], ",");
if istable(chayi) then
tmp2 += (j=0?"":";")$array2str(chayi, ",");
end else
tmp2 += "";
end;
tmp2 += "】";
data := replaceText(data, "in", "与");
return tmp2;
end
else if data like "nil" and not (data like "\\) and \\(") then
begin
if (data like '<>' or data like '=') and (str2array(data,"<>")[0]='nil' or str2array(data,"<>")[0]=nil or str2array(data,"<>")[1]=nil or str2array(data,"<>")[1]='nil') then
return data$"【nil】";
return data$"【"$FormatFloat(fStr,setPrecision(eval(&str2array(data,"=")[1]),precision))$"】";
end
else
return data$"【"$eval(&data)$"】";
end;
ParseRegExpr("([<|>|=])+",data, "" , ret, MPos, Mlen);
if istable(ret) then
flag := ret[0][0];
tmp := str2array(data, flag);
for i:=0 to length(tmp)-1 do
tmp[i] := replaceText(tmp[i], "--", "+");
try
Rresult := eval(&tmp[1]);
except
//dolog("zhang1021",tmp[1]);//记录报错公式
end;
{if length(tmp)>2 then
return "公式错误."; }
try
LResult := eval(&tmp[0]);
if WCZ then
begin
WCZ += 0.000000001;
if samevalue(setPrecision(LResult,precision),setPrecision(Rresult,precision),WCZ) then
return 1;
end;
LResult := isNan(LResult)?0:Lresult;
Rresult := isNan(Rresult)?0:Rresult;
//if eval(&floattostr(setPrecision(LResult,precision))$flag$floattostr(setPrecision(Rresult,precision))) then
// 获取勾稽的左边结果/右边结果/差额,并给予入库
sysparams["LResult"] := FormatFloat(fStr, Lresult);
sysparams["RResult"] := FormatFloat(fStr, Rresult);
sysparams["chaE"] := strtofloat(FormatFloat(fStr, Lresult))-strtofloat(FormatFloat(fStr, Rresult));
if eval(&FormatFloat(fStr, LResult)$flag$FormatFloat(fStr,Rresult)) then
return 1;
return tmp[0]$(tmp[0] like "[\\+\\-\\*\\/]"?"【"$FormatFloat(fStr, Lresult)$"】":"")$flag$tmp[1]$
(tmp[1] like "[\\+\\-\\*\\/]"?"【"$FormatFloat(fStr, Rresult)$"】":"");
except
end;
end;
//将公式的数值转化为千分位
class function IDS_AuditExpr.FormulaeConvert(v);
begin
if not v then
return v;
ParseRegExpr("\\d+\\.\\d+|\\d+",v,"",ret,MPos,Mlen);
tmp := v;
ret := select distinct * from ret order by length([0]) desc end;
for i:=0 to length(ret)-1 do
begin
if length(ret[i][0])>3 then
begin
//转化为千分位后根据原值小数位数保留小数位
if ret[i][0] like "\\." then
p := length(str2array(ret[i][0], ".")[1]);
tmpvalue := FormatFloat("#,##0"$(p?"."$DupeString('0', p):""), strtofloat(ret[i][0]));
end else
continue;
tmp := replaceText(tmp, ret[i][0], tmpvalue);
end;
return tmp;
end;
//获取报告讯息
class function IDS_AuditExpr.GetReportInfo(data);
begin
//针对提交勾稽去取报告id
if istable(data) then
begin
did:=data[0];
sql:="select a.ID,
a.fundid,
a.reportdate,
a.begindate,
"$ids_tdbc("td_ifnull", array("g.ORG_ESTABLISH_DATE", ids_tdbc("td_strtofloat", array(ids_tdbc("td_formatDateTime", array("p.establishdate", "yyyymmdd"))))))$" Fdate,
a.CLASSIFY
from ts_rformaldata c
left join ts_reportinfo a
on a.fundid = c.tip2
and a.classify = c.classify
and ((a.change_date is null and a.publishdate = c.enddate and
a.begindate = c.begindate) or
(a.change_date is not null and
((c.enddate = a.publishdate and
c.begindate = "$ids_tdbc("td_strtofloat", array(ids_tdbc("td_formatDateTime", array("a.change_date", "yyyymmdd"))))$") or
(c.enddate = "$ids_tdbc("td_incDay", array("a.change_date", -1))$" and
c.begindate = a.begindate))))
left join ts_productinfo p
on a.fundid = p.productid
and p.isvalid = 1
left join TS_FUNDCHANGELOG g
on p.productid = g.fundid
and g.isvalid = 1
where c.id = '"$did$"' and p.isvalid = 1
and c.status <> 0 order by a.isvalid desc";
ret:=ids_execsql(sql,r);
return r;
end else
rid := data;
sql :="select ID,r.fundid,reportdate,begindate,"$ids_TDBC("td_ifnull", array("ORG_ESTABLISH_DATE", ids_TDBC("td_strtofloat",array(ids_TDBC("td_formatDateTime", array("p.establishdate", "yyyymmdd"))))))$" Fdate, CLASSIFY, r.CHANGE_DATE
from ts_reportinfo r join ts_productinfo p on r.fundid = p.productid
left join TS_FUNDCHANGELOG g on p.productid =g.fundid
where r.isvalid=1 and id = '"$rid$"' and p.isvalid = 1 order by Fdate";
ids_execsql(sql,r);
return r;
end;
//解析勾稽表达式,传入一个字符串类型的勾稽表达式,去掉运算符,返回一个数组。
class function IDS_AuditExpr.analysisFormulae(Formulae);
Begin
//去除空格
sysparams["StrReplace"] := array();
Formulae := specialStrReplace(Formulae, 0);
if Pos('or', Formulae) or Pos('and', Formulae) then
ReplaceStr := ReplaceText(Formulae, ' ', '');
else
ReplaceStr := Formulae;
// 勾稽公式页面获取章节公式指标额外增加的文本需替换掉
addReplace := array("(总和)","整表","(上期末)","上期年报","一季报","二季报","三季报","四季报",
"中报","上期报告","前年报告","(期初)","(期末)","(集合)","(份额总和)","TXT","#");
//替换字符串中的运算符
wantReplace := Array('GetMaxValue', 'GetMinValue', '>=', '<=', '+', '-', '*', '/', '>', '<', '=', 'and', 'or', 'Min', 'Max', 'abs', 'AnsiContainsText', 'in',
'without', '?', ':ture', ';;;', ';;', ':true',',','(买入)','(卖出)','(下属数据总和)','funcOfIf'{, "'"}, "funcOfRoundTo", "funcOfRoundDown",
"funcOfIsNull", "not", "SameText", "ifThen", "isNull", "≠", "公募基金代码", "公募基金全称", "公募基金简称", "textMatch", "getMonthSpan", "fundInfo", "基金存续起始日");
if SYSPARAMS["addChapterRow"] then
wantReplace &= addReplace;
for i:=0 to length(wantReplace)-1 do
Begin
if Pos(wantReplace[i], ReplaceStr) then
ReplaceStr := ReplaceStr(ReplaceStr, wantReplace[i], ';');
End;
//正则替换掉and or min等情况的字符
reAry := array(';\\d+;', ';\\d+$', ';Min\\\(', ';\\d+;', ';\\d+\\\);\\\(', ';\\d+\\\)', ';\\\)\\\);', ';\\\(\\\(;', ';Max\\\(',';\\d+\\.\\d+',';abs\\\(');
for k := 0 to length(reAry) - 1 do
begin
ParseRegExpr(reAry[k], ReplaceStr, "", matchResult, MPos, Mlen);
if matchResult<>0 then
Begin
for j:=0 to length(matchResult)-1 do
ReplaceStr := ReplaceText(ReplaceStr, matchResult[j][0], ';');
End;
end;
if ReplaceStr like ';$' then ReplaceStr := ReplaceStr[1:length(ReplaceStr)-1];
if ReplaceStr like '^;' then ReplaceStr := ReplaceStr[2:length(ReplaceStr)];
//将字符串转化为数组
tAry := Str2Array(ReplaceStr,';');
//去除每个元素首尾的空格
sAry := array();
for h := 0 to length(tAry) - 1 do
begin
sAry[h] := Trim(tAry[h]);
end;
if not (Pos('(', ReplaceStr) or Pos(')', ReplaceStr)) then
begin
sAry := sselect distinct specialStrReplace(thisrow, 1) from sAry where not (thisrow like "^\\d+$") order by lengthW(specialStrReplace(thisrow, 1)) desc end;
return sAry;
//return array2str(sAry);
end;
//去掉每个元素中多余的括号
nAry := array();
for m := 0 to length(sAry) - 1 do
begin
s := sAry[m];
//查找"("与")"的位置
sLen := length(s);
brackets := array('(',')');
markAry := array(array(),array());
for idx := 0 to length(brackets)-1 do
begin
t:=s;
i:=1;
j:=0;
k:=0;
while AnsiContainsText(t,brackets[idx]) do
begin
i := pos(brackets[idx], t);
k := k + i;
markAry[idx][j]:=k;
j:=j+1;
if k+1>sLen then break;
t:=s[k+1:sLen];
end;
end;
if not markAry[0] then
begin
if not markAry[1] then
begin
nAry[m] := s;
continue;
end
else
begin
s := ReplaceText(s, ')', '');
nAry[m] := s;
continue;
end;
end
else
begin
if not markAry[1] then
begin
s := ReplaceText(s, '(', '');
nAry[m] := s;
continue;
end;
end;
//就近原则匹配相近的左右括号
leftPair := array();
rightPair := array();
for i := 0 to length(markAry[1]) - 1 do
begin
for j := 0 to length(markAry[0]) - 1 do
begin
if markAry[0][j] in leftPair then continue;
if markAry[0][j] < markAry[1][i] then
begin
k := 1;
while markAry[0][j+k] do
begin
if not (markAry[0][j+k] in leftPair) then break;
k := k + 1;
end;
if markAry[0][j+k] then
begin
if markAry[0][j+k] > markAry[1][i] then
begin
leftPair[length(leftPair)] := markAry[0][j];
rightPair[length(rightPair)] := markAry[1][i];
end
else continue;
end
else
begin
leftPair[length(leftPair)] := markAry[0][j];
rightPair[length(rightPair)] := markAry[1][i];
end;
end
else break;
end;
end;
//删除多余的括号
for l := 0 to length(markAry[0]) - 1 do
begin
if not (markAry[0][l] in leftPair) then s[markAry[0][l]:markAry[0][l]] := '';
end;
for n := 0 to length(markAry[1]) -1 do
begin
if not (markAry[1][n] in rightPair) then s[markAry[1][n]:markAry[1][n]] := '';
end;
//删除占位符
//s := replaceStr(trim(s), '\0', '');
//删除头尾能够配对的括号针对不规范的公式需要加try包裹防止公式列表页面查询时报错
try
if rightPair[length(rightPair)-1] - leftPair[length(leftPair)-1] + 1 = length(s) then s := s[2:length(s)-1];
except
end;
nAry[m] := trim(s);
end;
t := sselect distinct trim(specialStrReplace(thisrow, 1)) from nAry where not (trim(thisrow) like "^\\d+$" or thisrow='' or thisrow like "'[\\s\\S]+'" or specialStrReplace(thisrow, 1) like "^'[\\s\\S]+'$") order by lengthW(specialStrReplace(thisrow, 1)) desc end;
//t := sselect distinct thisrow from nAry where not (thisrow like "^\\d+$") order by lengthW(thisrow) desc end;
//t := array2str(t);
return t;
End;
class function IDS_AuditExpr.encryptFormulae(s);
begin
result := Base64Encode(tostring(s));
return result;
end;
class function IDS_AuditExpr.decryptFormulae(s);
begin
result := Base64Decode(tostring(s));
return result;
end;
class function IDS_AuditExpr.XBRLInsert(Formulae, FundType, Classify, isencrypt);
Begin
//新勾稽条目插入接收四个参数Formulae勾稽表达式FundType基金类型Classify报告类型isencrypt是否需要加密
table := "XBRL_DataAuditConf";
eFormulae := class(IDS_AuditExpr).encryptFormulae(Formulae);
sql := "select * from "$table$"
where formulae in ('"$Formulae$"','"$eFormulae$"')
and classify='"$Classify$"'";
ids_execsql(sql, r1);
if istable(r1) then return '该勾稽关系式已存在';
if isencrypt then Formulae := eFormulae;
sql:="INSERT INTO "$table$"
(
Formulae,
FundType,
Classify
)
VALUES
(
"
$ "'" $ Formulae $ "'" $ ","
$ (FundType?("'" $ FundType $ "'"):"null") $ ","
$ "'" $ Classify $ "'" $
")";
ret1 := ids_execsql(sql,r);
if ret1=0 then
errorMsg := SQLErrorMsg();
else
return 'success';
if errorMsg then
return errorMsg;
End;
class function IDS_AuditExpr.specialStrReplace(str, r_type); //针对部分特殊字符进行临时转换 r_type: 0-转化为特殊串 1-转化为原字符串
begin
if not str then
return '';
arr := specialStrConf();
StrList := array();
SNum := 0;
if r_type then
begin
StrList := sysparams["StrReplace"];
if istable(StrList) then
begin
for StrNum:=length(StrList)-1 downto 0 do
str := replaceStr(str, StrList[StrNum][1], StrList[StrNum][0]);
end;
for StrNum:=0 to length(arr)-1 do
str := replaceStr(str, arr[StrNum][1], arr[StrNum][0]);
end
else
begin
for StrNum:=0 to length(arr)-1 do
str := replaceStr(str, arr[StrNum][0], arr[StrNum][1]);
ParseRegExpr("'+[^']+'+",str,"",StrArr,MPos,Mlen);
for StrArrNum:=0 to length(StrArr)-1 do
begin
str := replaceStr(str, StrArr[StrArrNum][0], "@STR"$SNum$"@");
sysparams["StrReplace"][SNum] := array(StrArr[StrArrNum][0], "@STR"$SNum$"@");
SNum += 1;
end;
ParseRegExpr("[^+\\-\\*/<> =]+",str,"",result,MPos,Mlen);
result := select * from result order by length([0]) desc end;
for StrNum:=0 to length(result)-1 do
begin
if ansipos("#", result[StrNum][0]) and ansipos("getLastYear", result[StrNum][0])=0
and ansipos("AnsiContainsText", result[StrNum][0])=0 and ansipos("TXT", result[StrNum][0])=0
and ansipos("funcOfRowCount", result[StrNum][0])=0 and ansipos("isNull", result[StrNum][0])=0 then
begin
filterStr := result[StrNum][0];
if filterStr like "^\\([\\s\\S]+\\)$" and ansipos("fundInfo", filterStr)=0 then
filterStr := filterStr[2:length(filterStr)-1]; // 如果条件替换串的首位都是括号,则需要去掉
str := replaceStr(str, filterStr, "@FILTER"$StrNum$"@");
sysparams["StrReplace"][SNum] := array(filterStr, "@FILTER"$StrNum$"@");
SNum += 1;
end
end;
end;
return str;
end;
function specialStrConf();
begin
return array(
("“-”", "@SP1@"),
("定期存款/协议存款", "@SP2@"),
("报告期末基金资产组合情况-注", "@SP3@"),
("资产负债表-注", "@SP4@"),
("银行间市场债券正回购-文", "@SP5@"),
("管理人对报告期内基金利润分配情况的说明-FA", "@SP6@"),
("(利率风险的敏感性分析)对资产负债表日基金资产净值的影响金额", "@SP7@"),
("积极投资前五名股票明细(指数基金)", "@SP8@"),
("(基金中基金)报告期末按公允价值占基金资产净值比例大小排序的基金投资明细", "@SP9@"),
("#like ", "@SP10@")
);
end;
function replace_FH(str);
begin
FHconf := array(
("@横杠@", "-")
);
for r_i:=0 to length(FHconf)-1 do
begin
if str like FHconf[r_i][0] then
str := replaceStr(str, FHconf[r_i][0], FHconf[r_i][1]);
end;
end;
// 以下为公募勾稽构筑新增函数
function funcOfIf(a, b, c); // if判断函数
begin
if eval(&a) then
return eval(&b);
else
return eval(&c);
end;
function funcOfRoundTo(n, p); // 精度进位函数
begin
return SimpleRoundTo(n, -p);
end;
function funcOfRoundDown(n, p); // 精度舍位函数
begin
ns := tostring(n);
if ansipos(".", ns) then
begin
tmp := str2array(ns, ".");
return strtofloat(tmp[0]$"."$leftStr(tmp[1],p));
end
else
return strtofloat(ns);
end;
function isNull(value); // 空判断, 原函数只能处理数值类型,与系统函数重名,但只生效于勾稽公式中的变量
begin
if tostring(value)<>"" and not ifnil(value) then
return 0;
return 1;
end;
function 公募基金代码(fundid);
begin
pinfo := sysparams["productInfo"];
if not istable(pinfo) then
pinfo := getProductInfoByGM();
pinfo := select ["PRODUCTID"] from pinfo where ["PRODUCTID"]=fundid end;
if istable(pinfo) then
return 1;
else
return 0;
end;
function 公募基金简称(shortname);
begin
pinfo := sysparams["productInfo"];
if not istable(pinfo) then
pinfo := getProductInfoByGM();
pinfo := select ["SHORTNAME"], ["SUB_SHORTNAME"] from pinfo where ["SUB_SHORTNAME"]=trim(shortname) or ["SHORTNAME"]=trim(shortname) end;
if istable(pinfo) then
return 1;
else
return 0;
end;
function 公募基金全称(fullname);
begin
pinfo := sysparams["productInfo"];
if not istable(pinfo) then
pinfo := getProductInfoByGM();
pinfo := select ["FULLNAME"] from pinfo where ["FULLNAME"]=trim(fullname) end;
if istable(pinfo) then
return 1;
else
return 0;
end;
function 基金存续起始日(fundid);
begin
pinfo := sysparams["productInfo"];
if not istable(pinfo) then
pinfo := getProductInfoByGM();
pinfo := select ["EXISTENCEDATE"], ["ESTABLISHDATE"] from pinfo where ["PRODUCTID"]=fundid end;
if istable(pinfo) then
return pinfo[0]["EXISTENCEDATE"]?:pinfo[0]["ESTABLISHDATE"];
else
return 0;
end;
function getProductInfoByGM();
begin
sql := "select PRODUCTID, SHORTNAME, FULLNAME, SUB_SHORTNAME, MANAGER, CUSTODIAN, ESTABLISHDATE, NOTICEDATE, EXISTENCEDATE, LISTING_DATE from ts_productinfo where isvalid = 1 and (FULLNAME like '%证券投资基金' or FUNDPRODUCTTYPE = '公募基金')";
ret := ids_execsql(sql, r);
if not (ret and istable(r)) then
r := array();
sysparams["productInfo"] := r;
return r;
end;
function GetMaxValue();
begin
tmpParams := params;
valArr := array();
if istable(tmpParams) then
begin
for i:=0 to length(tmpParams)-1 do
begin
if istable(tmpParams[i]) then
valArr &= tmpParams[i];
else if ifstring(tmpParams[i]) or ifnumber(tmpParams[i]) then
valArr[length(valArr)] := tmpParams[i];
end;
end;
if istable(valArr) then
v := MaxValue(valArr);
else
v := 0;
return v;
end;
function GetMinValue();
begin
tmpParams := params;
valArr := array();
if istable(tmpParams) then
begin
for i:=0 to length(tmpParams)-1 do
begin
if istable(tmpParams[i]) then
valArr &= tmpParams[i];
else if ifstring(tmpParams[i]) or ifnumber(tmpParams[i]) then
valArr[length(valArr)] := tmpParams[i];
end;
end;
if istable(valArr) then
v := MinValue(valArr);
else
v := 0;
return v;
end;

View File

@ -0,0 +1,258 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
递归读取文件提取 SQL 语句
- select/vselect/sselect ... end; (不匹配字符串中的关键字select和end之间不能有分号)
- update ... end; (update和end之间不能有分号)
- delete ... ; (delete到分号)
- insert ... ; (insert到分号)
- 按文件分组输出
"""
import os
import re
from pathlib import Path
def extract_sql_statements(content):
"""
从文本内容中提取 SQL 语句
- select/vselect/sselect ... end; (select和end之间不能有分号)
- update ... end; (update和end之间不能有分号)
- delete ... ; (delete到分号)
- insert ... ; (insert到分号)
不会匹配字符串中的关键字
Args:
content: 文件内容字符串
Returns:
按类型分类的SQL语句字典
"""
# 将所有字符串替换为占位符(保持长度不变)
def replace_string_content(match):
quote = match.group(0)[0]
length = len(match.group(0))
return quote + ' ' * (length - 2) + quote
# 将注释替换为空格(保持长度不变)
def replace_comment(match):
return ' ' * len(match.group(0))
# 替换单引号字符串
content_cleaned = re.sub(r"'(?:[^'\\]|\\.)*?'", replace_string_content, content)
# 替换双引号字符串
content_cleaned = re.sub(r'"(?:[^"\\]|\\.)*?"', replace_string_content, content_cleaned)
# 替换单行注释 //
content_cleaned = re.sub(r'//.*?$', replace_comment, content_cleaned, flags=re.MULTILINE)
# 替换多行注释 /* */
content_cleaned = re.sub(r'/\*.*?\*/', replace_comment, content_cleaned, flags=re.DOTALL)
results = {
'select': [],
'sselect': [],
'vselect': [],
'update': [],
'delete': [],
'insert': []
}
# 在清理后的内容中匹配,但从原始内容中提取
# 匹配 vselect ... end; (中间不能有分号)
vselect_pattern = r'(?i)\bvselect\b(?!\s*\()[^;]*?end\s*;'
for match in re.finditer(vselect_pattern, content_cleaned, re.DOTALL):
original_text = content[match.start():match.end()].strip()
results['vselect'].append(original_text)
# 匹配 sselect ... end; (中间不能有分号)
sselect_pattern = r'(?i)\bsselect\b(?!\s*\()[^;]*?end\s*;'
for match in re.finditer(sselect_pattern, content_cleaned, re.DOTALL):
original_text = content[match.start():match.end()].strip()
results['sselect'].append(original_text)
# 匹配 select ... end; (但不包括 vselect 和 sselect中间不能有分号)
select_pattern = r'(?i)(?<![vs])\bselect\b(?!\s*\()[^;]*?end\s*;'
for match in re.finditer(select_pattern, content_cleaned, re.DOTALL):
original_text = content[match.start():match.end()].strip()
results['select'].append(original_text)
# 匹配 update ... end; (中间不能有分号)
update_pattern = r'(?i)\bupdate\b(?!\s*\()[^;]*?end\s*;'
for match in re.finditer(update_pattern, content_cleaned, re.DOTALL):
original_text = content[match.start():match.end()].strip()
results['update'].append(original_text)
# 匹配 delete ... ; (到第一个分号为止)
delete_pattern = r'(?i)\bdelete\b(?!\s*\()(?:\s+from\b)[^;]*?;'
for match in re.finditer(delete_pattern, content_cleaned, re.DOTALL):
original_text = content[match.start():match.end()].strip()
results['delete'].append(original_text)
# 匹配 insert ... ; (到第一个分号为止)
insert_pattern = r'(?i)\binsert\b(?!\s*\()[^;]*?;'
for match in re.finditer(insert_pattern, content_cleaned, re.DOTALL):
original_text = content[match.start():match.end()].strip()
results['insert'].append(original_text)
return results
def read_files_recursively(directory, output_file, file_extensions=None):
"""
递归读取目录下的所有文件提取SQL语句并按文件分组输出
Args:
directory: 要扫描的目录路径
output_file: 输出文件路径
file_extensions: 要处理的文件扩展名列表 ['.sql', '.txt']None表示所有文件
"""
# 按文件存储SQL语句
# 结构: {file_path: {'select': [...], 'sselect': [...], 'vselect': [...]}}
files_statements = {}
processed_files = 0
error_files = []
# 递归遍历目录
for root, dirs, files in os.walk(directory, followlinks=True):
for filename in files:
file_path = os.path.join(root, filename)
# 跳过输出文件本身
if os.path.abspath(file_path) == os.path.abspath(output_file):
continue
# 如果指定了文件扩展名,只处理匹配的文件
if file_extensions:
if not any(filename.lower().endswith(ext.lower()) for ext in file_extensions):
continue
try:
# 尝试多种编码读取文件
encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']
content = None
for encoding in encodings:
try:
with open(file_path, 'r', encoding=encoding) as f:
content = f.read()
break
except UnicodeDecodeError:
continue
if content is None:
print(f"警告: 无法读取文件 {file_path} (编码问题)")
error_files.append(file_path)
continue
# 提取SQL语句按类型分类
matches = extract_sql_statements(content)
# 统计该文件中的语句数量
total_matches = sum(len(stmts) for stmts in matches.values())
if total_matches > 0:
files_statements[file_path] = matches
print(f"{file_path}: 找到 {total_matches} 条语句 "
f"(select: {len(matches['select'])}, "
f"sselect: {len(matches['sselect'])}, "
f"vselect: {len(matches['vselect'])}, "
f"update: {len(matches['update'])}, "
f"delete: {len(matches['delete'])}, "
f"insert: {len(matches['insert'])})")
processed_files += 1
except Exception as e:
print(f"错误: 处理文件 {file_path} 时出错: {str(e)}")
error_files.append(file_path)
# 计算总数
total_select = sum(len(stmts['select']) for stmts in files_statements.values())
total_sselect = sum(len(stmts['sselect']) for stmts in files_statements.values())
total_vselect = sum(len(stmts['vselect']) for stmts in files_statements.values())
total_update = sum(len(stmts['update']) for stmts in files_statements.values())
total_delete = sum(len(stmts['delete']) for stmts in files_statements.values())
total_insert = sum(len(stmts['insert']) for stmts in files_statements.values())
total_statements = total_select + total_sselect + total_vselect + total_update + total_delete + total_insert
# 写入输出文件(按文件分组输出)
if total_statements > 0:
with open(output_file, 'w', encoding='utf-8') as f:
f.write(f"// 共提取到 {total_statements} 条SQL语句\n")
f.write(f"// 处理了 {processed_files} 个文件\n")
f.write(f"// SELECT: {total_select}\n")
f.write(f"// SSELECT: {total_sselect}\n")
f.write(f"// VSELECT: {total_vselect}\n")
f.write(f"// UPDATE: {total_update}\n")
f.write(f"// DELETE: {total_delete}\n")
f.write(f"// INSERT: {total_insert}\n")
f.write("//" + "=" * 80 + "\n")
# 按文件输出
for file_path, statements in sorted(files_statements.items()):
file_total = sum(len(stmts) for stmts in statements.values())
f.write("\n")
f.write("//" + "=" * 80 + "\n")
f.write(f"// 文件: {file_path}\n")
f.write(f"// 共 {file_total} 条语句 "
f"(SELECT: {len(statements['select'])}, "
f"SSELECT: {len(statements['sselect'])}, "
f"VSELECT: {len(statements['vselect'])}, "
f"UPDATE: {len(statements['update'])}, "
f"DELETE: {len(statements['delete'])}, "
f"INSERT: {len(statements['insert'])})\n")
f.write("//" + "=" * 80 + "\n")
# 按顺序输出select -> sselect -> vselect -> update -> delete -> insert
for stmt_type in ['select', 'sselect', 'vselect', 'update', 'delete', 'insert']:
stmts = statements[stmt_type]
if stmts:
for idx, stmt in enumerate(stmts, 1):
f.write(stmt)
f.write("\n")
print(f"\n✓ 成功提取 {total_statements} 条SQL语句")
print(f" - SELECT: {total_select}")
print(f" - SSELECT: {total_sselect}")
print(f" - VSELECT: {total_vselect}")
print(f" - UPDATE: {total_update}")
print(f" - DELETE: {total_delete}")
print(f" - INSERT: {total_insert}")
print(f"✓ 结果已保存到: {output_file}")
else:
print(f"\n! 没有找到匹配的SQL语句")
if error_files:
print(f"\n! 有 {len(error_files)} 个文件处理失败")
def main():
"""主函数"""
# 配置参数
source_directory = "/mnt/c/Programs/Tinysoft/TSLGen2/funcext" # 当前目录,可以修改为你的目标目录
output_file = "test_sql_statements.tsf"
# 可以指定只处理特定扩展名的文件,如 ['.sql', '.txt']
# 设为 None 则处理所有文件
file_extensions = ['.tsf']
print(f"开始扫描目录: {os.path.abspath(source_directory)}")
print(f"输出文件: {output_file}")
print(f"文件类型: {file_extensions if file_extensions else '所有文件'}")
print("-" * 80)
# 检查目录是否存在
if not os.path.exists(source_directory):
print(f"错误: 目录不存在: {source_directory}")
return
# 执行提取
read_files_recursively(source_directory, output_file, file_extensions)
if __name__ == "__main__":
main()

View File

@ -43,7 +43,8 @@ for root_dir in "${ROOT_DIRS[@]}"; do
# 遍历当前目录下的所有 .tsf 文件
while IFS= read -r -d '' file; do
echo "--- 正在解析: $file"
output=$(tree-sitter parse "$file" 2>&1)
# output=$(tree-sitter parse "$file" 2>&1)
output=$(tree-sitter parse "$file" 2>&1 | tr -d '\0')
if echo "$output" | grep -q "ERROR\|MISSING"; then
echo "❌ 语法错误在文件: $file"
echo "$output"

View File

@ -0,0 +1,12 @@
<?tslx>
<?tsl echo "123"; ?>
aaaa
<?= echo "123" ?>
<?tsl
a := 1;
function abc();
begin
a := 1;
end

View File

@ -0,0 +1,606 @@
// ============================================
// TSF 语法单元测试
// ============================================
// ============================================
// 1. 字面量测试
// ============================================
// 数字字面量
123;
+456;
-789;
123.456;
.456;
123.;
123e10;
123e-5;
+123.456e+10;
0xFF;
0xABCD;
0b1010;
0o777;
// 字符串字面量
"hello world";
'single quote';
"escape \n \r \t \\ \" \'";
"unicode \u4E2D \U00004E2D";
"hex \x41 \x42";
L"wide string";
L'wide char';
// 字符字面量
#65;
#$41;
#$FF;
// 布尔值
true;
false;
TRUE;
FALSE;
True;
False;
// 特殊值
nil;
nan;
+inf;
-inf;
...;
// ============================================
// 2. 标识符测试
// ============================================
identifier;
_underscore;
camelCase;
PascalCase;
snake_case;
with123numbers;
中文标识符;
混合Chinese123标识符;
// ============================================
// 3. 一元表达式测试
// ============================================
// 前缀一元运算符
+x;
-x;
not x;
.!x;
.!!x;
##x();
// 表达式运算符
@expr;
&expr;
// 前缀自增自减
++x;
--x;
// 后缀自增自减
x++;
x--;
// ============================================
// 4. 二元表达式测试
// ============================================
// 算术运算
a + b;
a - b;
a * b;
a / b;
a \ b;
a % b;
a mod b;
a div b;
// 幂运算(右结合)
a ^ b;
a :^ b;
a ^ b ^ c;
// 矩阵运算
a :* b;
a :/ b;
a :\ b;
a | b;
a :| b;
// 位运算
a .| b;
a .& b;
a .^ b;
// 移位运算
a shl b;
a shr b;
a rol b;
a ror b;
// 比较运算
a = b;
a .= b;
a <> b;
a .<> b;
a > b;
a .> b;
a < b;
a .< b;
a >= b;
a .>= b;
a <= b;
a .<= b;
a like b;
a in b;
a is b;
// 逻辑运算
a or b;
a and b;
a .|| b;
a .&& b;
a || b;
a && b;
// 集合运算
a union b;
a union2 b;
a intersect b;
a outersect b;
a minus b;
// 范围
a -> b;
// 连接运算
a $ b;
// 对数运算
a ~ b;
// ============================================
// 5. 三元表达式测试
// ============================================
condition ? consequence : alternative;
a > b ? a : b;
nested ? (inner ? x : y) : z;
condition ? : default_value;
// ============================================
// 6. 赋值表达式测试
// ============================================
// 基本赋值
x := 10;
// 复合赋值
x += 1;
x -= 1;
x *= 2;
x /= 2;
x \= 2;
x ^= 2;
x ~= 2;
x %= 2;
x div= 2;
x |= 1;
x :|= 1;
x &= 1;
x :*= 1;
x :/= 1;
x :\= 1;
x :^= 1;
x ::= 1;
x .|= 1;
x .&= 1;
x .||= 1;
x .&&= 1;
x .^= 1;
x union= set1;
x union2= set1;
x intersect= set1;
x outersect= set1;
x minus= set1;
// 下标赋值
arr[0] := 10;
matrix[i, j] := 20;
// 属性赋值
obj.property := value;
// 解包赋值
[a, b, c] := array(1, 2, 3);
[x, y] := tuple;
// ============================================
// 7. 数组测试(重点)
// ============================================
// 空数组
array();
// 简单数组
array(1, 2, 3);
array(1, 2, 3,);
// 多种类型混合
array(1, "string", true, nil, 3.14);
// 键值对数组
array(a: 1, b: 2, c: 3);
array("key1": "value1", "key2": "value2");
array(1: "one", 2: "two", 3: "three");
// 隐式数组(嵌套括号)
array((1, 2, 3));
array((a, b), (c, d));
// 键值对带隐式数组
array(
point1: (x, y, z),
point2: (1, 2, 3)
);
// 混合数组元素
array(
1,
key: value,
(a, b, c),
nested: (1, 2, 3),
"string"
);
// 嵌套数组
array(
array(1, 2, 3),
array(4, 5, 6),
array(7, 8, 9)
);
// 多层嵌套
array(
array(
array(1, 2),
array(3, 4)
),
array(
array(5, 6),
array(7, 8)
)
);
// 复杂键值对嵌套
array(
matrix: array(
row1: array(1, 2, 3),
row2: array(4, 5, 6)
),
vector: array(x: 1, y: 2, z: 3),
nested: (
(1, 2),
(3, 4)
)
);
// 数组带表达式
array(1 + 2, 3 * 4, 5 ^ 6);
array(func(), obj.method(), arr[0]);
// 数组带条件表达式
array(
a > b ? a : b,
condition ? x : y
);
// 尾随逗号测试
array(1, 2, 3,);
array(a: 1, b: 2,);
array((1, 2, 3,));
// ============================================
// 8. 下标访问测试
// ============================================
// 基本下标
arr[0];
arr[i];
matrix[x, y];
// 切片
arr[:];
arr[1:];
arr[:10];
arr[1:10];
// 矩阵切片
matrix[:, :];
matrix[1:5, 2:8];
matrix[:, 0];
matrix[0, :];
matrix[1:, :5];
// 多维切片
tensor[1:5, 2:8, 3:9];
tensor[:, :, 0];
// 嵌套下标
arr[0][1];
matrix[i][j];
arr[func()][obj.attr];
// ============================================
// 9. 属性访问测试
// ============================================
obj.property;
obj.method;
nested.obj.property;
arr[0].attribute;
func().result;
(expr).attr;
// ============================================
// 9.5. 链式调用测试(属性和方法混合)
// ============================================
// 方法后访问属性
obj.method().property;
func().result.value;
// 属性后调用方法
obj.property.method();
data.field.toString();
// 混合链式调用
obj.Abc().def.FGH();
obj.method1().property.method2();
obj.a().b().c().d();
obj.a.b().c.d();
// 复杂链式
obj.getData().results[0].format();
obj.query().filter().map().reduce();
api.connect().auth().request().parse();
// 链式调用带参数
obj.method(arg).property.next(param);
builder.setX(1).setY(2).setZ(3).build();
// 链式调用带下标
obj.method()[0].property;
arr.filter()[0].name;
data.get("key").items[0].process();
// 超长链式
obj.a.b.c.d.e.f.g.h.i.j.k();
func1().func2().func3().func4().func5();
// 混合运算符的链式
obj.fetch().result := value;
// 链式调用在表达式中
obj.calculate().result + other.getValue().data;
arr.first().value * matrix.last().coefficient;
// ============================================
// 10. 函数调用测试
// ============================================
// 无参数调用
func();
// 单参数
func(arg);
// 多参数
func(a, b, c);
// 命名参数
func(name: value);
func(x: 1, y: 2, z: 3);
// 混合参数
func(1, 2, name: value, 3, key: data);
// const 参数
func(const arg);
func(const x, const y);
// 星号参数
// func(*);
// func(a, *, b);
// 嵌套调用
func1(func2(func3()));
// 链式调用
obj.method1().method2().method3();
// ============================================
// 11. 括号表达式测试
// ============================================
(expr);
((nested));
(a + b) * (c + d);
(condition ? x : y);
// ============================================
// 12. 内置表达式测试
// ============================================
// new 表达式
new ClassName;
new MyClass();
// echo 表达式
echo "message";
echo x, y, z;
echo "value:", value, "result:", result;
// raise 表达式
raise Exception;
raise CustomError("message");
// inherited 表达式
inherited;
inherited method();
inherited ParentClass.method();
// ============================================
// 13. 复杂组合测试
// ============================================
// 运算符优先级混合
a + b * c;
(a + b) * c;
a ^ b * c;
a + b ^ c;
a and b or c;
(a and b) or c;
// 链式运算
a.b.c.d;
arr[0][1][2];
obj.method().property[0];
// 复杂赋值
obj.arr[i].prop := value;
matrix[x, y] += delta;
obj.counter++;
// 复杂数组初始化
array(
id: 1,
name: "test",
values: array(1, 2, 3),
matrix: array(
array(1, 2),
array(3, 4)
),
computed: func(),
conditional: x > 0 ? pos : neg,
nested: (
(a, b),
(c, d)
)
);
// 复杂表达式
((a + b) * c - d) / (e ^ f);
func(x: a + b, y: c * d, z: array(1, 2, 3));
obj.method(arr[0], key: value).property++;
// 多层嵌套结构
array(
level1: array(
level2: array(
level3: array(
data: (1, 2, 3)
)
)
)
);
// ============================================
// 14. 边界情况测试
// ============================================
// 空切片
arr[];
// 单元素数组
array(1);
array(1,);
// 单键值对
array(key: value);
// 空隐式数组(语法上可能无效,但测试边界)
array(());
// 连续运算符
a+-b;
a-+b;
// 长标识符链
very.long.chain.of.attributes.and.calls().more[0].data;
// 深层嵌套表达式
((((((expr))))));
// 多行数组格式
array(
1,
2,
3,
4,
5
);
// ============================================
// 15. 解包模式测试
// ============================================
[a] := array(1);
[a, b] := array(1, 2);
[x, y, z] := tuple;
[first, second, third] := values;
// ============================================
// 16. 组合运算测试
// ============================================
// 矩阵和标量混合
matrix :* scalar + vector;
(matrix :| vector) :^ 2;
// 位运算和逻辑运算
(a .& b) .|| (c .^ d);
a and (b .| c);
// 集合运算链
set1 union set2 intersect set3;
(set1 union set2) minus set3;
// 范围和运算
(1 -> 10) union (20 -> 30);
array(0 -> 9);
// 字符串连接
"hello" $ " " $ "world";
str1 $ str2 $ str3;
// ============================================
// 17. 特殊语法组合
// ============================================
// 三元嵌套
a ? b ? c : d : e ? f : g;
// 赋值链(如果支持)
a := b := c := 0;
// 调用后自增
arr[0]++;
// 属性链式赋值
obj.a.b.c := value;
// 复杂下标表达式
arr[i + 1][j * 2];
matrix[func(x), obj.y];
// // ============================================
// // 测试完成
// // ============================================

View File

@ -0,0 +1,87 @@
class(test).test1(); // 允许这样调用class function
// class(); // 不允许这么调用函数
// in();
// do();
// select();
// sselect();
// vselect();
// update();
// delete();
// insert();
type(); // 仅有type允许函数调用
// var global static const
var type := 1; // 仅var允许
// global type: string := "123";
// static type: integer := 123;
// const type = 123;
// 赋值
type := 1; // 允许左值,也要允许右值
// class := 1; // 不允许
// do := 1; // 不允许
// select := 1; // 不允许
// sselect := 1; // 不允许
// vselect := 1; // 不允许
// update := 1; // 不允许
// delete := 1; // 不允许
// insert := 1; // 不允许
// in := 1; // 不允许
// 对象调用 除了class都允许
o := new test();
o.In();
o.type();
o.Do();
o.Select();
o.SSelect();
o.VSelect();
o.Delete();
o.Update();
o.Insert();
// function class(); // 不允许函数名
// begin
// end;
// function In(); // 不允许
// begin
// end;
// function Do();
// begin
// end;
// function Select();
// begin
// end;
// function SSelect();
// begin
// end;
// function VSelect();
// begin
// end;
// function Delete();
// begin
// end;
// function Update();
// begin
// end;
// function Insert();
// begin
// end;
function type();
begin
end;
type test = class
class function test1();
// function class(); //不允许
function In();
function type();
function Do();
function Select();
function SSelect();
function VSelect();
function Delete();
function Update();
function Insert();
end;

View File

@ -0,0 +1,15 @@
function(arr, i);
begin
end
function(arr, i);
begin
echo "123";
end;
pf := function(arr, i);
begin
echo "123";
end
pf := function(arr, i);
begin
echo "123";
end;;

View File

@ -0,0 +1,242 @@
// 共提取到 127 条SQL语句
// 处理了 115 个文件
// SELECT: 64 条
// SSELECT: 23 条
// VSELECT: 11 条
// UPDATE: 24 条
// DELETE: 3 条
// INSERT: 2 条
//================================================================================
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/OfficeXml-dev/funcext/OfficeXml/docx/vba/DocxVba.tsf
// 共 1 条语句 (SELECT: 0, SSELECT: 1, VSELECT: 0, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
sselect ['FileName'] from zip.Files() where AnsiStartsText('word/media/image', ['FileName']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/OfficeXml-dev/generator/CreateAdapter.tsf
// 共 3 条语句 (SELECT: 2, SSELECT: 0, VSELECT: 0, UPDATE: 1, DELETE: 0, INSERT: 0)
//================================================================================
select * from children where not ifnil(['adapter_key']) end;
select * from children where not ifnil(['adapter_child_key']) end;
update children set ['adapter_key'] = array(['adapter_key']) where ifString(['adapter_key']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/OfficeXml-dev/generator/CreateDecorator.tsf
// 共 6 条语句 (SELECT: 1, SSELECT: 1, VSELECT: 0, UPDATE: 4, DELETE: 0, INSERT: 0)
//================================================================================
select ['field'], select * from thisgroup end as 'detail' from children group by ['field'] having countof(['field']) > 1 end;
sselect["field"] from ml_children end;
update attrs set ['new_field'] = format('XmlAttr%s', ['field']) end;
update children set ['origin_field'] = ['field'] end;
update children set ['field'] = uppercase(['prefix']) + ['field'] where not ifnil(['ml']) and ['field'] in fields end;
update children set ['new_field'] = format('XmlChild%s', ['field']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/OfficeXml-dev/generator/CreateTslUnit.tsf
// 共 16 条语句 (SELECT: 5, SSELECT: 3, VSELECT: 0, UPDATE: 8, DELETE: 0, INSERT: 0)
//================================================================================
select ['field'], select * from thisgroup end as 'detail' from attrs group by ['field'] having countof(['field']) > 1 end;
select ['field'], select * from thisgroup end as 'detail' from children group by ['field'] having countof(['field']) > 1 end;
select ['origin_field'], select * from thisgroup end as 'detail' from attrs group by ['origin_field'] having countof(['origin_field']) > 1 end;
select ['origin_field'], select * from thisgroup end as 'detail' from children group by ['origin_field'] having countof(['origin_field']) > 1 end;
select * from children where ['multi'] <> 1 end;
sselect ['ml'] from children where ifString(['ml']) end;
sselect ['field'] from mu_attrs end;
sselect ['field'] from mu_children end;
update attrs set ['origin_field'] = ['field'] end;
update attrs set ['field'] = uppercase(['prefix']) + ['field'] where ifstring(['prefix']) and ['field'] in fields end;
update attrs set ['field'] = ['field'] + 'N' where ifnil(['prefix']) and ['field'] in fields end;
update attrs set ['new_field'] = format('XmlAttr%s', ['field']) end;
update children set ['origin_field'] = ['field'] end;
update children set ['field'] = uppercase(['prefix']) + ['field'] where ifstring(['prefix']) and ['field'] in fields end;
update children set ['field'] = ['field'] + 'N' where ifnil(['prefix']) and ['field'] in fields end;
update children set ['new_field'] = format('XmlChild%s', ['field']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/TSOffice/funcext/TSOffice/TOfficeObj.tsf
// 共 10 条语句 (SELECT: 3, SSELECT: 5, VSELECT: 1, UPDATE: 1, DELETE: 0, INSERT: 0)
//================================================================================
select thisrowindex as 'Index' from commentsArray_ where ['ID'] = id end;
select * from commentsArray_ where thisrowindex <= r[1]['Index'] and thisrowindex >= r[0]['Index'] end;
select * from Tabs where thisrowindex <> ind end;
sselect distinct ['ID'] from commentsArray_ where ['ID'] >= 0 end;
sselect ['obj'] from Tabs end;
sselect ['FileName'] from zipfile.Files() where AnsiStartsText('word/media/image', ['FileName']) end;
sselect ['FileName'] from zipfile_.Files() where AnsiStartsText('word/media/image', ['FileName']) end;
sselect ['FileName'] from zipfile_.Files() where AnsiStartsText('word/' $ headerFooter, ['FileName']) end;
vselect countof( ['FileName'] ) from docx.Zip().Files() where AnsiStartsText('word/embeddings/Workbook', ['FileName']) end;
update pArr set ['pIndex'] = i end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/TSOffice/funcext/TSOffice/TSDocxFile.tsf
// 共 1 条语句 (SELECT: 0, SSELECT: 0, VSELECT: 1, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
vselect thisrowindex from files where ['FileName']=file end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/TSOffice/funcext/TSOffice/TSUtils/NodeInfo.tsf
// 共 4 条语句 (SELECT: 2, SSELECT: 2, VSELECT: 0, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
select * from children where lName = lowerCase(['field']) end;
select * from children where lName = lowerCase(['field']) end;
sselect * from attrs where lName = lowerCase([0]) end;
sselect * from attrs where lName = lowerCase([0]) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/TSOffice/funcext/TSOffice/document/TDocxChart.tsf
// 共 1 条语句 (SELECT: 0, SSELECT: 0, VSELECT: 1, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
vselect countof( ['FileName'] ) from docx.Zip().Files() where AnsiStartsText('word/charts/chart', ['FileName']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/TSOffice/funcext/TSOffice/document/TDocxCopy.tsf
// 共 3 条语句 (SELECT: 0, SSELECT: 3, VSELECT: 0, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
sselect ['FileName'] from zip.Files() where AnsiStartsText('word/' $ tar_prefix, ['FileName']) end;
sselect ['FileName'] from zip.Files() where AnsiStartsText('word/media/image', ['FileName']) end;
sselect ['FileName'] from zip.Files() where AnsiStartsText('word/charts/chart', ['FileName']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/TSOffice/funcext/TSOffice/worksheet/xlsxImage.tsf
// 共 1 条语句 (SELECT: 0, SSELECT: 1, VSELECT: 0, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
sselect ['FileName'] from zipfile_.Files() where AnsiStartsText('xl/media/image', ['FileName']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/TSOffice/funcext/TSOffice/worksheet/xlsxWorkBook.tsf
// 共 6 条语句 (SELECT: 1, SSELECT: 2, VSELECT: 3, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
select * from sheetNames_ where thisrowindex <> ind end;
sselect ['FileName'] from files where AnsiContainsStr(['FileName'], 'tables/table') end;
sselect ['name'] from sheetNames_ end;
vselect maxof(['sheetId']) from sheetNames_ end;
vselect thisrowindex from files where ['FileName'] = oldname end;
vselect maxof(['sheetId']) from sheetNames_ end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/other/TS_Excel.tsf
// 共 1 条语句 (SELECT: 0, SSELECT: 1, VSELECT: 0, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
sselect length(str2array(thisrow, "@"))-1 from arr order by length(str2array(thisrow, "@")) desc end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/other/sysconfdb.tsf
// 共 17 条语句 (SELECT: 12, SSELECT: 1, VSELECT: 0, UPDATE: 2, DELETE: 1, INSERT: 1)
//================================================================================
select ['TSID'],['CID'],['CNAME'],['KEY_NAME'] FROM sqltable 'ts_sys_config' of GetDBAlias() where ["PID"] = arr['pid'] and (['CNAME'] = arr['cname'] or ['KEY_NAME'] = arr['key_name']) end;
select ['cid'],['pid'],['cname'],['c_type'],['key_name'],['c_remarks'],['created_name'],['created_time'],setEncodeRSA(['c_type'], ['value_s']) AS 'value_s',['value_list'] from arr end;
select * from data where ['PID'] = '0' AND ['KEY_NAME'] = RootKey END;
Select * from data where ['PID'] = d1[0]['CID'] END;
Select * from d2 where ['KEY_NAME'] = NodeKeyArr[i] END;
select * from data where ['PID'] = '0' AND ['KEY_NAME'] = RootKey END;
Select * from data where ['PID'] = d1[0]['CID'] END;
Select * from d2 where ['KEY_NAME'] = NodeKeyArr[i] END;
select * from data where ['CID'] = cid end;
select * from data where ['PID'] = cid end;
select ['CNAME'] as "配置名称",
['KEY_NAME'] as "配置KEY",
GetConfigurationKey(['C_TYPE'],['VALUE_S'],['VALUE_LIST']) as "配置值",
['C_TYPE'] as "配置类型",
['C_REMARKS'] as "备注",
GetConfigurationValueList(['C_TYPE'],['VALUE_LIST']) as "配置可选值" from data where ['CID'] = cid end;
select * from data where ['PID'] = cid end;
sselect ['VALUE'] from d2 where ['KEY'] in t1 end;
Update sqltable 'ts_sys_config' of GetDBAlias() set ['PID'] = arr['pid'],
['CNAME'] = arr['cname'],
['KEY_NAME'] = arr['key_name'],
['VALUE_S'] = arr['value_s'],
['C_TYPE'] = arr['c_type'],
['C_REMARKS'] = arr['c_remarks'],
['UPDATED_BY'] = arr['updated_by'],
['VALUE_LIST'] = arr['value_list'],
['UPDATED_TIME'] = arr['updated_time'] where ['CID'] = arr['cid'] end;
Update sqltable 'ts_sys_config' of GetDBAlias() set ['VALUE_S'] = arr['value_s'],
['UPDATED_BY'] = arr['updated_by'],
['UPDATED_TIME'] = arr['updated_time'] where ['CID'] = arr['cid'] end;
DELETE FROM sqltable 'SELECT * FROM ts_sys_config' of GetDBAlias() where ["CID"] IN arr ;
Insert INTO sqltable 'ts_sys_config' of GetDBAlias() arr;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/other/tgenreport.tsf
// 共 21 条语句 (SELECT: 10, SSELECT: 2, VSELECT: 3, UPDATE: 6, DELETE: 0, INSERT: 0)
//================================================================================
select * from t order by ["名称"] end;
select * from r where not (['did'] in (sselect ['ID'] from existsDidArr end)) end;
select * from preData where ['EID']= r[i]['EID'] end;
select * from preData where ['EID']= r[i]['EID'] end;
select * from wordConf where ['Value']=reportInfo['TType'] end;
select [xName] from data end;
select * from xData join data on [1].[xName] = [2].[xName] end;
select [1],[3] from mgConf where [1]=num_row and [1]<>[3] and ([1]= num_colunm or [3]= num_colunm) end;
select ['doc_order_no'],trim(['org_content']) as 'org_content' from reportData where ['type'] = 'title' end;
select * from fzhTable where ['doc_order_no'] like '^'$ZXCWBB end;
select * from r where not (['did'] in (sselect ['ID'] from existsDidArr end)) end;
sselect distinct ['DATA_ID'] from EArr where ['type']='pdf' end;
vselect ['PRE_DATA'] from rpt_latestData where ['EID']= EArr[j]['EID'] and ['PRE_TYPE']= EArr[j]['type'] end;
vselect countof( * ) from fuZhuHaoTable where ['org_content'] like '转型前' end;
vselect ['doc_order_no'] from fzhTable where ['org_content'] = title end;
update r set ['did']= GenDataId(['EID'],productionID,pubDate,init_begindate,classify) end;
update r set ['DATASOURCE']= class(Element).dataSource_autoInherit() where not (['EID'] in array('20150512171332212_95089541912828646461640993495')) end;
update r set ['DATA_INIT_STATE']= 4 where not (['EID'] in array('20150512171332212_95089541912828646461640993495','20150304151252139_31808466412173073041697290538')) end;
update EArr set ['content'] = ['doc_order_no']$' '$['content'] where ['doc_order_no'] and pos('、',['doc_order_no'])=0 end;
update EArr set ['content'] = ['doc_order_no']$['content'] where ['doc_order_no'] and pos('、',['doc_order_no'])<>0 end;
update reportConf set ['content'] = updateFuZhuHaoSub(fuzhuhaoHash,['content']) ,['Data'] = updateFuZhuHaoSub(fuzhuhaoHash,['Data'])
where ['ename'] in array('资产负债表','利润表') and istable(['Data']) end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/tsword/ts_word.tsf
// 共 1 条语句 (SELECT: 0, SSELECT: 1, VSELECT: 0, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
sselect thisrow from rules where thisrow <> "" end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/tsword/tsword_report.tsf
// 共 2 条语句 (SELECT: 1, SSELECT: 0, VSELECT: 0, UPDATE: 1, DELETE: 0, INSERT: 0)
//================================================================================
select [1],[3] from mgConf where [1]=num_row and [1]<>[3] and ([1]= num_colunm or [3]= num_colunm) end;
update cellAlignConf set ["horizAlign"] = "left" end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/word2arr/helpdoctools.tsf
// 共 29 条语句 (SELECT: 27, SSELECT: 0, VSELECT: 2, UPDATE: 0, DELETE: 0, INSERT: 0)
//================================================================================
select ["node_id"],["parent_id"],["node_name"],"sys" as "created_by","sys" as "updated_by",["node_order"],["node_type"],["bus_key"],1 as "is_valid" from data end;
select ["node_id"],(["content"] + ".md") as "content","sys" as "created_by",["describe"] from data where ["describe"] <> "" and ["describe"] <> nil end;
select ["node_id"],["content"],["created_by"] from tmp_contentdata end;
select ["node_id"],["title"] from data where ["node_id"] = id end;
select ["node_id"],["title"] from data where ["node_id"] = id or ["title"] = name end;
select ["node_id"], ["parent_id"], ["node_order"], ["node_type"], ["bus_key"], ["node_name"] AS "title" from MenuData ORDER BY ["node_order"] end;
select * from MenuData where ["node_id"] = id end;
select ["node_id"], ["parent_id"], ["node_order"], ["node_type"], ["bus_key"], ["node_name"] AS "title" from MenuData ORDER BY ["node_order"] end;
select * from MenuData where ["node_id"] = id end;
select ["node_id"] AS "value", ["parent_id"], ["node_name"] AS "label", ["node_order"] from MenuData end;
select ["value"], ["label"] from MenuData where ["parent_id"] = id end;
select ["node_id"], ["parent_id"], ["node_order"], ["node_type"], ["bus_key"], ["node_name"] AS "title" from MenuData ORDER BY ["node_order"] end;
select *, 1 as "expand" from MenuData where ["parent_id"] = id end;
select * from MenuData where ["parent_id"] = nodeData[i]["value"] ORDER BY ["node_order"] end;
select * from MenuData where ["parent_id"] = nodeData[i]["node_id"] ORDER BY ["node_order"] end;
select * from MenuData where ["parent_id"] = nodeData[i]["node_id"] ORDER BY ["node_order"] end;
select ["node_id"], ["parent_id"], ["node_name"] AS "title" from MenuData where ["node_id"] = id end;
select ["node_id"], ["parent_id"], ["node_name"] AS "title", ["bus_key"] from MenuData where ["parent_id"] = id end;
select ["node_id"],["node_name"] AS "title" from res["data"] end;
select ["node_id"], (["node_order"] + 1) AS "node_order" from MenuList where ["node_order"] >= index ORDER BY ["node_order"] end;
select ["node_id"] from oldMenuData where ["parent_id"] = pid end;
select ["node_id"] from oldMenuData where ["parent_id"] = pid and ["title"] = newMenuData[i]["title"] and IsEmpty(["bus_key"]) end;
select ["node_id"], ["parent_id"], ["node_order"], ["node_type"], ["bus_key"], ["node_name"] AS "title" from res["data"] ORDER BY ["node_order"] end;
select ["node_id"], ["parent_id"], ["title"] from MenuData where ["node_id"] = nodeId end;
select ["node_id"], ["parent_id"], ["node_order"] from MenuData where ["parent_id"] = nodeData[0]["node_id"] ORDER BY ["node_order"] end;
select ["node_id"],["node_name"] AS "title" from res["data"] end;
select ["parent_id"],["node_id"],["node_order"] from res["data"] where ["node_id"] <> param["bid"] end;
vselect ["content"] from menuList where ["node_id"] = pid end;
vselect ["node_order"] from res["data"] ORDER BY ["node_order"] desc end;
//================================================================================
// 文件: /mnt/c/Programs/Tinysoft/TSLGen2/funcext/word2arr/sethelptreedocdb.tsf
// 共 4 条语句 (SELECT: 0, SSELECT: 0, VSELECT: 0, UPDATE: 1, DELETE: 2, INSERT: 1)
//================================================================================
update sqltable "ts_helpDocMenu" of FDBAlias set ["is_valid"] = 0 where ["node_id"] in data end;
delete from sqltable "ts_helpDocContent" of FDBAlias where ["node_id"] in data;
delete from sqltable "ts_helpDocMenu" of FDBAlias where ["node_id"] in data;
insert into sqltable tableName of FDBAlias data[i:i+999-1];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,11 +3,11 @@
echo "=== TSF 基础语法验证 ==="
echo "步骤1: 生成解析器"
tree-sitter generate
if [ $? -ne 0 ]; then
echo "❌ 生成解析器失败"
exit 1
fi
# tree-sitter generate
# if [ $? -ne 0 ]; then
# echo "❌ 生成解析器失败"
# exit 1
# fi
echo -e "\n步骤2: 测试最简单的完整结构"

View File

@ -0,0 +1,4 @@
<?tslx>
aa<?=ids_version()?>"
src='js?v=<?=ids_version()?>'
style="display: none;">

View File

@ -0,0 +1,201 @@
// TSF 变量声明测试代码示例
// 用于测试 var, static, global, const 语法
//================================================================================
// 1. var 声明测试
//================================================================================
// 保留关键字
do := 13;
type := "1234";
in := f2();
class := defff;
delete := +inf;
// 基本声明
var simple;
var withType: integer;
var withInit := 42;
var complete: real := 3.14;
// 多变量声明
var a, b, c;
var x, y, z: integer;
var m, n := 100;
var p, q, r: string := "default";
// 数组类型
var intArray: array of integer;
var stringArray: array of string := null;
var matrix: array of real;
var tensor: array of float;
// 复杂类型名
var list: System.Collections.List;
var dict: System.Collections.Generic.Dictionary_string_int;
var custom: MyNamespace.MyClass.NestedType;
// 复杂初始化器
var fromFunc := getValue();
var fromExpr := (a + b) * c;
var fromArray := array(1, 2, 3, 4, 5);
var fromCall := createObject("param1", 123, true);
//================================================================================
// 2. static 声明测试
//================================================================================
// 基本声明
static counter;
static typed: integer;
static initialized := 0;
static full: string := "static";
// 多变量声明
static s1, s2, s3;
static sx, sy: real;
static sa, sb := 999;
static sp, sq, sr: boolean := false;
// 静态数组
static cache: array of object;
static buffer: array of byte := null;
static lookupTable: array of integer := createTable();
// 复杂类型和初始化
static singleton: MyClass := MyClass.getInstance();
static config: dictionary := loadConfig("app.config");
static handler: EventHandler := lambda(e) { handleEvent(e); };
//================================================================================
// 3. global 声明测试
//================================================================================
// 基本声明
global appConfig;
global settings: object;
global database := null;
global connection: DBConnection := connect();
// 多变量声明
global g1, g2;
global gx, gy, gz: any;
global ga, gb := "global";
global gp, gq: integer := 0;
// 全局数组和集合
global users: array of User;
global cache: dictionary := createDictionary();
global eventQueue: array of Event := array();
// 复杂全局变量
global logger: ILogger := createLogger("app.log");
global threadPool: ThreadPool := ThreadPool(4);
global serviceLocator: ServiceLocator := ServiceLocator.default;
//================================================================================
// 4. const 声明测试
//================================================================================
// 数值常量
const PI = 3.14159265359;
const E = 2.71828;
const MAX_INT: integer = 2147483647;
const MIN_INT: integer = -2147483648;
// 字符串常量
const VERSION = "1.0.0";
const APP_NAME: string = "MyApplication";
const PATH_SEPARATOR = "/";
const EMPTY = "";
// 布尔常量
const DEBUG = true;
const RELEASE: boolean = false;
const ENABLED = true;
// 表达式常量
const BUFFER_SIZE = 1024 * 8;
const TIMEOUT = 60 * 1000;
const MASK = 0xFF;
const COMBINED = "Hello" $ " " $ "World";
// 特殊值常量
const NULL_VALUE = null;
const DEFAULT_PORT = 8080;
const MAX_RETRIES: integer = 3;
//================================================================================
// 5. 混合声明测试
//================================================================================
// 同一作用域内的多种声明
var localVar: integer;
static sharedStatic := 0;
global globalResource: Resource;
const CONFIG_FILE = "config.json";
// 连续声明
var v1; var v2; var v3;
static s1; static s2;
global g1; global g2;
const C1 = 1; const C2 = 2;
//================================================================================
// 6. 特殊字符和命名测试
//================================================================================
// 下划线开头
var _private;
static __internal: integer;
const _CONSTANT = 100;
// 数字结尾
var var1, var2, var3;
static counter_2024;
global data_123: array of integer;
const VERSION_2 = "2.0";
// Unicode标识符如果支持
var 变量: string;
static 静态变量 := 0;
global 全局设置: dictionary;
const 常量值 = "中文";
//================================================================================
// 7. 边界和错误情况(这些应该导致解析错误)
//================================================================================
// 以下是故意的错误示例,用于测试错误处理
// var x = 10; // 错误:应该使用 :=
// const C := 5; // 错误const应该使用 =
// const EMPTY_CONST; // 错误const必须有值
// var ; // 错误:缺少标识符
// var x: integer // 错误:缺少分号
//================================================================================
// 8. 格式化和空白测试
//================================================================================
// 紧凑格式
var a,b,c:integer:=0;
static x,y:real;
global m:string:="test";
const K=999;
// 宽松格式
var spaced , names : type := value ;
static wide : integer ;
global lots , of , spaces ;
const SPACEY = "value" ;
// 多行格式
var
multiline,
declaration,
test: integer
:= 0;
//================================================================================
// 测试结束
//================================================================================

View File

@ -0,0 +1,607 @@
uid := OnlineUserId();
if not uid then
begin
rqUrl := createObject("TWebRequest").url;
return CreateObject('TWebResponse').SendRedirect('/website/security/login.tsl?url='$base64encode(rqUrl)$'&e=base64');
end;
isAdmin := class(IDS_ResponseRightDB).isAdmin(uid);
// 公式操作权限
opRight := 0;
uinfo := class(IDS_ResponseRightDB).getUserInfoByRoleName("勾稽公式操作权限");
if isAdmin or (istable(uinfo) and uid in (sselect ["MEMBERID"] from uinfo end)) then
begin
opRight := 1;
end;
classify := Q("classify") ?: ''; //业务类型
tid := Q("tid") ?:''; // 模板id
key := Q("key") ?:''; // 公式id
onlyFormula := Q('onlyFormula')?:''; //仅显示当前公式相关
zhiBiaoJson := ''; // 指标json串
zhiBiaoArr := array(); // 指标数据
GJGongShi := ''; // 勾稽公式
onlyFormulaCheck := ''; // 仅显示当前公式相关选择
GJWuChaZhi := ""; // 容差范围:默认万分之一
GJBeiZhu := "";
checkedClassifyArr := array();
elements := Q("elements") ?: '';
xbrlFormulaSign := class(xbrl_formula).getAllFormulaSign();
xbrlFormulaOperatorSign := class(xbrl_formula).operatorSign();
xbrlFormulaFunctionSign := class(xbrl_formula).functionSign();
xbrlFormulaAttributeSign := class(xbrl_formula).attributeSign();
xbrlFormulaCondition := class(xbrl_formula).formulaCondition();
ele := createobject("ids_element");
// 获取报告参数属性标签集合
DictionaryByReport := ele.DictionaryByReport();
xbrlFormulaReportAttrSign := select distinct ["标签中文名称"] as "key",["标签中文名称"] as "title",["标签ID"] as "e_id" from DictionaryByReport where ["标签英文名称"]<>"GongGaoXinXi_#_ZTGG" end;
formulaAttrs := array(
"is_sanxing": -1,
"is_fenji": -1,
"is_shangshi": -1,
"is_etf": -1,
"is_etf_lianjie": -1,
"is_zhishu": -1,
"is_jiji": -1,
"is_fof": -1,
"is_mom": -1,
"is_xinjijin": -1,
"is_faqishi": -1,
"fundtype": array2str(xbrlFormulaCondition["基金类别"]["default"], ";"),
"guzhifangshi": array2str(xbrlFormulaCondition["估值方式"]["default"], ";"),
"yunzuofangshi": array2str(xbrlFormulaCondition["运作方式"]["default"], ";"),
"adaptType":"limit",
"fundid": "",
);
errorMsg := '';
//修改公式
if key then
begin
obj := createObject("ids_xbrl_expr");
obj.exprAttr(key);
GJWuChaZhi := obj.GJWuChaZhi ?: "";
(*
if istable(obj.GJZhiBiao) then
begin
GJGongShi := obj.GJZhiBiao;
if istable(obj.GJXiangMu) then
begin
for i,v in obj.GJXiangMu do
update GJGongShi set thisrow = v[1] where thisrow = v[0] end;
end;
GJGongShi := sselect "[[" $ thisrow $ "]]" as thisrow from GJGongShi end;
GJGongShi := array2str(GJGongShi, "");
end;
*)
gongShi := obj.GJGongShi;
if gongShi then
begin
GJGongShi := _getSplitFormula(gongShi);
GJBeiZhu := obj.GJBeiZhu ?: "";
formulaAttrs := array(
"is_sanxing": obj.isSanXing,
"is_fenji": obj.isFenJi,
"is_shangshi": obj.isShangShi,
"is_etf": obj.isETF,
"is_etf_lianjie": obj.isETFLianJie,
"is_zhishu": obj.isZhiShu,
"is_jiji": obj.isJianJuJiJi,
"is_fof": obj.isFOF,
"is_mom": obj.isMOM,
"is_xinjijin": obj.isXinJiJin,
"is_faqishi": obj.isFaQiShi,
"fundtype": obj.GJFanWei,
"guzhifangshi": obj.GuZhiFangShi,
"yunzuofangshi": obj.YunZuoFangShi,
"adaptType":obj.adaptType,
"fundid": obj.fundid,
);
zhiBiaoArr := obj.GJZhiBiao;
if istable(zhiBiaoArr) then
zhiBiaoJson := class(TSTool).Arr2Json(zhiBiaoArr);
// json 串内有单引号需转义
if ansipos("'", zhiBiaoJson) then
zhiBiaoJson := ansireplacetext(zhiBiaoJson,"'","\\'");
if ansipos("'", GJGongShi) then
GJGongShi := ansireplacetext(GJGongShi,"'","\\'");
if obj.classify then
classifyArr := str2array(obj.classify, ";");
if not classify and classifyArr then
begin
classify := classifyArr[0];
checkedClassifyArr &= classifyArr;
end else
begin
if classify then
checkedClassifyArr &= str2array(classify, ";");
if classifyArr then
checkedClassifyArr &= classifyArr;
end;
onlyFormulaCheck := '<div class="filter-tool">'
$ '<label class="ivu-checkbox-wrapper ivu-checkbox-small ivu-checkbox-border">'
$ '<span class="ivu-checkbox '$(onlyFormula?'ivu-checkbox-checked':'')$'">'
$ '<span class="ivu-checkbox-inner"></span>'
$ '<input type="checkbox" class="js-onlyFormula ivu-checkbox-input" '$(onlyFormula?'checked':'')$'>'
$ '</span> 仅显示当前公式相关</label>'
$ '</div>';
end else
begin
errorMsg := '<div class="alert alert-danger">无效公式,请确认该公式是否删除!</div>';
end;
end else
begin // 新增公式
if classify then
begin
if classify = "SEMIYEAR_REP" or classify = "YEAR_REP" then
checkedClassifyArr := array("SEMIYEAR_REP", "YEAR_REP");
else
checkedClassifyArr &= str2array(classify, ";");
end;
end;
checkedClassifyJson := '';
if istable(checkedClassifyArr) then
checkedClassifyJson := class(TStool).Arr2Json(distinctstr(checkedClassifyArr)); // 默认选中的业务
businessOfUser := class(IDS_BusinessDB).getKeyByUID(uid);
classifyList := class(ts_switch).getSwitchApplyClassify("report_audit_view");
classifyData := select ["BUSINESS_KEY"], ["BUSINESS_NAME"] from businessOfUser where ["BUSINESS_KEY"] in classifyList order by ["BUSINESS_NAME"] end;
classifyList := array(("BUSINESS_KEY": "", "BUSINESS_NAME": "-")) union classifyData;
if not tid then
begin
templateList := select ["TID"], ["TNAME"] from class(IDS_TemplateDB).getTemplateByClassify(classify) end;
tid := _getTemplateID(templateList);
end else
begin
templateList := select ["TID"], ["TNAME"] from class(IDS_TemplateDB).getTemplateByClassify(classify) end;
end;
templateList := array(("TID": "", "TNAME": "-")) union templateList;
temptList := '';
dataContent := '';
emListHtml := '';
if tid then
begin
// 元素标签相关信息
r := class(xbrl_formula).getElementTagInfo(tid); // 根据模板获取指标标签信息
// 根据业务获取业务相关标签
baseData := class(xbrl_formula).getBasicIndexByClassify(classify);
// 模板相关元素
emList := class(IDS_RformalDB).getTempleteElementList(tid);
// 标签数据字典
Dictionary := ele.Dictionary();
Dictionary := select ["标签ID"] as "E_ID",["标签中文名称"] as "E_CN_NAME" from Dictionary end;
emArr := array();
cnArr := array();
rt := select distinct ["E_ID"], ["E_CN_NAME"] from r end;
if istable(baseData) then
rt := rt union2 baseData union2 Dictionary;
for i := 0 to length(rt) - 1 do
begin
emArr[rt[i]["E_ID"]] := rt[i]["E_CN_NAME"];
cnArr[rt[i]["E_CN_NAME"]] := rt[i]["E_ID"];
end;
// 元素标签
(*elementArr := class(IDS_ElementDB).getElementNameAndXbTag();
elementTagArr := array();
vv := 0;
for i := 0 to length(elementArr) - 1 do
begin
if not elementArr[i]["XBRL_TAG"] then continue;
elementTagArr[elementArr[i]["NAME"]] := elementArr[i]["NAME"];
end;
*)
// xbElementJson := class(TStool).Arr2Json(elementTagArr); // 含xbtag标签的元素用来匹配元素这个函数
xbElementJson := "";
elementJson := class(TStool).Arr2Json(emArr); // 元素id标签对照 "1702":"本报告期基金份额总额", ...
cn_nameJson := class(TStool).Arr2Json(cnArr); // 中文标签对照 {"本报告期基金份额总额":"1702", ...
// \转义
if ansipos("\\", elementJson) then
elementJson := AnsiReplaceText(elementJson, "\\", "\\\\");
//文本类的e_id
eTxt := select ["E_ID"],["EID"],["E_CN_NAME"],["ISEM"] from r where ["ISEM"] = 1 end;
rela_eid := array();
if istable(zhiBiaoArr) and istable(r) then
begin
// 获取仅在公式指标中的相关元素
rela_eid := sselect distinct ["EID"] from r where ["E_ID"] in zhiBiaoArr end;
end;
if istable(emList) then
begin
filterList := array(("title": "元素筛选", "key": "ELEMENT-NAME", "option": emList, "mulitList": "mulitEm"));
emListHtml := '<div class="filter-tool">';
emListHtml += checkFilter(filterList, 'formulae-filter', '');
emListHtml += '</div>';
end;
// 模板元素数据
data := class(xbrl_formula).getTemplateData(tid);
if istable(data) then
begin
elementArr := array();
if elements then
elementArr := str2array(elements, ";") ;
if istable(rela_eid) and onlyFormula then
begin
if istable(elementArr) then
rela_eid &= elementArr;
data := select * from data where ["EID"] in rela_eid or ["TYPE"] = "title" end;
data := class(ReportData).deleteEmptyChapter(data); // 去除空章节
end
else if istable(elementArr) then
begin
data := select * from data where ["EID"] in elementArr or ["TYPE"] = "title" end;
data := class(ReportData).deleteEmptyChapter(data); // 去除空章节
end;
temptList := class(xbrl_formula).getTemplateHtml(data);
end;
end else
begin
data := array();
temptList := '';
end;
<?tslx>
<!DOCTYPE html>
<html>
<head>
<title>勾稽公式构建</title>
<meta http-equiv="Content-Type" content="text/html;charset=gb2312" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" type="text/css" href="/resource/iview/iview.css?v=<?=ids_version()?>" />
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/jquery-ui/jquery.ui.all.css">
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/bootstrap/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/bootstrap/button.css"/>
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/font-awesome.css" />
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/style.css?v=<?=ids_version()?>" />
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/report.css?v=<?=ids_version()?>" />
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/live_report.css?v=<?=ids_version()?>" />
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/xbrl_data_audit_view.css?v=<?=ids_version()?>" />
<link rel="stylesheet" type="text/css" href="/resource/fundids/css/formula.css?v=<?=ids_version()?>" />
<link rel="stylesheet" type="text/css" href="/resource/codemirror/lib/codemirror.css" />
<script type="text/javascript" src="/resource/js/jquery-1.8.3.min.js" ></script>
<script type="text/javascript" src="/resource/js/jquery-migrate-1.8.3.min.js" ></script>
<script type="text/javascript" src="/resource/js/jquery-ui-1.10.2.custom.min.js"></script>
<script type="text/javascript" src="/resource/vue/vue.min.js" charset="UTF-8"></script>
<script type="text/javascript" src="/resource/iview/iview.min.js" charset="UTF-8"></script>
<script type="text/javascript" src="/resource/fundids/js/tsalert.js"></script>
<script type="text/javascript" src="/resource/fundids/js/edittable.js"></script>
<script type="text/javascript" src="/resource/fundids/js/global.js?v=<?=ids_version()?>"></script>
<script type="text/javascript" src="/resource/fundids/js/autoTextarea.js?v=<?=ids_version()?>"></script>
<script type="text/javascript" src="/resource/js/dropdown-button.js"></script>
<script type="text/javascript" src="/resource/fundids/js/dm.ext.js?v=<?=ids_version()?>"></script>
<script type="text/javascript" src="/resource/fundids/js/live_report.js?v=<?=ids_version()?>"></script>
<script type='text/javascript' src='/resource/fundids/js/xbrl_formula_page.js?v=<?=ids_version()?>'></script>
<script type="text/javascript" src="/resource/fundids/js/xbrl_formula_view.js?v=<?=ids_version()?>"></script>
<script type="text/javascript" src="/resource/fundids/js/check_xbrldata.js?v=<?=ids_version()?>"></script>
<script type="text/javascript" src="/resource/codemirror/lib/codemirror.umd.js"></script>
</head>
<body>
<div id="xbrl-formula-view-wrapper">
<div class="top-banner">
<div class="title-wrapper"></div>
<div class="tools-wrapper">
<div class="left-tools">
<div class="filter-tool">
<span>业务类型:</span>
<?=class(html).makeSelect(classifyList, ' class="formula-classify-select" ', classify, 'BUSINESS_NAME', 'BUSINESS_KEY')?>
</div>
<div class="filter-tool">
<span>模板:</span>
<?=class(html).makeSelect(templateList, ' class="formula-model-select" ', tid, 'TNAME', 'TID')?>
</div>
<?=emListHtml?>
<?=onlyFormulaCheck?>
<div id="page-params" class="filter-tool">
<input type="hidden" name="elements" value="<?=elements?>">
<input type="hidden" name="classify" value="<?=classify?>">
<input type="hidden" name="tid" value="<?=tid?>">
<input type="hidden" name="key" value="<?=key?>">
<input type="hidden" name="onlyFormula" value="<?=onlyFormula?>">
</div>
<div class="filter-tool">
<button class="btn btn-primary" @click="clickQueryBtn"> 查 询 </button>
</div>
</div>
<div class="right-tools">
<div class="filter-tool">
<?=key and opRight?'<button class="btn btn-primary btn-add-formulae" data-classify="'$classify$'"> + 新增 </button>':''?>
</div>
</div>
</div>
</div>
<div class="aui-page-panel-inner">
<!--目录-->
<div class="aui-page-panel-nav">
<div class="report_navigator">
<div class="filter-panel">导航栏</div>
<?=temptList?>
<div class="bottomth"></div>
</div>
</div>
<div class="aui-page-panel-content row-fluid">
<!--数据-->
<div class="model-data-area js-rpt-content">
<div class="report_page data_page">
<?tsl
if errorMsg then
echo errorMsg;
sysparams["xbrl_formula_view"] := true;
for i := 0 to length(data) - 1 do
begin
textClass := '';
zhibiaoSpan := '';
echo '<div class="element-data-container">';
if data[i]['EID'] then
begin
editBtn := '';
zhibiaoid := '';
if istable(eTxt) then
begin
try
zhibiaoid := sselect ["E_ID"] from eTxt where ["EID"] = data[i]["EID"] and ["ISEM"] = 1 end[0] ?:'';
if zhibiaoid then
zhibiaoSpan := '<span class="zhibiaoid" data-zhibiaoid="'$zhibiaoid$'"> ['$zhibiaoid$']</span>';
except
zhibiaoSpan := '';
end;
end;
(*
if data[i]["TYPE"] = 'txt' then
begin
textClass := 'hasTextSample';
if zhibiaoid then
editBtn := '<div class="text-right element-data-actions">'
$ '<button class="btn btn-mini edit-txt-index" data-zhibiaoid="'$zhibiaoid$'" title="设置文本变量指标">指标</button>'
$ '<button class="btn btn-mini edit-template-txt" title="修改文本样例">样例</button>'
$ '<button class="btn btn-mini btn-formula-list" title="相关公式">公式</button>'
$ '</div>';
end else
begin
editBtn := '<div class="text-right element-data-actions">'
$ '<button class="btn btn-mini btn-formula-list" title="相关公式">公式</button>'
$ '</div>';
end;
*)
editBtn := '<div class="text-right element-data-actions">'
$ '<button class="btn btn-mini btn-formula-list" data-eid="'$data[i]['EID']$'" title="相关公式">公式</button>'
$ '</div>';
echo '<div class="element-bar"><span class="element-name" data-eid="'$data[i]["EID"]$'"><i class="'$getTypetag(data[i]["TYPE"])$'"></i> '$data[i]["NAME"]$zhibiaoSpan$'</span>'$editBtn$'</div>';
end;
echo '<div class="row">
<div class="element-data-content '$textClass$'">';
class(xbrldataauditreport).rpt_show_data(data[i], true);
echo '</div></div>';
echo '</div>';
end;
?>
</div>
</div>
<!--公式区-->
<div class="formula-edit-area" v-cloak>
<tabs :animated="true" size="small">
<tab-pane label="公式编辑">
<div class="formula-tools">
<div class="formula-tools-list">
<div v-for="(item, index) of toolListData" :key="index" class="formula-tools-list-item">
<i-button type="small" class="formula-tools-item" @click="insertSign(item)" :title="item.info">{{ item.label }}</i-button>
</div>
</div>
<div class="formula-tools-list" style="display: none;">
<div v-for="(item, index) of reportAttrListData" :key="index" class="formula-tools-list-item">
<i-button type="small" class="formula-tools-item" @click="insertSign(item)" :title="item.info">{{ item.label }}</i-button>
</div>
</div>
</div>
<div class="formula-editor">
<editor-component ref="editor" :editor-data.sync="formula" :completion-options="formulaRule" :vaild-params="vaildParams" :invalid-param-msg="invalidParamMsg" symbol-sets="" />
</div>
</tab-pane>
<tab-pane label="公式属性">
<div class="formula-tools">
<div class="formula-attrs">
<div class="formula-attrs-list">
<p>适用业务:</p>
<checkbox-group v-model="formulaClassify" class="formula-classify" size="small">
<?tsl
for i,v in classifyData do
echo '<div class="formula-classify-item"><checkbox label="'$v["BUSINESS_KEY"]$'">'$v["BUSINESS_NAME"]$'</checkbox></div>';
?>
</checkbox-group>
</div>
<?tsl
for i,v in xbrlFormulaCondition do
begin
if not pos("IS_", v["name"]) then
begin
echo '<div class="formula-attrs-list"><p>'$i$'</p>&nbsp;<checkbox-group v-model="'$lowerCase(v["name"])$'" class="formula-prod-attr" size="small">';
for si,sv in v["value"] do
echo '<div class="formula-prod-attr-item"><checkbox label="'$sv$'">'$sv$'</checkbox></div>';
echo '</checkbox-group></div>';
end;
end;
?>
<div class="formula-attrs-list">
<p>其它属性:</p>
<div class="formula-prod-attr ">
<?tsl
otherAttrs := select * from xbrlFormulaCondition where pos("IS_", thisrow["name"]) end;
for i,v in otherAttrs do
begin
//echo '<div class="formula-prod-attr-item"><checkbox v-model="'$lowerCase(v["name"])$'" size="small">'$i$'</checkbox></div>';
echo
'<div class="formula-prod-attr-item">
<label>'$i$'</label>
<i-select placeholder="" v-model="'$lowerCase(v["name"])$'" style="width:70px; margin-left:4px;">
<i-option value=-1 key="">&nbsp</i-option>
<i-option value=1 key="1">是</i-option>
<i-option value=0 key="0">否</i-option>
</i-select>
</div>';
end;
?>
</div>
</div>
<div class="formula-attrs-list">
<p>基&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;金:</p>
<div class="formula-prod-attr">
<i-select placeholder="" v-model="adaptType" style="width:100px">
<i-option value="limit" key="1">仅适用</i-option>
<i-option value="defy" key="0">不适用</i-option>
<i-option value="extend" key="2">额外适用</i-option>
</i-select>
<input value="<?=formulaAttrs["fundid"]?>" type="text" class="form-control mul_produnct ui-autocomplete-input" autocomplete="off" placeholder="请输入主基金代码">&nbsp;&nbsp;<button type="button" title="基金选择" class="btn CheckfundList">...</button></span>
</div>
</div>
<div class="formula-attrs-list">
<p>容差范围:</p>
<input-number class="formula-wucha" :max="1" :min="0" :step="0.0001" v-model="formulaWucha" size="small" style="height:30px;"></input-number>
</div>
<div class="formula-attrs-list">
<p>备&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;注:</p>
<i-input class="formula-beizhu" v-model="beizhu" type="textarea" :autosize="{minRows: 2,maxRows: 3}" size="small" ></i-input>
</div>
</div>
</div>
</tab-pane>
<tab-pane label="数据字典"></tab-pane>
<?tsl
if opRight then
echo '<template #extra>
<i-button size="small" type="primary" :disabled="!formula" @click="saveFormula(\'save\')"> 保存 </i-button>
<i-button size="small" :disabled="!formula" @click="saveFormula(\'saveAS\')" title="另存为新的公式"> 另存 </i-button>
<i-button size="small" :disabled="!formula" @click="saveFormula(\'test\')" title="测试勾稽校验"> 校验 </i-button>
<i-button size="small" type="error" :disabled="!formula" @click="clearFormula"> 清空 </i-button>
</template>';
?>
</tabs>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var XBRL_FORMULA_SIGN = <?=class(TSTool).Arr2Json(xbrlFormulaSign)?>;
var XBRL_FORMULA_OPERATOR_SIGN = <?=class(TSTool).Arr2Json(xbrlFormulaOperatorSign)?>;
var XBRL_FORMULA_FUNCTION_SIGN = <?=class(TSTool).Arr2Json(xbrlFormulaFunctionSign)?>;
var XBRL_FORMULA_ATTRIBUTE_SIGN = <?=class(TSTool).Arr2Json(xbrlFormulaAttributeSign)?>;
var XBRL_FORMULA_CONDITION = <?=class(TSTool).Arr2Json(xbrlFormulaCondition)?>;
var XBRL_FORMULA_REPORTATTR_SIGN = <?=class(TSTool).Arr2Json(xbrlFormulaReportAttrSign)?>;
var elementJson = '<?=elementJson?>';
if(elementJson && !Array.isArray(elementJson)){
elementJson = JSON.parse(elementJson);
}
var xbElementJson = '<?=xbElementJson?>';
if(xbElementJson && !Array.isArray(xbElementJson)){
xbElementJson = JSON.parse(xbElementJson);
}
var cn_nameJson = '<?=cn_nameJson?>';
if(cn_nameJson && !Array.isArray(cn_nameJson)){
cn_nameJson = JSON.parse(cn_nameJson);
}
var zhiBiaoJson = '<?=zhiBiaoJson?>';
if(zhiBiaoJson && !Array.isArray(zhiBiaoJson)){
zhiBiaoJson = JSON.parse(zhiBiaoJson);
}
var GOUJIGONGSHI = '<?=GJGongShi?:""?>';
var FORMULAID = '<?=key?:""?>';
var formulaWucha = '<?=GJWuChaZhi?:""?>';
var formulaClassify = '<?=checkedClassifyJson?:""?>';
var formulaBeizhu = '<?=GJBeiZhu?:""?>';
<?tsl
for i,v in formulaAttrs do
Begin
// echo "var "$i$" = "$(v <> "" ? '"'$v$'"' : "false")$";";
echo "var "$i$" = '"$v$"';";
end;
?>
$(function(){
// 基金代码选择器
AddAutoComplete_mul(".mul_produnct","product","");
// 添加文本指标标签
addTextTag(cn_nameJson);
// 指标标签
addElementTag(elementJson, zhiBiaoJson);
});
</script>
</body>
</html>
<?tsl
function getTypetag(types);
begin
case types of
'txt': typetag := 'icon-text-width';
'table': typetag := 'icon-table';
'pic':typetag := 'icon-picture';
'complex': typetag := 'icon-list';
'mix':typetag := 'icon-th-large';
'radio': typetag := 'icon-ok-circle';
else
typetag := '';
end;
return typetag;
end;
function _getTemplateID(t);
begin
if not istable(t) then
return '';
t1 := sselect ["TID"] from t where LeftStr(["TNAME"], 2) = "普通" order by ["TNAME"],["IS_BASIC"] desc end;
if istable(t1) then
return t1[0];
t2 := sselect ["TID"] from t order by ["IS_BASIC"] desc end;
if istable(t1) then
return t2[0];
return '';
end;
function _getSplitFormula(f);
begin
f1 := f;
t := class(IDS_AuditExpr).analysisFormulae(f1);
item := array();
for i := 0 to length(t) - 1 do
begin
k := "[[REP_"$i$"]]";
item[i]:= array("v": "[["$t[i]$"]]", "k": k );
f := replaceStr(f, t[i], k );
end;
for i := 0 to length(item) - 1 do
begin
f := replaceStr(f, item[i]["k"], item[i]["v"]);
end;
attrArr := class(xbrl_formula).attributeSign();
for i := 0 to length(attrArr) - 1 do
begin
attr := attrArr[i]["key"];
if pos(attr, f) then
f := replaceStr(f, "("$attr$")", "]][[("$attr$")");
end;
f := replaceStr(f, "#", "]]#[[" );
return f;
end;

View File

@ -0,0 +1,7 @@
// function 公募基金简称(shortname);
function abc(shortname);
begin
中文 := 123;
pinfo := sysparams["productInfo"];
end;

View File

@ -1,46 +0,0 @@
root = true
[*]
charset = utf-8
[*.{json,toml,yml,gyp}]
indent_style = space
indent_size = 2
[*.js]
indent_style = space
indent_size = 2
[*.scm]
indent_style = space
indent_size = 2
[*.{c,cc,h}]
indent_style = space
indent_size = 4
[*.rs]
indent_style = space
indent_size = 4
[*.{py,pyi}]
indent_style = space
indent_size = 4
[*.swift]
indent_style = space
indent_size = 4
[*.go]
indent_style = tab
indent_size = 8
[Makefile]
indent_style = tab
indent_size = 8
[parser.c]
indent_size = 2
[{alloc,array,parser}.h]
indent_size = 2

View File

@ -1,41 +0,0 @@
* text=auto eol=lf
# Generated source files
src/*.json linguist-generated
src/parser.c linguist-generated
src/tree_sitter/* linguist-generated
# C bindings
bindings/c/** linguist-generated
CMakeLists.txt linguist-generated
Makefile linguist-generated
# Rust bindings
bindings/rust/* linguist-generated
Cargo.toml linguist-generated
Cargo.lock linguist-generated
# Node.js bindings
bindings/node/* linguist-generated
binding.gyp linguist-generated
package.json linguist-generated
package-lock.json linguist-generated
# Python bindings
bindings/python/** linguist-generated
setup.py linguist-generated
pyproject.toml linguist-generated
# Go bindings
bindings/go/* linguist-generated
go.mod linguist-generated
go.sum linguist-generated
# Swift bindings
bindings/swift/** linguist-generated
Package.swift linguist-generated
Package.resolved linguist-generated
# Zig bindings
build.zig linguist-generated
build.zig.zon linguist-generated

View File

@ -1,50 +0,0 @@
# Rust artifacts
target/
Cargo.lock
# Node artifacts
build/
prebuilds/
node_modules/
package-lock.json
# Swift artifacts
.build/
Package.resolved
# Go artifacts
_obj/
# Python artifacts
.venv/
dist/
*.egg-info
*.whl
# C artifacts
*.a
*.so
*.so.*
*.dylib
*.dll
*.pc
*.exp
*.lib
# Zig artifacts
.zig-cache/
zig-cache/
zig-out/
# Example dirs
/examples/*/
# Grammar volatiles
*.wasm
*.obj
*.o
# Archives
*.tar.gz
*.tgz
*.zip

View File

@ -1 +0,0 @@
ret := insert into sqltable tableName of FDBAlias data[i:i+999-1];

File diff suppressed because it is too large Load Diff

View File

@ -1,364 +0,0 @@
// Example TSF program demonstrating various language features
// Variable declarations
var x, y, z: int;
var name: string;
var matrix: array of real;
// Constants
const PI = 3.14159;
const MAX_SIZE: int = 100;
const GREETING = "Hello, World!";
// Global and static variables
global config: object;
static counter := 0;
// Basic arithmetic
x := 10;
y := 20;
z := x + y * 2; // z = 50
// String operations
name := "John" $ " " $ "Doe";
// Boolean operations
var isValid := x > 0 and y < 100;
var isReady := not (z = 0) or name <> "";
// Bitwise operations
var flags := 0b1010;
flags := flags .| 0b0101; // flags = 0b1111
flags := flags shl 2; // flags = 0b111100
// Matrix operations
var A, B, C: matrix;
C := A :* B; // Matrix multiplication
C := A :^ 2; // Matrix power
// Set operations
var set1, set2, result: set;
result := set1 union set2;
result := set1 intersect set2;
// Ternary operator
var max := x > y ? x : y;
var sign := x > 0 ? 1 : x < 0 ? -1 : 0;
// Prefix and postfix operators
++counter;
var oldValue := counter++;
// Augmented assignments
x += 10;
y *= 2;
flags .&= 0xFF;
set1 union= set2;
obj.a := 123;
obj["a"] := 456;
// Special values
var nothing := nil;
var infinity := +inf;
var negInfinity := -inf;
// array
arr := array();
arr := array(1, 2, 3);
arr := array(1, "abc", true, nil);
arr := array("key1": "value1", "key2": "value2");
arr := array((1, "a"), array(2, "b"));
arr := array(array(1, 2), array(3, 4));
arr := array(array(array(1, 2), array(3, 4)), array(array(5, 6), array(7, 8)));
arr := array(("key1": array(1, 2, 3)), ("key2": (4, 5, 6)));
arr := array("outer": array("inner1": 1, "inner2": 2), "data": array(1, 2, 3));
arr := array((array(1, 2), array("a", "b")), (array(3, 4), array("c", "d")));
arr := array("data": array((1, "first"), (2, "second")), "meta": array("count": 2, "type": "test"));
arr := array(var1, var2, array(var3, var4));
arr := array(func1(), func2(arg), array(func3(), func4(arg1, arg2)));
arr := array(func(), (a + b));
arr := array((x + y), z, (a * b));
arr := array(func(), (a + b), var1, (c * d));
arr := array((a, b, c), (x + y), ("key": "value"));
arr := array(1, func(), (expr), (a, b), "key": (value));
data := array(
"users": array(
("id": 1, "name": "Alice", "scores": array(85, 92, 78)),
("id": 2, "name": "Bob", "scores": array(91, 87, 95))
),
"metadata": array(
"total": 2,
"average": array(88.0, 89.5, 86.5),
"nested": array(
"level1": array(
"level2": array(1, 2, 3),
"level2b": (true, false, nil)
)
)
)
);
// 访问嵌套数据
first_user := data["users"][0];
first_score := first_user["scores"][0];
nested_value := data["metadata"]["nested"]["level1"]["level2"][1];
echo "First user score:", first_score;
echo "Nested value:", nested_value;
// Function calls (hypothetical functions)
process(data);
calculate(x: 10, y: 20, mode: "fast");
##process(data);
// Attribute access (hypothetical objects)
// var length := myString.length;
// config.settings.timeout := 3000;
// Array subscripting and slicing
// var first := array[0];
// var subArray := array[1:10];
// var tail := array[5:];
// Complex expressions with precedence
var result1 := 2 + 3 * 4 ^ 2; // 2 + 3 * 16 = 50
var result2 := (2 + 3) * 4 ^ 2; // 5 * 16 = 80
var result3 := not a > b and c <= d or e = f;
// Derivative operator (mathematical)
// var derivative := !f;
// Expression operators
// var reference := @value;
// var address := &variable;
{ This is a block comment
It can span multiple lines
and contain any text }
(* This is a nested comment
It can also span multiple lines
and is Pascal-style *)
// Chained comparisons
var inRange := 0 <= x <= 100;
var ordered := a < b < c;
// Nested assignments
var a := var b := var c := 0;
// Complex type specifications
var complexType: array of array of real;
var functionType: procedure of integer;
// const
const a: real = 10;
const b = "123";
// augmented_assignment
a += 10;
b += func();
c /= ma[1];
// return
return;
return 10;
return f(10);
// break
break;
// continue
continue;
// echo
echo abc $ def $ "123" $ 456, funcstr(), "\n";
// raise
raise abc() $ "123";
// inherited
inherited abc();
// new
obj := new abc();
// if
if condition then
f1();
else
raise "abc";
if condition1 then
begin
if f1() then echo 1;
else echo 2;
end
else if condition2 then
begin
echo 3;
end
else begin
echo 4;
end
// for
for i:=0 to 10 do
echo i, "\n";
for k,v in arr do
begin
echo "k = 0", "\n";
echo "v = 1", "\n";
end
// while
while true do
echo 123;
// repeat
repeat
echo 1;
a++;
until a > 10;
// case
case x of
1,2: return abc;
"acde": begin
return def;
end
else begin
a := 1;
return a;
end
end;
// try
try
a := 1;
except
a := 2;
end
// function
function foo(a, b, c): real
begin
end
function foo(a, b: integer; c: string);
begin
end;
function foo(a: boolean; b: integer; c: string): real;
begin
end
function foo(a: boolean = true; b: integer = 123);
begin
end;
// function pointer
pf := function(a, b)
begin
echo a;
end;
// type
type A = class
public
function create()
begin
end
function foo1(a: real; b: real);virtual;
function foo2(a: real; b: real);virtual;
begin
end
function operator[](index);
property row read readrow;
property col read readcol write wirtecol;
private
[weakref]a1: real;
static a2: real;
a3: tslobj;
end;
function A.create();
begin
pf := aa;
end;
function operator A.[](index);
begin
a := 1;
end;
function A.foo1(a: real; b: real);virtual;
begin
pf := function();
begin
end
end
// select
select * from abc end;
select *, ["abc"] from abc end;
select *, ["abc"] as "def" from abc end;
select * from abc where func(["abc"]) end;
select * from abc where ["abc"] > 1 end;
R1 := select *,RoundTo((["英语成绩"]+["语文成绩"]+["历史成绩"]+["地理成绩"])/4,2) as "文科成绩" from ScoreList end;
return select ['stockid'],['date'],['close']*['vol'] as nil from markettable end;
A := select *,ThisOrder as "Order" from A order by ["AAA"] end;
B := select * from EnglishScore where ["英语成绩"] > 85 order by ["英语成绩"] end;
B := select drange(0 to 9) * from EnglishScore order by ["英语成绩"] desc end;
B := select drange(1 of 10) from EnglishScore order by ["英语成绩"] desc end;
a := select * from abc where ["abc"] > 1 end;
b := select * from abc group by ["abc"] end;
c := select * from abc group by func(["acbb"]) end;
b := select * from abc group by ["abc"], ["def"] end;
e := select * from abc where func(["abc"]) order by ["A"] desc end;
select AvgOf(["英语成绩"]) from EnglishScore where ["英语成绩"]>85 end;
return select ["性别"],AvgOf(["英语成绩"]),CountOf( * ),groupfunc(["英语成绩"]) from EnglishScore group by ["性别"],groupfunc(["英语成绩"]) end;
return select AvgOf(["英语成绩"]), CountOf( * ), groupfunc(["英语成绩"]) from EnglishScore group by groupfunc(["英语成绩"]) having CountOf( * ) >1 end;
R := select [1].*,[2].["英语成绩"] from A join B on [1].["学号"]=[2].["学号"] end;
R := select [1].*,[2].["英语成绩"] from A cross join B where [1].["学号"]=[2].["学号"] end;
R := select [1].*,[2].["英语成绩"] from A, B where [1].["学号"]=[2].["学号"] end;
R := select [1].*,[2].["英语成绩"] from A join B with ([1].["学号"] on [2].["学号"]) end;
R := select [1].*,[2].["英语成绩"] from A join B with ([1].["学号"],[1].["班级"] on [2].["学号"],[2].["班级"]) end;
R := select [1].*,[2].["英语成绩"],[3].["语文成绩"] from A join B on [1].["学号"]=[2].["学号"] join C on [1].["学号"]=[3].["学号"] end;
R := select [1].*,[2].["英语成绩"],[3].["俄语成绩"] from A left join B on [1].["学号"]=[2].["学号"] left join C on [1].["学号"]=[3].["学号"] end;
R := select [1].["学号"]?:[2].["学号"] as "学号",[1].["英语成绩"],[2].["俄语成绩"] from B full join C on [1].["学号"]=[2].["学号"] end;
R1 := select ThisRow from R where ThisRow>5 end;
R2 := sselect ThisRow from R where ThisRow>5 end;
R2 := vselect SumOf( ["英语成绩"] ) from B end;
R1 := select ["性别"],["年龄"], AvgOf(["身高"]), select * from ThisGroup end as "详细信息" from R group by ["性别"],["年龄"] end;
// update
update B set ["英语成绩"] = 79 where ["学号"] = "03" end;
update B set ["英语成绩"]=79,["语文成绩"]=80 where ["学号"]="03" end;
update children set ['origin_field'] = ['field'] end;
update children set ['field'] = uppercase(['prefix']) + ['field'] where not ifnil(['ml']) and ['field'] in fields end;
update children set ['new_field'] = format('XmlChild%s', ['field']) end;
// delete
delete from A where ["学号"] = "01";
delete from A;
// insert
insert into a values("06","路人甲");
insert into a insertfields(["学号"],["姓名"],["英语成绩"]) values("06","路人甲",80);
// []
A[2,3];
A[2:5,3:6];
A[:,3:6];
A[3,:];
A[:,"columnName"];
A[array(2,4,6)];
A[array(2,3),array(1,2)];
[a,b,c] := arr;
// End of example

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
{
"name": "tree-sitter-tsf",
"version": "1.0.0",
"description": "",
"main": "bingings/node",
"scripts": {
"test": "tree-sitter test"
},
"tree-sitter":
[
{
"scope": "source.tsf",
"file-types": ["tsf"]
}
],
"dependencies": {
"nan": "^2.23.0"
},
"devDependencies": {
"tree-sitter-cli": "^0.25.6"
}
}

File diff suppressed because it is too large Load Diff