curl-programming-lang 1.4.0__tar.gz → 1.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {curl_programming_lang-1.4.0/curl_programming_lang.egg-info → curl_programming_lang-1.4.2}/PKG-INFO +67 -5
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/README.md +66 -4
- curl_programming_lang-1.4.2/ai_module.py +291 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2/curl_programming_lang.egg-info}/PKG-INFO +67 -5
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/interpreter.py +1 -1
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/parser.py +24 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/pyproject.toml +1 -1
- curl_programming_lang-1.4.0/ai_module.py +0 -159
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/LICENSE +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/curl_programming_lang.egg-info/SOURCES.txt +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/curl_programming_lang.egg-info/dependency_links.txt +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/curl_programming_lang.egg-info/entry_points.txt +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/curl_programming_lang.egg-info/top_level.txt +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/errors.py +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/lexer.py +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/main.py +0 -0
- {curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2}/setup.cfg +0 -0
{curl_programming_lang-1.4.0/curl_programming_lang.egg-info → curl_programming_lang-1.4.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: curl-programming-lang
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: Curl is an open-source programming language built on Python technology
|
|
5
5
|
Author: Ritvik Gautam
|
|
6
6
|
License: Apache-2.0
|
|
@@ -47,9 +47,10 @@ git clone https://github.com/gautamritvik/Curl-Programming.git
|
|
|
47
47
|
cd Curl-Programming
|
|
48
48
|
pip install -e .
|
|
49
49
|
```
|
|
50
|
+
|
|
50
51
|
---
|
|
51
52
|
|
|
52
|
-
## Running the Curl terminal
|
|
53
|
+
## Running the Curl terminal (REPL)
|
|
53
54
|
|
|
54
55
|
```
|
|
55
56
|
curlang
|
|
@@ -61,6 +62,15 @@ curlang
|
|
|
61
62
|
curlang [YOUR-FILE].curl
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
## CLI flags
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
curlang --version show version
|
|
69
|
+
curlang --license show license
|
|
70
|
+
curlang --credits show credits
|
|
71
|
+
curlang --help show help
|
|
72
|
+
```
|
|
73
|
+
|
|
64
74
|
---
|
|
65
75
|
|
|
66
76
|
## Syntax Reference
|
|
@@ -81,7 +91,7 @@ pcType{"Hello, " + var{name} + "!"}\
|
|
|
81
91
|
Prompts the user for input. The result is accessed anywhere with `input{ans}`.
|
|
82
92
|
|
|
83
93
|
```
|
|
84
|
-
pcAsk{"What is your name?"
|
|
94
|
+
pcAsk{"What is your name? >>"}\
|
|
85
95
|
pcType{"You said: " + input{ans}}\
|
|
86
96
|
```
|
|
87
97
|
|
|
@@ -198,12 +208,51 @@ import{"math", m}\
|
|
|
198
208
|
|
|
199
209
|
### AI — `pcAI`
|
|
200
210
|
|
|
201
|
-
|
|
211
|
+
`pcAI` is a **built-in AI module** — no import required. It supports conversation history, system prompts, and an interactive chat mode.
|
|
212
|
+
|
|
213
|
+
Configure it with environment variables:
|
|
214
|
+
|
|
215
|
+
| Variable | Default | Description |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| `CURL_AI_KEY` or `OPENAI_API_KEY` | *(none)* | Your API key |
|
|
218
|
+
| `CURL_AI_BASE_URL` | `https://api.openai.com/v1` | API base URL |
|
|
219
|
+
| `CURL_AI_MODEL` | `gpt-4o-mini` | Model name |
|
|
220
|
+
|
|
221
|
+
Works with **OpenAI**, **OpenRouter**, **Ollama**, or any OpenAI-compatible API.
|
|
222
|
+
|
|
223
|
+
**Ask a question:**
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
var{reply, pcAI.ask{"What is the capital of France?"}}\
|
|
227
|
+
pcType{var{reply}}\
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Set a persona (system prompt):**
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
pcAI.context{"You are a friendly pirate who answers in rhymes."}\
|
|
234
|
+
var{reply, pcAI.ask{"What is 2 + 2?"}}\
|
|
235
|
+
pcType{var{reply}}\
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Interactive chat loop** (type `exit` to quit):
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
pcAI.chat{""}\
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Other methods:**
|
|
202
245
|
|
|
203
246
|
```
|
|
204
|
-
|
|
247
|
+
var{s, pcAI.summarize{"some long text"}}\
|
|
248
|
+
var{a, pcAI.analyze{"some data"}}\
|
|
249
|
+
var{mood, pcAI.sentiment{"I love this!"}}\
|
|
250
|
+
var{t, pcAI.translate{"Bonjour — translate to English"}}\
|
|
251
|
+
pcAI.reset{""}\
|
|
205
252
|
```
|
|
206
253
|
|
|
254
|
+
> Conversation history is kept across `pcAI.ask` calls and auto-compacts when it gets long.
|
|
255
|
+
|
|
207
256
|
---
|
|
208
257
|
|
|
209
258
|
## Symbols
|
|
@@ -221,6 +270,9 @@ pcAI{".on", "You are a helpful assistant", "profanityControl"}\
|
|
|
221
270
|
| `!=` | Not equals |
|
|
222
271
|
| `=` | Assignment |
|
|
223
272
|
| `+` | Concatenation / addition |
|
|
273
|
+
| `-` | Subtraction |
|
|
274
|
+
| `*` | Multiplication |
|
|
275
|
+
| `/` | Division |
|
|
224
276
|
|
|
225
277
|
---
|
|
226
278
|
|
|
@@ -246,6 +298,16 @@ func{sayBye}\
|
|
|
246
298
|
|
|
247
299
|
---
|
|
248
300
|
|
|
301
|
+
## Example AI program
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
pcAI.context{"You are a helpful tutor."}\
|
|
305
|
+
var{answer, pcAI.ask{"Explain what a variable is in one sentence."}}\
|
|
306
|
+
pcType{var{answer}}\
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
249
311
|
## License
|
|
250
312
|
|
|
251
313
|
This project is licensed under the Apache License 2.0 (OSI-approved).
|
|
@@ -28,9 +28,10 @@ git clone https://github.com/gautamritvik/Curl-Programming.git
|
|
|
28
28
|
cd Curl-Programming
|
|
29
29
|
pip install -e .
|
|
30
30
|
```
|
|
31
|
+
|
|
31
32
|
---
|
|
32
33
|
|
|
33
|
-
## Running the Curl terminal
|
|
34
|
+
## Running the Curl terminal (REPL)
|
|
34
35
|
|
|
35
36
|
```
|
|
36
37
|
curlang
|
|
@@ -42,6 +43,15 @@ curlang
|
|
|
42
43
|
curlang [YOUR-FILE].curl
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
## CLI flags
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
curlang --version show version
|
|
50
|
+
curlang --license show license
|
|
51
|
+
curlang --credits show credits
|
|
52
|
+
curlang --help show help
|
|
53
|
+
```
|
|
54
|
+
|
|
45
55
|
---
|
|
46
56
|
|
|
47
57
|
## Syntax Reference
|
|
@@ -62,7 +72,7 @@ pcType{"Hello, " + var{name} + "!"}\
|
|
|
62
72
|
Prompts the user for input. The result is accessed anywhere with `input{ans}`.
|
|
63
73
|
|
|
64
74
|
```
|
|
65
|
-
pcAsk{"What is your name?"
|
|
75
|
+
pcAsk{"What is your name? >>"}\
|
|
66
76
|
pcType{"You said: " + input{ans}}\
|
|
67
77
|
```
|
|
68
78
|
|
|
@@ -179,12 +189,51 @@ import{"math", m}\
|
|
|
179
189
|
|
|
180
190
|
### AI — `pcAI`
|
|
181
191
|
|
|
182
|
-
|
|
192
|
+
`pcAI` is a **built-in AI module** — no import required. It supports conversation history, system prompts, and an interactive chat mode.
|
|
193
|
+
|
|
194
|
+
Configure it with environment variables:
|
|
195
|
+
|
|
196
|
+
| Variable | Default | Description |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `CURL_AI_KEY` or `OPENAI_API_KEY` | *(none)* | Your API key |
|
|
199
|
+
| `CURL_AI_BASE_URL` | `https://api.openai.com/v1` | API base URL |
|
|
200
|
+
| `CURL_AI_MODEL` | `gpt-4o-mini` | Model name |
|
|
201
|
+
|
|
202
|
+
Works with **OpenAI**, **OpenRouter**, **Ollama**, or any OpenAI-compatible API.
|
|
203
|
+
|
|
204
|
+
**Ask a question:**
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
var{reply, pcAI.ask{"What is the capital of France?"}}\
|
|
208
|
+
pcType{var{reply}}\
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Set a persona (system prompt):**
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
pcAI.context{"You are a friendly pirate who answers in rhymes."}\
|
|
215
|
+
var{reply, pcAI.ask{"What is 2 + 2?"}}\
|
|
216
|
+
pcType{var{reply}}\
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Interactive chat loop** (type `exit` to quit):
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
pcAI.chat{""}\
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Other methods:**
|
|
183
226
|
|
|
184
227
|
```
|
|
185
|
-
|
|
228
|
+
var{s, pcAI.summarize{"some long text"}}\
|
|
229
|
+
var{a, pcAI.analyze{"some data"}}\
|
|
230
|
+
var{mood, pcAI.sentiment{"I love this!"}}\
|
|
231
|
+
var{t, pcAI.translate{"Bonjour — translate to English"}}\
|
|
232
|
+
pcAI.reset{""}\
|
|
186
233
|
```
|
|
187
234
|
|
|
235
|
+
> Conversation history is kept across `pcAI.ask` calls and auto-compacts when it gets long.
|
|
236
|
+
|
|
188
237
|
---
|
|
189
238
|
|
|
190
239
|
## Symbols
|
|
@@ -202,6 +251,9 @@ pcAI{".on", "You are a helpful assistant", "profanityControl"}\
|
|
|
202
251
|
| `!=` | Not equals |
|
|
203
252
|
| `=` | Assignment |
|
|
204
253
|
| `+` | Concatenation / addition |
|
|
254
|
+
| `-` | Subtraction |
|
|
255
|
+
| `*` | Multiplication |
|
|
256
|
+
| `/` | Division |
|
|
205
257
|
|
|
206
258
|
---
|
|
207
259
|
|
|
@@ -227,6 +279,16 @@ func{sayBye}\
|
|
|
227
279
|
|
|
228
280
|
---
|
|
229
281
|
|
|
282
|
+
## Example AI program
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
pcAI.context{"You are a helpful tutor."}\
|
|
286
|
+
var{answer, pcAI.ask{"Explain what a variable is in one sentence."}}\
|
|
287
|
+
pcType{var{answer}}\
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
230
292
|
## License
|
|
231
293
|
|
|
232
294
|
This project is licensed under the Apache License 2.0 (OSI-approved).
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import json
|
|
4
|
+
import urllib.request
|
|
5
|
+
import urllib.error
|
|
6
|
+
|
|
7
|
+
# ── ANSI terminal codes ───────────────────────────────────────────────────────
|
|
8
|
+
_R = "\033[0m" # reset
|
|
9
|
+
_B = "\033[1m" # bold
|
|
10
|
+
_I = "\033[3m" # italic
|
|
11
|
+
_U = "\033[4m" # underline
|
|
12
|
+
_CY = "\033[36m" # cyan (inline code)
|
|
13
|
+
_DM = "\033[2m" # dim (horizontal rules)
|
|
14
|
+
|
|
15
|
+
def _md(text):
|
|
16
|
+
"""Convert basic Markdown to ANSI escape codes for terminal display."""
|
|
17
|
+
out = []
|
|
18
|
+
for line in text.split("\n"):
|
|
19
|
+
# headings
|
|
20
|
+
if line.startswith("### "):
|
|
21
|
+
line = f"{_B}{line[4:]}{_R}"
|
|
22
|
+
elif line.startswith("## "):
|
|
23
|
+
line = f"{_B}{_U}{line[3:]}{_R}"
|
|
24
|
+
elif line.startswith("# "):
|
|
25
|
+
line = f"{_B}{_U}{line[2:]}{_R}"
|
|
26
|
+
# horizontal rule
|
|
27
|
+
elif re.fullmatch(r'[-*_]{3,}', line.strip()):
|
|
28
|
+
line = f"{_DM}{'─' * 40}{_R}"
|
|
29
|
+
else:
|
|
30
|
+
# bullet points
|
|
31
|
+
line = re.sub(r'^(\s*)[-*] ', r'\1• ', line)
|
|
32
|
+
# bold + italic
|
|
33
|
+
line = re.sub(r'\*\*\*(.+?)\*\*\*', lambda m: f"{_B}{_I}{m.group(1)}{_R}", line)
|
|
34
|
+
# bold
|
|
35
|
+
line = re.sub(r'\*\*(.+?)\*\*', lambda m: f"{_B}{m.group(1)}{_R}", line)
|
|
36
|
+
# italic
|
|
37
|
+
line = re.sub(r'\*(.+?)\*', lambda m: f"{_I}{m.group(1)}{_R}", line)
|
|
38
|
+
# inline code
|
|
39
|
+
line = re.sub(r'`([^`]+)`', lambda m: f"{_CY}{m.group(1)}{_R}", line)
|
|
40
|
+
out.append(line)
|
|
41
|
+
return "\n".join(out)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ── module constants ──────────────────────────────────────────────────────────
|
|
45
|
+
_DEFAULT_SYSTEM = """\
|
|
46
|
+
You are Curl-Bot, an AI assistant built into the Curl programming language.
|
|
47
|
+
Curl is an open-source programming language created by Ritvik Gautam that runs on Python.
|
|
48
|
+
You are helpful, concise, and friendly.
|
|
49
|
+
Respond in plain text only — no Markdown, no asterisks, no bullet symbols, no backticks.
|
|
50
|
+
|
|
51
|
+
=== CURL LANGUAGE REFERENCE ===
|
|
52
|
+
|
|
53
|
+
Every statement ends with a backslash (\\).
|
|
54
|
+
Blocks open with - and close with --\\.
|
|
55
|
+
|
|
56
|
+
PRINT:
|
|
57
|
+
pcType{"Hello, world!"}\
|
|
58
|
+
pcType{"Hello " + var{name}}\
|
|
59
|
+
|
|
60
|
+
INPUT (stores in input{ans}):
|
|
61
|
+
pcAsk{"What is your name? >>"}\
|
|
62
|
+
|
|
63
|
+
VARIABLES:
|
|
64
|
+
var{name, "Ritvik"}\ — assign string
|
|
65
|
+
var{score, 100}\ — assign number
|
|
66
|
+
var{greeting, "Hi " + var{name}}\
|
|
67
|
+
pcType{var{name}}\ — print variable
|
|
68
|
+
|
|
69
|
+
LISTS:
|
|
70
|
+
list{1; 2; 3}\
|
|
71
|
+
|
|
72
|
+
FUNCTIONS:
|
|
73
|
+
createFunc{greet}-
|
|
74
|
+
pcType{"Hello!"}\
|
|
75
|
+
--\\
|
|
76
|
+
func{greet}\ — call the function
|
|
77
|
+
|
|
78
|
+
IF / ELIF / ELSE:
|
|
79
|
+
if{var{x} == 10, then}-
|
|
80
|
+
pcType{"ten"}\
|
|
81
|
+
--\\
|
|
82
|
+
|
|
83
|
+
if{var{x} == 10, then}-
|
|
84
|
+
pcType{"ten"}\
|
|
85
|
+
else:
|
|
86
|
+
pcType{"not ten"}\
|
|
87
|
+
--\\
|
|
88
|
+
|
|
89
|
+
if{var{x} > 10, then}-
|
|
90
|
+
pcType{"big"}\
|
|
91
|
+
elif{var{x} == 10, then}-
|
|
92
|
+
pcType{"exact"}\
|
|
93
|
+
--\\--\\
|
|
94
|
+
|
|
95
|
+
OPERATORS:
|
|
96
|
+
== equals != not equals
|
|
97
|
+
< less than > greater than
|
|
98
|
+
<= less/equal >= greater/equal
|
|
99
|
+
+ concatenate or add
|
|
100
|
+
- subtract * multiply / divide
|
|
101
|
+
\\ end of line - open block --\\ close block
|
|
102
|
+
{} argument list "" string data ; list separator
|
|
103
|
+
, parameter sep
|
|
104
|
+
|
|
105
|
+
OTHER LANGUAGE BLOCKS:
|
|
106
|
+
otherCoding{"Python",
|
|
107
|
+
print("hi")
|
|
108
|
+
}\\
|
|
109
|
+
|
|
110
|
+
otherCoding{"JavaScript",
|
|
111
|
+
console.log("hi")
|
|
112
|
+
}\\
|
|
113
|
+
|
|
114
|
+
AI MODULE (built-in, no import needed):
|
|
115
|
+
pcAI.context{"You are a pirate."}\\ — set bot persona (resets history)
|
|
116
|
+
var{r, pcAI.ask{"What is 2+2?"}}\\ — ask a question (history kept)
|
|
117
|
+
pcType{var{r}}\\
|
|
118
|
+
pcAI.chat{""}\\ — start interactive chat loop (type exit to quit)
|
|
119
|
+
pcAI.reset{""}\\ — clear conversation history
|
|
120
|
+
pcAI.summarize{"some long text"}\\
|
|
121
|
+
pcAI.analyze{"some data"}\\
|
|
122
|
+
pcAI.sentiment{"I love this!"}\\ — returns: positive / negative / neutral
|
|
123
|
+
pcAI.translate{"Bonjour to English"}\\
|
|
124
|
+
|
|
125
|
+
IMPORTS:
|
|
126
|
+
import{"math", m}\\ — any Python stdlib or installed package
|
|
127
|
+
|
|
128
|
+
=== END OF CURL REFERENCE ===
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
_COMPACT_THRESHOLD = 20 # auto-compact after this many messages in history
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class CurlAIModule:
|
|
135
|
+
"""
|
|
136
|
+
AI standard library for Curl.
|
|
137
|
+
|
|
138
|
+
Env vars:
|
|
139
|
+
CURL_AI_KEY / OPENAI_API_KEY — API key
|
|
140
|
+
CURL_AI_BASE_URL — base URL (default: https://api.openai.com/v1)
|
|
141
|
+
CURL_AI_MODEL — model (default: gpt-4o-mini)
|
|
142
|
+
|
|
143
|
+
Methods (Curl syntax):
|
|
144
|
+
ai.context{"system prompt"}\ — set bot persona
|
|
145
|
+
ai.ask{"prompt"}\ — one-shot question (history kept)
|
|
146
|
+
ai.chat{""}\ — interactive chat loop (exit to quit)
|
|
147
|
+
ai.reset{""}\ — clear history
|
|
148
|
+
ai.summarize{"text"}\
|
|
149
|
+
ai.analyze{"text"}\
|
|
150
|
+
ai.sentiment{"text"}\
|
|
151
|
+
ai.translate{"text to English"}\
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
def __init__(self):
|
|
155
|
+
self._system = _DEFAULT_SYSTEM
|
|
156
|
+
self._history = []
|
|
157
|
+
|
|
158
|
+
# ── configuration ─────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
def context(self, prompt):
|
|
161
|
+
"""Set a persistent system prompt (persona) for all subsequent calls."""
|
|
162
|
+
self._system = str(prompt)
|
|
163
|
+
self._history = []
|
|
164
|
+
return ""
|
|
165
|
+
|
|
166
|
+
def reset(self, _=""):
|
|
167
|
+
"""Clear conversation history while keeping the current context."""
|
|
168
|
+
self._history = []
|
|
169
|
+
return ""
|
|
170
|
+
|
|
171
|
+
# ── internal ──────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
def _raw_request(self, messages):
|
|
174
|
+
api_key = os.environ.get("CURL_AI_KEY") or os.environ.get("OPENAI_API_KEY", "")
|
|
175
|
+
base_url = os.environ.get("CURL_AI_BASE_URL", "https://api.openai.com/v1").rstrip("/")
|
|
176
|
+
model = os.environ.get("CURL_AI_MODEL", "gpt-4o-mini")
|
|
177
|
+
|
|
178
|
+
if not api_key and "openai.com" in base_url:
|
|
179
|
+
raise RuntimeError(
|
|
180
|
+
"No API key found. Set CURL_AI_KEY (or OPENAI_API_KEY) in your environment.\n"
|
|
181
|
+
"For a free local model, install Ollama, run a model, then:\n"
|
|
182
|
+
" export CURL_AI_BASE_URL=http://localhost:11434/v1\n"
|
|
183
|
+
" export CURL_AI_MODEL=llama3.2"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
payload = json.dumps({
|
|
187
|
+
"model": model,
|
|
188
|
+
"messages": messages,
|
|
189
|
+
"temperature": 0.7,
|
|
190
|
+
}).encode()
|
|
191
|
+
|
|
192
|
+
headers = {"Content-Type": "application/json"}
|
|
193
|
+
if api_key:
|
|
194
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
195
|
+
|
|
196
|
+
req = urllib.request.Request(
|
|
197
|
+
f"{base_url}/chat/completions",
|
|
198
|
+
data=payload,
|
|
199
|
+
headers=headers,
|
|
200
|
+
)
|
|
201
|
+
try:
|
|
202
|
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
203
|
+
result = json.loads(resp.read())
|
|
204
|
+
return result["choices"][0]["message"]["content"].strip()
|
|
205
|
+
except urllib.error.HTTPError as e:
|
|
206
|
+
body = e.read().decode(errors="ignore")
|
|
207
|
+
raise RuntimeError(f"AI request failed ({e.code}): {body}")
|
|
208
|
+
except urllib.error.URLError as e:
|
|
209
|
+
raise RuntimeError(f"AI connection failed: {e.reason}")
|
|
210
|
+
|
|
211
|
+
def _compact_history(self):
|
|
212
|
+
summary_messages = [
|
|
213
|
+
{"role": "system", "content": self._system},
|
|
214
|
+
{
|
|
215
|
+
"role": "user",
|
|
216
|
+
"content": (
|
|
217
|
+
"Summarize the following conversation in 3-5 bullet points "
|
|
218
|
+
"to use as context going forward. Be concise.\n\n"
|
|
219
|
+
+ "\n".join(f"{m['role'].upper()}: {m['content']}" for m in self._history)
|
|
220
|
+
),
|
|
221
|
+
},
|
|
222
|
+
]
|
|
223
|
+
summary = self._raw_request(summary_messages)
|
|
224
|
+
self._history = [{"role": "assistant", "content": f"[Previous conversation summary]\n{summary}"}]
|
|
225
|
+
|
|
226
|
+
def _request(self, prompt):
|
|
227
|
+
if len(self._history) >= _COMPACT_THRESHOLD:
|
|
228
|
+
self._compact_history()
|
|
229
|
+
self._history.append({"role": "user", "content": str(prompt)})
|
|
230
|
+
messages = [{"role": "system", "content": self._system}] + self._history
|
|
231
|
+
reply = self._raw_request(messages)
|
|
232
|
+
self._history.append({"role": "assistant", "content": reply})
|
|
233
|
+
return reply
|
|
234
|
+
|
|
235
|
+
# ── public methods ────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
def ask(self, prompt):
|
|
238
|
+
"""Send a prompt (history kept) and return the response."""
|
|
239
|
+
return _md(self._request(prompt))
|
|
240
|
+
|
|
241
|
+
def chat(self, _=""):
|
|
242
|
+
"""Start an interactive chat loop. Type 'exit' to quit."""
|
|
243
|
+
print(f"\n{_B}Curl-Bot Chat{_R} — type {_CY}exit{_R} to quit\n")
|
|
244
|
+
while True:
|
|
245
|
+
try:
|
|
246
|
+
user_input = input(f"{_B}You:{_R} ").strip()
|
|
247
|
+
except (EOFError, KeyboardInterrupt):
|
|
248
|
+
print("\nGoodbye!")
|
|
249
|
+
break
|
|
250
|
+
if user_input.lower() == "exit":
|
|
251
|
+
print("Goodbye!")
|
|
252
|
+
break
|
|
253
|
+
if not user_input:
|
|
254
|
+
continue
|
|
255
|
+
reply = self._request(user_input)
|
|
256
|
+
print(f"\n{_B}Curl-Bot:{_R} {_md(reply)}\n")
|
|
257
|
+
return ""
|
|
258
|
+
|
|
259
|
+
def summarize(self, text):
|
|
260
|
+
messages = [
|
|
261
|
+
{"role": "system", "content": self._system},
|
|
262
|
+
{"role": "user", "content": f"Summarize in 2-3 sentences:\n\n{text}"},
|
|
263
|
+
]
|
|
264
|
+
return _md(self._raw_request(messages))
|
|
265
|
+
|
|
266
|
+
def analyze(self, text):
|
|
267
|
+
messages = [
|
|
268
|
+
{"role": "system", "content": self._system},
|
|
269
|
+
{"role": "user", "content": f"Analyze and provide key insights:\n\n{text}"},
|
|
270
|
+
]
|
|
271
|
+
return _md(self._raw_request(messages))
|
|
272
|
+
|
|
273
|
+
def sentiment(self, text):
|
|
274
|
+
messages = [
|
|
275
|
+
{"role": "system", "content": self._system},
|
|
276
|
+
{
|
|
277
|
+
"role": "user",
|
|
278
|
+
"content": (
|
|
279
|
+
"What is the sentiment? Reply with ONE word only: positive, negative, or neutral.\n\n"
|
|
280
|
+
+ text
|
|
281
|
+
),
|
|
282
|
+
},
|
|
283
|
+
]
|
|
284
|
+
return self._raw_request(messages) # raw — used in comparisons
|
|
285
|
+
|
|
286
|
+
def translate(self, text):
|
|
287
|
+
messages = [
|
|
288
|
+
{"role": "system", "content": self._system},
|
|
289
|
+
{"role": "user", "content": f"Translate the following:\n\n{text}"},
|
|
290
|
+
]
|
|
291
|
+
return _md(self._raw_request(messages))
|
{curl_programming_lang-1.4.0 → curl_programming_lang-1.4.2/curl_programming_lang.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: curl-programming-lang
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: Curl is an open-source programming language built on Python technology
|
|
5
5
|
Author: Ritvik Gautam
|
|
6
6
|
License: Apache-2.0
|
|
@@ -47,9 +47,10 @@ git clone https://github.com/gautamritvik/Curl-Programming.git
|
|
|
47
47
|
cd Curl-Programming
|
|
48
48
|
pip install -e .
|
|
49
49
|
```
|
|
50
|
+
|
|
50
51
|
---
|
|
51
52
|
|
|
52
|
-
## Running the Curl terminal
|
|
53
|
+
## Running the Curl terminal (REPL)
|
|
53
54
|
|
|
54
55
|
```
|
|
55
56
|
curlang
|
|
@@ -61,6 +62,15 @@ curlang
|
|
|
61
62
|
curlang [YOUR-FILE].curl
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
## CLI flags
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
curlang --version show version
|
|
69
|
+
curlang --license show license
|
|
70
|
+
curlang --credits show credits
|
|
71
|
+
curlang --help show help
|
|
72
|
+
```
|
|
73
|
+
|
|
64
74
|
---
|
|
65
75
|
|
|
66
76
|
## Syntax Reference
|
|
@@ -81,7 +91,7 @@ pcType{"Hello, " + var{name} + "!"}\
|
|
|
81
91
|
Prompts the user for input. The result is accessed anywhere with `input{ans}`.
|
|
82
92
|
|
|
83
93
|
```
|
|
84
|
-
pcAsk{"What is your name?"
|
|
94
|
+
pcAsk{"What is your name? >>"}\
|
|
85
95
|
pcType{"You said: " + input{ans}}\
|
|
86
96
|
```
|
|
87
97
|
|
|
@@ -198,12 +208,51 @@ import{"math", m}\
|
|
|
198
208
|
|
|
199
209
|
### AI — `pcAI`
|
|
200
210
|
|
|
201
|
-
|
|
211
|
+
`pcAI` is a **built-in AI module** — no import required. It supports conversation history, system prompts, and an interactive chat mode.
|
|
212
|
+
|
|
213
|
+
Configure it with environment variables:
|
|
214
|
+
|
|
215
|
+
| Variable | Default | Description |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| `CURL_AI_KEY` or `OPENAI_API_KEY` | *(none)* | Your API key |
|
|
218
|
+
| `CURL_AI_BASE_URL` | `https://api.openai.com/v1` | API base URL |
|
|
219
|
+
| `CURL_AI_MODEL` | `gpt-4o-mini` | Model name |
|
|
220
|
+
|
|
221
|
+
Works with **OpenAI**, **OpenRouter**, **Ollama**, or any OpenAI-compatible API.
|
|
222
|
+
|
|
223
|
+
**Ask a question:**
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
var{reply, pcAI.ask{"What is the capital of France?"}}\
|
|
227
|
+
pcType{var{reply}}\
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Set a persona (system prompt):**
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
pcAI.context{"You are a friendly pirate who answers in rhymes."}\
|
|
234
|
+
var{reply, pcAI.ask{"What is 2 + 2?"}}\
|
|
235
|
+
pcType{var{reply}}\
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Interactive chat loop** (type `exit` to quit):
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
pcAI.chat{""}\
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Other methods:**
|
|
202
245
|
|
|
203
246
|
```
|
|
204
|
-
|
|
247
|
+
var{s, pcAI.summarize{"some long text"}}\
|
|
248
|
+
var{a, pcAI.analyze{"some data"}}\
|
|
249
|
+
var{mood, pcAI.sentiment{"I love this!"}}\
|
|
250
|
+
var{t, pcAI.translate{"Bonjour — translate to English"}}\
|
|
251
|
+
pcAI.reset{""}\
|
|
205
252
|
```
|
|
206
253
|
|
|
254
|
+
> Conversation history is kept across `pcAI.ask` calls and auto-compacts when it gets long.
|
|
255
|
+
|
|
207
256
|
---
|
|
208
257
|
|
|
209
258
|
## Symbols
|
|
@@ -221,6 +270,9 @@ pcAI{".on", "You are a helpful assistant", "profanityControl"}\
|
|
|
221
270
|
| `!=` | Not equals |
|
|
222
271
|
| `=` | Assignment |
|
|
223
272
|
| `+` | Concatenation / addition |
|
|
273
|
+
| `-` | Subtraction |
|
|
274
|
+
| `*` | Multiplication |
|
|
275
|
+
| `/` | Division |
|
|
224
276
|
|
|
225
277
|
---
|
|
226
278
|
|
|
@@ -246,6 +298,16 @@ func{sayBye}\
|
|
|
246
298
|
|
|
247
299
|
---
|
|
248
300
|
|
|
301
|
+
## Example AI program
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
pcAI.context{"You are a helpful tutor."}\
|
|
305
|
+
var{answer, pcAI.ask{"Explain what a variable is in one sentence."}}\
|
|
306
|
+
pcType{var{answer}}\
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
249
311
|
## License
|
|
250
312
|
|
|
251
313
|
This project is licensed under the Apache License 2.0 (OSI-approved).
|
|
@@ -82,6 +82,10 @@ class Parser:
|
|
|
82
82
|
if token[0] == IDENTIFIER and self.peek() and self.peek()[0] == DOT:
|
|
83
83
|
return self.parse_method_call_stmt()
|
|
84
84
|
|
|
85
|
+
# pcAI.method{arg}\ — built-in AI module, no import needed
|
|
86
|
+
if token[0] == KEYWORD and token[1] == "pcAI" and self.peek() and self.peek()[0] == DOT:
|
|
87
|
+
return self.parse_pcai_method_call_stmt()
|
|
88
|
+
|
|
85
89
|
if token[0] != KEYWORD:
|
|
86
90
|
raise SyntaxError(f"Expected a Curl keyword, got {token[0]} {repr(token[1])}")
|
|
87
91
|
|
|
@@ -217,6 +221,16 @@ class Parser:
|
|
|
217
221
|
self.consume(LINE_END)
|
|
218
222
|
return {"type": "method_call", "module": module, "method": method, "arg": arg}
|
|
219
223
|
|
|
224
|
+
def parse_pcai_method_call_stmt(self):
|
|
225
|
+
self.consume(KEYWORD, "pcAI")
|
|
226
|
+
self.consume(DOT)
|
|
227
|
+
method = self.consume(IDENTIFIER)[1]
|
|
228
|
+
self.consume(LBRACE)
|
|
229
|
+
arg = self.parse_concat_expr()
|
|
230
|
+
self.consume(RBRACE)
|
|
231
|
+
self.consume(LINE_END)
|
|
232
|
+
return {"type": "method_call", "module": "pcAI", "method": method, "arg": arg}
|
|
233
|
+
|
|
220
234
|
def parse_import(self):
|
|
221
235
|
self.consume(KEYWORD, "import")
|
|
222
236
|
self.consume(LBRACE)
|
|
@@ -281,6 +295,16 @@ class Parser:
|
|
|
281
295
|
if token[0] == KEYWORD:
|
|
282
296
|
kw = token[1]
|
|
283
297
|
|
|
298
|
+
# pcAI.method{arg} as an expression — e.g. var{x, pcAI.ask{"prompt"}}\
|
|
299
|
+
if kw == "pcAI" and self.peek() and self.peek()[0] == DOT:
|
|
300
|
+
self.pos += 1 # consume pcAI
|
|
301
|
+
self.consume(DOT)
|
|
302
|
+
method = self.consume(IDENTIFIER)[1]
|
|
303
|
+
self.consume(LBRACE)
|
|
304
|
+
arg = self.parse_concat_expr()
|
|
305
|
+
self.consume(RBRACE)
|
|
306
|
+
return {"type": "method_call", "module": "pcAI", "method": method, "arg": arg}
|
|
307
|
+
|
|
284
308
|
if kw == "var":
|
|
285
309
|
self.pos += 1
|
|
286
310
|
self.consume(LBRACE)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "curl-programming-lang"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.2"
|
|
8
8
|
description = "Curl is an open-source programming language built on Python technology"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.7"
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
import urllib.request
|
|
4
|
-
import urllib.error
|
|
5
|
-
|
|
6
|
-
_DEFAULT_SYSTEM = (
|
|
7
|
-
"You are Curl-Bot, an AI assistant built into the Curl programming language. "
|
|
8
|
-
"Curl is an open-source language created by Ritvik Gautam that runs on Python. "
|
|
9
|
-
"You are helpful, concise, and friendly."
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
# Auto-compact after this many user+assistant message pairs in history
|
|
13
|
-
_COMPACT_THRESHOLD = 20
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class CurlAIModule:
|
|
17
|
-
"""
|
|
18
|
-
AI standard library for Curl.
|
|
19
|
-
|
|
20
|
-
Configuration via environment variables:
|
|
21
|
-
CURL_AI_KEY or OPENAI_API_KEY — API key (not needed for local models)
|
|
22
|
-
CURL_AI_BASE_URL — base URL (default: https://api.openai.com/v1)
|
|
23
|
-
set to http://localhost:11434/v1 for Ollama
|
|
24
|
-
CURL_AI_MODEL — model name (default: gpt-4o-mini)
|
|
25
|
-
|
|
26
|
-
Usage in Curl:
|
|
27
|
-
import{"ai", ai}\
|
|
28
|
-
ai.context{"You are a pirate who only speaks in rhymes."}\
|
|
29
|
-
var{answer, ai.ask{"What is the capital of France?"}}\
|
|
30
|
-
pcType{var{answer}}\
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
def __init__(self):
|
|
34
|
-
self._system = _DEFAULT_SYSTEM
|
|
35
|
-
self._history = [] # list of {"role": ..., "content": ...}
|
|
36
|
-
|
|
37
|
-
def context(self, prompt):
|
|
38
|
-
"""Set a persistent system prompt (persona/context) for all subsequent AI calls."""
|
|
39
|
-
self._system = str(prompt)
|
|
40
|
-
self._history = [] # reset history when context changes
|
|
41
|
-
return ""
|
|
42
|
-
|
|
43
|
-
def reset(self, _=""):
|
|
44
|
-
"""Clear conversation history (start a fresh chat while keeping context)."""
|
|
45
|
-
self._history = []
|
|
46
|
-
return ""
|
|
47
|
-
|
|
48
|
-
def _raw_request(self, messages):
|
|
49
|
-
"""Send a messages list to the API and return the reply string."""
|
|
50
|
-
api_key = os.environ.get("CURL_AI_KEY") or os.environ.get("OPENAI_API_KEY", "")
|
|
51
|
-
base_url = os.environ.get("CURL_AI_BASE_URL", "https://api.openai.com/v1").rstrip("/")
|
|
52
|
-
model = os.environ.get("CURL_AI_MODEL", "gpt-4o-mini")
|
|
53
|
-
|
|
54
|
-
if not api_key and "openai.com" in base_url:
|
|
55
|
-
raise RuntimeError(
|
|
56
|
-
"No API key found. Set CURL_AI_KEY (or OPENAI_API_KEY) in your environment.\n"
|
|
57
|
-
"For a free local model, install Ollama (https://ollama.com), run a model, then set:\n"
|
|
58
|
-
" export CURL_AI_BASE_URL=http://localhost:11434/v1\n"
|
|
59
|
-
" export CURL_AI_MODEL=llama3.2"
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
payload = json.dumps({
|
|
63
|
-
"model": model,
|
|
64
|
-
"messages": messages,
|
|
65
|
-
"temperature": 0.7,
|
|
66
|
-
}).encode()
|
|
67
|
-
|
|
68
|
-
headers = {"Content-Type": "application/json"}
|
|
69
|
-
if api_key:
|
|
70
|
-
headers["Authorization"] = f"Bearer {api_key}"
|
|
71
|
-
|
|
72
|
-
req = urllib.request.Request(
|
|
73
|
-
f"{base_url}/chat/completions",
|
|
74
|
-
data=payload,
|
|
75
|
-
headers=headers,
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
80
|
-
result = json.loads(resp.read())
|
|
81
|
-
return result["choices"][0]["message"]["content"].strip()
|
|
82
|
-
except urllib.error.HTTPError as e:
|
|
83
|
-
body = e.read().decode(errors="ignore")
|
|
84
|
-
raise RuntimeError(f"AI request failed ({e.code}): {body}")
|
|
85
|
-
except urllib.error.URLError as e:
|
|
86
|
-
raise RuntimeError(f"AI connection failed: {e.reason}")
|
|
87
|
-
|
|
88
|
-
def _compact_history(self):
|
|
89
|
-
"""Summarize old history into a single assistant message to stay within limits."""
|
|
90
|
-
summary_messages = [
|
|
91
|
-
{"role": "system", "content": self._system},
|
|
92
|
-
{
|
|
93
|
-
"role": "user",
|
|
94
|
-
"content": (
|
|
95
|
-
"Summarize the following conversation history into 3-5 bullet points "
|
|
96
|
-
"so it can be used as context going forward. Be concise.\n\n"
|
|
97
|
-
+ "\n".join(
|
|
98
|
-
f"{m['role'].upper()}: {m['content']}" for m in self._history
|
|
99
|
-
)
|
|
100
|
-
),
|
|
101
|
-
},
|
|
102
|
-
]
|
|
103
|
-
summary = self._raw_request(summary_messages)
|
|
104
|
-
self._history = [{"role": "assistant", "content": f"[Previous conversation summary]\n{summary}"}]
|
|
105
|
-
|
|
106
|
-
def _request(self, prompt):
|
|
107
|
-
"""Send prompt with history, auto-compact when history is too long."""
|
|
108
|
-
if len(self._history) >= _COMPACT_THRESHOLD:
|
|
109
|
-
self._compact_history()
|
|
110
|
-
|
|
111
|
-
self._history.append({"role": "user", "content": str(prompt)})
|
|
112
|
-
|
|
113
|
-
messages = [{"role": "system", "content": self._system}] + self._history
|
|
114
|
-
reply = self._raw_request(messages)
|
|
115
|
-
|
|
116
|
-
self._history.append({"role": "assistant", "content": reply})
|
|
117
|
-
return reply
|
|
118
|
-
|
|
119
|
-
def ask(self, prompt):
|
|
120
|
-
"""Send a prompt (with conversation history) and return the response."""
|
|
121
|
-
return self._request(prompt)
|
|
122
|
-
|
|
123
|
-
def summarize(self, text):
|
|
124
|
-
"""Summarize text in 2-3 sentences (no history tracking)."""
|
|
125
|
-
messages = [
|
|
126
|
-
{"role": "system", "content": self._system},
|
|
127
|
-
{"role": "user", "content": f"Summarize the following in 2-3 sentences:\n\n{text}"},
|
|
128
|
-
]
|
|
129
|
-
return self._raw_request(messages)
|
|
130
|
-
|
|
131
|
-
def analyze(self, text):
|
|
132
|
-
"""Analyze text and return key insights (no history tracking)."""
|
|
133
|
-
messages = [
|
|
134
|
-
{"role": "system", "content": self._system},
|
|
135
|
-
{"role": "user", "content": f"Analyze the following and provide key insights:\n\n{text}"},
|
|
136
|
-
]
|
|
137
|
-
return self._raw_request(messages)
|
|
138
|
-
|
|
139
|
-
def sentiment(self, text):
|
|
140
|
-
"""Return the sentiment of text: positive, negative, or neutral (no history tracking)."""
|
|
141
|
-
messages = [
|
|
142
|
-
{"role": "system", "content": self._system},
|
|
143
|
-
{
|
|
144
|
-
"role": "user",
|
|
145
|
-
"content": (
|
|
146
|
-
"What is the sentiment of this text? "
|
|
147
|
-
"Reply with only one word: positive, negative, or neutral.\n\n" + text
|
|
148
|
-
),
|
|
149
|
-
},
|
|
150
|
-
]
|
|
151
|
-
return self._raw_request(messages)
|
|
152
|
-
|
|
153
|
-
def translate(self, text):
|
|
154
|
-
"""Translate text — include the target language in the text itself (no history tracking)."""
|
|
155
|
-
messages = [
|
|
156
|
-
{"role": "system", "content": self._system},
|
|
157
|
-
{"role": "user", "content": f"Translate the following:\n\n{text}"},
|
|
158
|
-
]
|
|
159
|
-
return self._raw_request(messages)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|