curl-programming-lang 1.2.0__tar.gz → 1.3.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: curl-programming-lang
3
- Version: 1.2.0
3
+ Version: 1.3.0
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
@@ -0,0 +1,83 @@
1
+ import os
2
+ import json
3
+ import urllib.request
4
+ import urllib.error
5
+
6
+
7
+ class CurlAIModule:
8
+ """
9
+ AI standard library for Curl.
10
+
11
+ Configuration via environment variables:
12
+ CURL_AI_KEY or OPENAI_API_KEY — API key (not needed for local models)
13
+ CURL_AI_BASE_URL — base URL (default: https://api.openai.com/v1)
14
+ set to http://localhost:11434/v1 for Ollama
15
+ CURL_AI_MODEL — model name (default: gpt-4o-mini)
16
+
17
+ Usage in Curl:
18
+ import{"ai", ai}\
19
+ var{answer, ai.ask{"What is the capital of France?"}}\
20
+ pcType{var{answer}}\
21
+ """
22
+
23
+ def _request(self, prompt):
24
+ api_key = os.environ.get("CURL_AI_KEY") or os.environ.get("OPENAI_API_KEY", "")
25
+ base_url = os.environ.get("CURL_AI_BASE_URL", "https://api.openai.com/v1").rstrip("/")
26
+ model = os.environ.get("CURL_AI_MODEL", "gpt-4o-mini")
27
+
28
+ if not api_key and "openai.com" in base_url:
29
+ raise RuntimeError(
30
+ "No API key found. Set CURL_AI_KEY (or OPENAI_API_KEY) in your environment.\n"
31
+ "For a free local model, install Ollama (https://ollama.com), run a model, then set:\n"
32
+ " export CURL_AI_BASE_URL=http://localhost:11434/v1\n"
33
+ " export CURL_AI_MODEL=llama3.2"
34
+ )
35
+
36
+ payload = json.dumps({
37
+ "model": model,
38
+ "messages": [{"role": "user", "content": str(prompt)}],
39
+ "temperature": 0.7,
40
+ }).encode()
41
+
42
+ headers = {"Content-Type": "application/json"}
43
+ if api_key:
44
+ headers["Authorization"] = f"Bearer {api_key}"
45
+
46
+ req = urllib.request.Request(
47
+ f"{base_url}/chat/completions",
48
+ data=payload,
49
+ headers=headers,
50
+ )
51
+
52
+ try:
53
+ with urllib.request.urlopen(req, timeout=60) as resp:
54
+ result = json.loads(resp.read())
55
+ return result["choices"][0]["message"]["content"].strip()
56
+ except urllib.error.HTTPError as e:
57
+ body = e.read().decode(errors="ignore")
58
+ raise RuntimeError(f"AI request failed ({e.code}): {body}")
59
+ except urllib.error.URLError as e:
60
+ raise RuntimeError(f"AI connection failed: {e.reason}")
61
+
62
+ def ask(self, prompt):
63
+ """Send a prompt and return the response as a string."""
64
+ return self._request(prompt)
65
+
66
+ def summarize(self, text):
67
+ """Summarize text in 2-3 sentences."""
68
+ return self._request(f"Summarize the following in 2-3 sentences:\n\n{text}")
69
+
70
+ def analyze(self, text):
71
+ """Analyze text and return key insights."""
72
+ return self._request(f"Analyze the following and provide key insights:\n\n{text}")
73
+
74
+ def sentiment(self, text):
75
+ """Return the sentiment of text: positive, negative, or neutral."""
76
+ return self._request(
77
+ f"What is the sentiment of this text? "
78
+ f"Reply with only one word: positive, negative, or neutral.\n\n{text}"
79
+ )
80
+
81
+ def translate(self, text):
82
+ """Translate text — include the target language in the text itself."""
83
+ return self._request(f"Translate the following:\n\n{text}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: curl-programming-lang
3
- Version: 1.2.0
3
+ Version: 1.3.0
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
@@ -1,5 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
+ ai_module.py
3
4
  errors.py
4
5
  interpreter.py
5
6
  lexer.py
@@ -1,4 +1,5 @@
1
1
  import importlib
2
+ from ai_module import CurlAIModule
2
3
 
3
4
 
4
5
  def execute(ast, env=None):
@@ -54,11 +55,17 @@ def _exec_stmt(stmt, env):
54
55
  _exec_other(stmt["language"], stmt["code"], env)
55
56
 
56
57
  elif t == "import":
57
- try:
58
- mod = importlib.import_module(stmt["package"])
59
- env["imports"][stmt["nickname"]] = mod
60
- except ImportError as e:
61
- raise ImportError(f"Could not import '{stmt['package']}': {e}")
58
+ if stmt["package"] == "ai":
59
+ env["imports"][stmt["nickname"]] = CurlAIModule()
60
+ else:
61
+ try:
62
+ mod = importlib.import_module(stmt["package"])
63
+ env["imports"][stmt["nickname"]] = mod
64
+ except ImportError as e:
65
+ raise ImportError(f"Could not import '{stmt['package']}': {e}")
66
+
67
+ elif t == "method_call":
68
+ _eval(stmt, env) # execute and discard return value
62
69
 
63
70
  elif t == "ai":
64
71
  print(f"[pcAI — mode: {stmt['mode']} | {stmt['directions']}]")
@@ -124,6 +131,17 @@ def _eval(expr, env):
124
131
  _call_func(expr["name"], env)
125
132
  return None
126
133
 
134
+ if t == "method_call":
135
+ module_name = expr["module"]
136
+ method_name = expr["method"]
137
+ arg = _eval(expr["arg"], env)
138
+ if module_name not in env["imports"]:
139
+ raise NameError(f"'{module_name}' is not imported — use import{{\"ai\", {module_name}}}\\")
140
+ module = env["imports"][module_name]
141
+ if not hasattr(module, method_name):
142
+ raise AttributeError(f"'{module_name}' has no method '{method_name}'")
143
+ return getattr(module, method_name)(arg)
144
+
127
145
  raise RuntimeError(f"Unknown expression type: {t!r}")
128
146
 
129
147
 
@@ -15,6 +15,7 @@ GT = "GT"
15
15
  LTE = "LTE"
16
16
  GTE = "GTE"
17
17
  ASSIGN = "ASSIGN"
18
+ DOT = "DOT"
18
19
  LBRACE = "LBRACE"
19
20
  RBRACE = "RBRACE"
20
21
  SEMICOLON = "SEMICOLON"
@@ -45,6 +46,7 @@ TOKEN_PATTERNS = [
45
46
  (TIMES, r'\*'),
46
47
  (DIVIDE, r'/'),
47
48
  (ASSIGN, r'='),
49
+ (DOT, r'\.'),
48
50
  (LBRACE, r'\{'),
49
51
  (RBRACE, r'\}'),
50
52
  (SEMICOLON, r';'),
@@ -136,7 +136,7 @@ def repl():
136
136
  pass # Windows — basic input still works, just no arrow keys
137
137
 
138
138
  print(f"Curl {__version__} ({platform.system()}) on {sys.platform}")
139
- print('Type "help", "copyright", "credits", "license", or "exit" for more information.')
139
+ print('Type "help", "copyright", "credits" or "license" for more information.')
140
140
 
141
141
  env = {
142
142
  "variables": {},
@@ -2,7 +2,7 @@ from lexer import (
2
2
  KEYWORD, STRING, NUMBER, IDENTIFIER,
3
3
  PLUS, MINUS, TIMES, DIVIDE,
4
4
  EQ, NEQ, LT, GT, LTE, GTE,
5
- LBRACE, RBRACE, SEMICOLON, COMMA, COLON, ARROW,
5
+ LBRACE, RBRACE, SEMICOLON, COMMA, COLON, ARROW, DOT,
6
6
  LINE_END, BLOCK_END, RAW_CODE
7
7
  )
8
8
 
@@ -19,6 +19,12 @@ class Parser:
19
19
  return self.tokens[self.pos]
20
20
  return None
21
21
 
22
+ def peek(self, offset=1):
23
+ idx = self.pos + offset
24
+ if idx < len(self.tokens):
25
+ return self.tokens[idx]
26
+ return None
27
+
22
28
  def consume(self, token_type, value=None):
23
29
  token = self.current()
24
30
  if token is None:
@@ -72,6 +78,10 @@ class Parser:
72
78
  if token is None:
73
79
  return None
74
80
 
81
+ # module.method{arg}\ — e.g. ai.ask{"prompt"}\
82
+ if token[0] == IDENTIFIER and self.peek() and self.peek()[0] == DOT:
83
+ return self.parse_method_call_stmt()
84
+
75
85
  if token[0] != KEYWORD:
76
86
  raise SyntaxError(f"Expected a Curl keyword, got {token[0]} {repr(token[1])}")
77
87
 
@@ -118,7 +128,7 @@ class Parser:
118
128
  if self.check(COMMA):
119
129
  # var{name, value}\ → assignment
120
130
  self.consume(COMMA)
121
- value = self.parse_expr()
131
+ value = self.parse_concat_expr()
122
132
  self.consume(RBRACE)
123
133
  self.consume(LINE_END)
124
134
  return {"type": "assign", "name": name, "value": value}
@@ -197,6 +207,16 @@ class Parser:
197
207
  self.consume(LINE_END)
198
208
  return {"type": "other_code", "language": lang, "code": code}
199
209
 
210
+ def parse_method_call_stmt(self):
211
+ module = self.consume(IDENTIFIER)[1]
212
+ self.consume(DOT)
213
+ method = self.consume(IDENTIFIER)[1]
214
+ self.consume(LBRACE)
215
+ arg = self.parse_concat_expr()
216
+ self.consume(RBRACE)
217
+ self.consume(LINE_END)
218
+ return {"type": "method_call", "module": module, "method": method, "arg": arg}
219
+
200
220
  def parse_import(self):
201
221
  self.consume(KEYWORD, "import")
202
222
  self.consume(LBRACE)
@@ -286,6 +306,19 @@ class Parser:
286
306
  self.consume(RBRACE)
287
307
  return {"type": "func_call_expr", "name": name}
288
308
 
309
+ # module.method{arg} — e.g. ai.ask{"prompt"}
310
+ if token[0] == IDENTIFIER:
311
+ module = token[1]
312
+ self.pos += 1
313
+ if self.check(DOT):
314
+ self.consume(DOT)
315
+ method = self.consume(IDENTIFIER)[1]
316
+ self.consume(LBRACE)
317
+ arg = self.parse_concat_expr()
318
+ self.consume(RBRACE)
319
+ return {"type": "method_call", "module": module, "method": method, "arg": arg}
320
+ return {"type": "var_ref", "name": module}
321
+
289
322
  raise SyntaxError(f"Unexpected token in expression: {token[0]} {repr(token[1])}")
290
323
 
291
324
  def _parse_list_body(self):
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "curl-programming-lang"
7
- version = "1.2.0"
7
+ version = "1.3.0"
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"
@@ -29,4 +29,4 @@ Repository = "https://github.com/gautamritvik/Curl-Programming"
29
29
  curlang = "main:main"
30
30
 
31
31
  [tool.setuptools]
32
- py-modules = ["main", "lexer", "parser", "interpreter", "errors"]
32
+ py-modules = ["main", "lexer", "parser", "interpreter", "errors", "ai_module"]