Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
ContenT
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ximper Linux
ContenT
Commits
daedc86a
Commit
daedc86a
authored
Feb 17, 2026
by
Roman Alifanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add json.get, str.urlencode and Telegram echobot example
parent
8e34932a
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
222 additions
and
8 deletions
+222
-8
LANGUAGE_SPEC.md
LANGUAGE_SPEC.md
+32
-2
README.md
README.md
+15
-2
README_ru.md
README_ru.md
+15
-2
dce.py
bootstrap/dce.py
+1
-1
dispatch_codegen.py
bootstrap/dispatch_codegen.py
+5
-1
stdlib.py
bootstrap/stdlib.py
+10
-0
stmt_codegen.py
bootstrap/stmt_codegen.py
+1
-0
echobot.ct
examples/telegram_echobot/echobot.ct
+61
-0
test_integration.py
tests/test_integration.py
+82
-0
No files found.
LANGUAGE_SPEC.md
View file @
daedc86a
...
...
@@ -584,6 +584,33 @@ result = json.stringify (data)
# {"name":"Alice","age":30,"city":"NYC"}
```
**json.get — извлечение из вложенного JSON:**
```
json_str = "\{\"user\": \{\"name\": \"Bob\", \"age\": 25\}\}"
name = json.get (json_str, ".user.name") # "Bob"
age = json.get (json_str, ".user.age") # 25
# Работа с массивами
items = "\{\"items\": [1, 2, 3]\}"
first = json.get (items, ".items[0]") # 1
count = json.get (items, ".items | length") # 3
```
Использует jq под капотом — поддерживает все jq-пути.
**Пример: Telegram бот**
```
response = http.get ("{base_url}/getUpdates")
chat_id = json.get (response, ".result[0].message.chat.id")
text = json.get (response, ".result[0].message.text")
encoded = text.urlencode ()
http.get ("{base_url}/sendMessage?chat_id={chat_id}&text={encoded}")
```
**Примечание:**
фигурные скобки в строках нужно экранировать как
`\{`
и
`\}`
.
### logger
...
...
@@ -622,6 +649,9 @@ char = text.charAt (0) # H
replaced = text.replace ("World", "ContenT")
parts = text.split (", ") # массив ["Hello", "World!"]
# URL-кодирование
encoded = text.urlencode () # "Hello%2C%20World%21"
# Символы (глобальные функции)
code = ord ("A") # 65
char = chr (65) # "A"
...
...
@@ -929,12 +959,12 @@ Error: Unknown method 'badMethod' for type 'fs'. Available: append, exists, list
Проверяются методы для:
-
**Массивов**
—
`filter`
,
`get`
,
`join`
,
`len`
,
`map`
,
`pop`
,
`push`
,
`set`
,
`shift`
,
`slice`
-
**Словарей**
—
`del`
,
`get`
,
`has`
,
`keys`
,
`set`
-
**Строк**
—
`charAt`
,
`contains`
,
`ends`
,
`index`
,
`len`
,
`lower`
,
`replace`
,
`split`
,
`starts`
,
`substr`
,
`trim`
,
`upper`
-
**Строк**
—
`charAt`
,
`contains`
,
`ends`
,
`index`
,
`len`
,
`lower`
,
`replace`
,
`split`
,
`starts`
,
`substr`
,
`trim`
,
`upper`
,
`urlencode`
-
**Файловых дескрипторов**
—
`close`
,
`read`
,
`readline`
,
`write`
,
`writeln`
-
**Stdlib namespaces**
:
-
`fs`
—
`append`
,
`exists`
,
`list`
,
`mkdir`
,
`open`
,
`read`
,
`remove`
,
`write`
-
`http`
—
`delete`
,
`get`
,
`post`
,
`put`
-
`json`
—
`parse`
,
`stringify`
-
`json`
—
`
get`
,
`
parse`
,
`stringify`
-
`logger`
—
`debug`
,
`error`
,
`info`
,
`warn`
-
`regex`
—
`extract`
,
`match`
-
`args`
—
`count`
,
`get`
...
...
README.md
View file @
daedc86a
...
...
@@ -199,8 +199,8 @@ try {
|
**HTTP**
|
`http.get/post/put/delete`
|
|
**Filesystem**
|
`fs.read/write/append/exists/remove/mkdir/list`
,
`fs.open()`
|
|
**File handles**
|
`f.read()`
,
`f.readline()`
,
`f.write()`
,
`f.writeln()`
,
`f.close()`
|
|
**JSON**
|
`json.parse()`
→ dict,
`json.stringify()`
→ string |
|
**Strings**
|
`.len()`
,
`.upper()`
,
`.lower()`
,
`.trim()`
,
`.contains()`
,
`.replace()`
,
`.split()`
,
`.substr()`
|
|
**JSON**
|
`json.parse()`
→ dict,
`json.stringify()`
→ string
,
`json.get(str, path)`
→ extract by jq path
|
|
**Strings**
|
`.len()`
,
`.upper()`
,
`.lower()`
,
`.trim()`
,
`.contains()`
,
`.replace()`
,
`.split()`
,
`.substr()`
,
`.urlencode()`
|
|
**Arrays**
|
`.push()`
,
`.pop()`
,
`.shift()`
,
`.len()`
,
`.get()`
,
`.set()`
,
`.join()`
,
`.slice()`
,
`.map()`
,
`.filter()`
|
|
**Dicts**
|
`.get()`
,
`.set()`
,
`.has()`
,
`.del()`
,
`.keys()`
|
|
**Regex**
|
`regex.match/extract`
|
...
...
@@ -338,6 +338,19 @@ FAIL failing test (1ms)
2 of 3 tests passed
```
## Examples
### Telegram Echo Bot
A simple Telegram bot that echoes messages back (
`examples/telegram_echobot/`
):
```
bash
export
TELEGRAM_BOT_TOKEN
=
"your_token"
python3 content run examples/telegram_echobot/echobot.ct
```
Uses
`json.get()`
for parsing Telegram API responses and
`str.urlencode()`
for URL encoding.
## Documentation
-
[
Language Specification
](
LANGUAGE_SPEC.md
)
...
...
README_ru.md
View file @
daedc86a
...
...
@@ -199,8 +199,8 @@ try {
|
**HTTP**
|
`http.get/post/put/delete`
|
|
**Файловая система**
|
`fs.read/write/append/exists/remove/mkdir/list`
,
`fs.open()`
|
|
**Файловые дескрипторы**
|
`f.read()`
,
`f.readline()`
,
`f.write()`
,
`f.writeln()`
,
`f.close()`
|
|
**JSON**
|
`json.parse()`
→ dict,
`json.stringify()`
→ string |
|
**Строки**
|
`.len()`
,
`.upper()`
,
`.lower()`
,
`.trim()`
,
`.contains()`
,
`.replace()`
,
`.split()`
,
`.substr()`
|
|
**JSON**
|
`json.parse()`
→ dict,
`json.stringify()`
→ string
,
`json.get(str, path)`
→ извлечь по jq-пути
|
|
**Строки**
|
`.len()`
,
`.upper()`
,
`.lower()`
,
`.trim()`
,
`.contains()`
,
`.replace()`
,
`.split()`
,
`.substr()`
,
`.urlencode()`
|
|
**Массивы**
|
`.push()`
,
`.pop()`
,
`.shift()`
,
`.len()`
,
`.get()`
,
`.set()`
,
`.join()`
,
`.slice()`
,
`.map()`
,
`.filter()`
|
|
**Словари**
|
`.get()`
,
`.set()`
,
`.has()`
,
`.del()`
,
`.keys()`
|
|
**Regex**
|
`regex.match/extract`
|
...
...
@@ -338,6 +338,19 @@ FAIL падающий тест (1ms)
2 of 3 tests passed
```
## Примеры
### Telegram эхо-бот
Простой Telegram-бот, который отправляет сообщения обратно (
`examples/telegram_echobot/`
):
```
bash
export
TELEGRAM_BOT_TOKEN
=
"your_token"
python3 content run examples/telegram_echobot/echobot.ct
```
Использует
`json.get()`
для парсинга ответов Telegram API и
`str.urlencode()`
для URL-кодирования.
## Документация
-
[
Спецификация языка
](
LANGUAGE_SPEC.md
)
...
...
bootstrap/dce.py
View file @
daedc86a
...
...
@@ -349,7 +349,7 @@ class UsageAnalyzer:
def
_check_method
(
self
,
method
:
str
):
string_methods
=
{
'upper'
,
'lower'
,
'trim'
,
'len'
,
'contains'
,
'starts'
,
'ends'
,
'index'
,
'replace'
,
'substr'
,
'split'
,
'charAt'
}
'ends'
,
'index'
,
'replace'
,
'substr'
,
'split'
,
'charAt'
,
'urlencode'
}
array_methods
=
{
'push'
,
'pop'
,
'shift'
,
'join'
,
'get'
,
'set'
,
'slice'
,
'len'
}
dict_methods
=
{
'get'
,
'set'
,
'has'
,
'del'
,
'keys'
}
...
...
bootstrap/dispatch_codegen.py
View file @
daedc86a
...
...
@@ -18,6 +18,7 @@ STR_METHODS = {
"trim"
:
"__ct_str_trim"
,
"contains"
:
"__ct_str_contains"
,
"starts"
:
"__ct_str_starts"
,
"ends"
:
"__ct_str_ends"
,
"index"
:
"__ct_str_index"
,
"replace"
:
"__ct_str_replace"
,
"substr"
:
"__ct_str_substr"
,
"split"
:
"__ct_str_split"
,
"charAt"
:
"__ct_str_char_at"
,
"urlencode"
:
"__ct_str_urlencode"
,
}
FILE_HANDLE_METHODS
=
{
...
...
@@ -32,7 +33,7 @@ BUILTIN_FUNCS = {"print", "exit", "len", "range", "ngrep", "is_number", "is_empt
FS_METHODS
=
{
"read"
,
"write"
,
"append"
,
"exists"
,
"remove"
,
"mkdir"
,
"list"
,
"open"
}
HTTP_METHODS
=
{
"get"
,
"post"
,
"put"
,
"delete"
}
JSON_METHODS
=
{
"parse"
,
"stringify"
}
JSON_METHODS
=
{
"parse"
,
"stringify"
,
"get"
}
LOGGER_METHODS
=
{
"info"
,
"warn"
,
"error"
,
"debug"
}
REGEX_METHODS
=
{
"match"
,
"extract"
}
ARGS_METHODS
=
{
"count"
,
"get"
}
...
...
@@ -656,6 +657,9 @@ class DispatchMixin:
dict_name
=
expr
.
arguments
[
0
]
.
name
if
dict_name
in
self
.
dict_vars
:
return
f
'__ct_json_stringify "{dict_name}"'
if
callee
.
object
.
name
==
"json"
and
callee
.
member
==
"get"
:
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
expr
.
arguments
]
return
f
'__ct_json_get "{args[0]}" "{args[1]}"'
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
expr
.
arguments
]
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
...
...
bootstrap/stdlib.py
View file @
daedc86a
...
...
@@ -330,6 +330,15 @@ class StdlibMixin:
self
.
emit
(
"}"
)
self
.
emit
()
self
.
emit
(
"__ct_json_get () {"
)
self
.
indent_level
+=
1
self
.
emit
(
'local __json="$1"'
)
self
.
emit
(
'local __path="$2"'
)
self
.
emit
(
'echo "$__json" | jq -r "$__path" 2>/dev/null'
)
self
.
indent_level
-=
1
self
.
emit
(
"}"
)
self
.
emit
()
self
.
emit
(
"__ct_json_stringify () {"
)
self
.
indent_level
+=
1
self
.
emit
(
'local -n __d="$1"'
)
...
...
@@ -454,6 +463,7 @@ class StdlibMixin:
self
.
emit
(
"__ct_str_upper () { __CT_RET=
\"
${1^^}
\"
; echo
\"
$__CT_RET
\"
; }"
)
self
.
emit
(
"__ct_str_lower () { __CT_RET=
\"
${1,,}
\"
; echo
\"
$__CT_RET
\"
; }"
)
self
.
emit
(
"__ct_str_char_at () { __CT_RET=
\"
${1:$2:1}
\"
; echo
\"
$__CT_RET
\"
; }"
)
self
.
emit
(
"__ct_str_urlencode () { __CT_RET=$(printf '
%
s'
\"
$1
\"
| jq -sRr @uri); echo
\"
$__CT_RET
\"
; }"
)
self
.
emit
(
"__ct_str_concat () { __CT_RET=
\"
$1$2
\"
; echo
\"
$__CT_RET
\"
; }"
)
self
.
emit
()
...
...
bootstrap/stmt_codegen.py
View file @
daedc86a
...
...
@@ -516,6 +516,7 @@ class StmtMixin:
"trim"
:
"__ct_str_trim"
,
"contains"
:
"__ct_str_contains"
,
"starts"
:
"__ct_str_starts"
,
"ends"
:
"__ct_str_ends"
,
"index"
:
"__ct_str_index"
,
"replace"
:
"__ct_str_replace"
,
"substr"
:
"__ct_str_substr"
,
"split"
:
"__ct_str_split"
,
"charAt"
:
"__ct_str_char_at"
,
"urlencode"
:
"__ct_str_urlencode"
,
}
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
...
...
examples/telegram_echobot/echobot.ct
0 → 100644
View file @
daedc86a
token = shell.capture ("printenv TELEGRAM_BOT_TOKEN")
if token == "" {
print ("Set TELEGRAM_BOT_TOKEN environment variable")
exit (1)
}
base_url = "https://api.telegram.org/bot{token}"
func get_username () {
response = http.get ("{base_url}/getMe")
username = json.get (response, ".result.username")
return username
}
func get_updates (offset) {
url = "{base_url}/getUpdates?timeout=30&offset={offset}"
response = http.get (url)
return response
}
func send_message (chat_id, text) {
encoded = text.urlencode ()
url = "{base_url}/sendMessage?chat_id={chat_id}&text={encoded}"
http.get (url)
}
username = get_username ()
if username == "null" {
print ("Invalid token")
exit (1)
}
print ("Bot @{username} started")
print ("Waiting for messages...")
offset = "0"
while true {
response = get_updates (offset)
count = json.get (response, ".result | length")
if count != "0" {
i = 0
while i < count {
update_id = json.get (response, ".result[{i}].update_id")
chat_id = json.get (response, ".result[{i}].message.chat.id")
text = json.get (response, ".result[{i}].message.text")
first_name = json.get (response, ".result[{i}].message.from.first_name")
offset = update_id + 1
if text != "null" {
print ("{first_name}: {text}")
send_message (chat_id, text)
}
i = i + 1
}
}
}
tests/test_integration.py
View file @
daedc86a
...
...
@@ -922,6 +922,87 @@ print(double(-1))
assert
code
!=
0
or
"must be"
in
stderr
class
TestJsonGet
:
def
test_json_get_simple
(
self
):
code
,
stdout
,
_
=
run_ct
(
r'''
json_str = "\{\"name\": \"Alice\"\}"
result = json.get(json_str, ".name")
print(result)
'''
)
assert
code
==
0
assert
"Alice"
in
stdout
def
test_json_get_nested
(
self
):
code
,
stdout
,
_
=
run_ct
(
r'''
json_str = "\{\"user\": \{\"name\": \"Bob\", \"age\": 25\}\}"
name = json.get(json_str, ".user.name")
age = json.get(json_str, ".user.age")
print(name)
print(age)
'''
)
assert
code
==
0
assert
"Bob"
in
stdout
assert
"25"
in
stdout
def
test_json_get_array
(
self
):
code
,
stdout
,
_
=
run_ct
(
r'''
json_str = "\{\"items\": [1, 2, 3]\}"
first = json.get(json_str, ".items[0]")
print(first)
'''
)
assert
code
==
0
assert
"1"
in
stdout
def
test_json_get_length
(
self
):
code
,
stdout
,
_
=
run_ct
(
r'''
json_str = "\{\"items\": [1, 2, 3, 4, 5]\}"
count = json.get(json_str, ".items | length")
print(count)
'''
)
assert
code
==
0
assert
"5"
in
stdout
class
TestUrlencode
:
def
test_urlencode_simple
(
self
):
code
,
stdout
,
_
=
run_ct
(
'''
text = "hello world"
encoded = text.urlencode()
print(encoded)
'''
)
assert
code
==
0
assert
"hello
%20
world"
in
stdout
def
test_urlencode_special_chars
(
self
):
code
,
stdout
,
_
=
run_ct
(
'''
text = "a=b&c=d"
encoded = text.urlencode()
print(encoded)
'''
)
assert
code
==
0
assert
"
%3
D"
in
stdout
or
"
%3
d"
in
stdout
assert
"
%26
"
in
stdout
def
test_urlencode_cyrillic
(
self
):
code
,
stdout
,
_
=
run_ct
(
'''
text = "привет"
encoded = text.urlencode()
print(encoded)
'''
)
assert
code
==
0
assert
"
%
"
in
stdout
assert
"привет"
not
in
stdout
def
test_urlencode_empty
(
self
):
code
,
stdout
,
_
=
run_ct
(
'''
text = ""
encoded = text.urlencode()
print("result: {encoded}")
'''
)
assert
code
==
0
assert
"result:"
in
stdout
class
TestMethodValidation
:
def
test_unknown_array_method
(
self
):
code
,
stdout
,
stderr
=
compile_ct_check
(
'''
...
...
@@ -956,6 +1037,7 @@ a = text.upper()
b = text.lower()
c = text.len()
d = text.trim()
e = text.urlencode()
'''
)
assert
code
==
0
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment