IncludeCPP 3.4.10__py3-none-any.whl → 3.4.21__py3-none-any.whl
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.
- includecpp/__init__.py +1 -1
- includecpp/__init__.pyi +131 -3
- includecpp/cli/commands.py +124 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +1482 -0
- includecpp/core/cssl/__init__.py +6 -6
- includecpp/core/cssl/cssl_builtins.py +243 -5
- includecpp/core/cssl/cssl_parser.py +298 -10
- includecpp/core/cssl/cssl_runtime.py +704 -53
- includecpp/core/cssl/cssl_types.py +403 -2
- includecpp/core/cssl_bridge.py +363 -0
- includecpp/generator/parser.cpp +1 -1
- {includecpp-3.4.10.dist-info → includecpp-3.4.21.dist-info}/METADATA +270 -3
- {includecpp-3.4.10.dist-info → includecpp-3.4.21.dist-info}/RECORD +17 -16
- {includecpp-3.4.10.dist-info → includecpp-3.4.21.dist-info}/WHEEL +0 -0
- {includecpp-3.4.10.dist-info → includecpp-3.4.21.dist-info}/entry_points.txt +0 -0
- {includecpp-3.4.10.dist-info → includecpp-3.4.21.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.4.10.dist-info → includecpp-3.4.21.dist-info}/top_level.txt +0 -0
|
@@ -99,6 +99,7 @@ class TokenType(Enum):
|
|
|
99
99
|
AT = auto()
|
|
100
100
|
GLOBAL_REF = auto() # r@<name> global variable declaration
|
|
101
101
|
SELF_REF = auto() # s@<name> self-reference to global struct
|
|
102
|
+
SHARED_REF = auto() # $<name> shared object reference
|
|
102
103
|
PACKAGE = auto()
|
|
103
104
|
PACKAGE_INCLUDES = auto()
|
|
104
105
|
AS = auto()
|
|
@@ -154,6 +155,11 @@ TYPE_GENERICS = {
|
|
|
154
155
|
'vector', 'stack', 'array', 'openquote'
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
# Functions that accept type parameters: FuncName<type>(args)
|
|
159
|
+
TYPE_PARAM_FUNCTIONS = {
|
|
160
|
+
'OpenFind' # OpenFind<string>(0)
|
|
161
|
+
}
|
|
162
|
+
|
|
157
163
|
# Injection helper prefixes (type::helper=value)
|
|
158
164
|
INJECTION_HELPERS = {
|
|
159
165
|
'string', 'integer', 'json', 'array', 'vector', 'combo', 'dynamic', 'sql'
|
|
@@ -232,6 +238,9 @@ class CSSLLexer:
|
|
|
232
238
|
elif char == '@':
|
|
233
239
|
self._add_token(TokenType.AT, '@')
|
|
234
240
|
self._advance()
|
|
241
|
+
elif char == '$':
|
|
242
|
+
# $<name> shared object reference
|
|
243
|
+
self._read_shared_ref()
|
|
235
244
|
elif char == '&':
|
|
236
245
|
# & for references
|
|
237
246
|
if self._peek(1) == '&':
|
|
@@ -470,6 +479,20 @@ class CSSLLexer:
|
|
|
470
479
|
value = self.source[name_start:self.pos]
|
|
471
480
|
self._add_token(TokenType.GLOBAL_REF, value)
|
|
472
481
|
|
|
482
|
+
def _read_shared_ref(self):
|
|
483
|
+
"""Read $<name> shared object reference"""
|
|
484
|
+
self._advance() # skip '$'
|
|
485
|
+
|
|
486
|
+
# Read the identifier (shared object name)
|
|
487
|
+
name_start = self.pos
|
|
488
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
489
|
+
self._advance()
|
|
490
|
+
|
|
491
|
+
value = self.source[name_start:self.pos]
|
|
492
|
+
if not value:
|
|
493
|
+
self.error("Expected identifier after '$'")
|
|
494
|
+
self._add_token(TokenType.SHARED_REF, value)
|
|
495
|
+
|
|
473
496
|
def _read_less_than(self):
|
|
474
497
|
# Check for <<== (code infusion left)
|
|
475
498
|
if self._peek(1) == '<' and self._peek(2) == '=' and self._peek(3) == '=':
|
|
@@ -952,7 +975,7 @@ class CSSLParser:
|
|
|
952
975
|
global_stmt = ASTNode('global_assignment', value=stmt)
|
|
953
976
|
root.children.append(global_stmt)
|
|
954
977
|
# Handle statements
|
|
955
|
-
elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or self._check(TokenType.SELF_REF):
|
|
978
|
+
elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF):
|
|
956
979
|
stmt = self._parse_expression_statement()
|
|
957
980
|
if stmt:
|
|
958
981
|
root.children.append(stmt)
|
|
@@ -1275,6 +1298,7 @@ class CSSLParser:
|
|
|
1275
1298
|
return None
|
|
1276
1299
|
|
|
1277
1300
|
def _parse_if(self) -> ASTNode:
|
|
1301
|
+
"""Parse if statement with support for else if AND elif syntax."""
|
|
1278
1302
|
self._expect(TokenType.PAREN_START)
|
|
1279
1303
|
condition = self._parse_expression()
|
|
1280
1304
|
self._expect(TokenType.PAREN_END)
|
|
@@ -1290,7 +1314,13 @@ class CSSLParser:
|
|
|
1290
1314
|
self._expect(TokenType.BLOCK_END)
|
|
1291
1315
|
node.children.append(then_block)
|
|
1292
1316
|
|
|
1293
|
-
|
|
1317
|
+
# Support both 'else if' AND 'elif' syntax
|
|
1318
|
+
if self._match_keyword('elif'):
|
|
1319
|
+
# elif is shorthand for else if
|
|
1320
|
+
else_block = ASTNode('else', children=[])
|
|
1321
|
+
else_block.children.append(self._parse_if())
|
|
1322
|
+
node.children.append(else_block)
|
|
1323
|
+
elif self._match_keyword('else'):
|
|
1294
1324
|
else_block = ASTNode('else', children=[])
|
|
1295
1325
|
if self._match_keyword('if'):
|
|
1296
1326
|
else_block.children.append(self._parse_if())
|
|
@@ -1322,18 +1352,186 @@ class CSSLParser:
|
|
|
1322
1352
|
return node
|
|
1323
1353
|
|
|
1324
1354
|
def _parse_for(self) -> ASTNode:
|
|
1355
|
+
"""Parse for loop - supports both syntaxes:
|
|
1356
|
+
|
|
1357
|
+
Python-style: for (i in range(0, n)) { }
|
|
1358
|
+
C-style: for (int i = 0; i < n; i = i + 1) { }
|
|
1359
|
+
for (i = 0; i < n; i++) { }
|
|
1360
|
+
"""
|
|
1325
1361
|
self._expect(TokenType.PAREN_START)
|
|
1362
|
+
|
|
1363
|
+
# Detect C-style by checking for semicolons in the for header
|
|
1364
|
+
# Look ahead without consuming tokens
|
|
1365
|
+
is_c_style = self._detect_c_style_for()
|
|
1366
|
+
|
|
1367
|
+
if is_c_style:
|
|
1368
|
+
# C-style: for (init; condition; update) { }
|
|
1369
|
+
return self._parse_c_style_for()
|
|
1370
|
+
else:
|
|
1371
|
+
# Python-style: for (var in range(start, end)) { }
|
|
1372
|
+
return self._parse_python_style_for()
|
|
1373
|
+
|
|
1374
|
+
def _detect_c_style_for(self) -> bool:
|
|
1375
|
+
"""Detect if this is a C-style for loop by looking for semicolons."""
|
|
1376
|
+
# Scan the tokens list directly without modifying self.pos
|
|
1377
|
+
pos = self.pos
|
|
1378
|
+
paren_depth = 1
|
|
1379
|
+
|
|
1380
|
+
while pos < len(self.tokens) and paren_depth > 0:
|
|
1381
|
+
token = self.tokens[pos]
|
|
1382
|
+
if token.type == TokenType.PAREN_START:
|
|
1383
|
+
paren_depth += 1
|
|
1384
|
+
elif token.type == TokenType.PAREN_END:
|
|
1385
|
+
paren_depth -= 1
|
|
1386
|
+
elif token.type == TokenType.SEMICOLON and paren_depth == 1:
|
|
1387
|
+
# Found semicolon at top level - C-style
|
|
1388
|
+
return True
|
|
1389
|
+
elif token.type == TokenType.KEYWORD and token.value == 'in':
|
|
1390
|
+
# Found 'in' keyword - Python-style
|
|
1391
|
+
return False
|
|
1392
|
+
pos += 1
|
|
1393
|
+
|
|
1394
|
+
return False # Default to Python-style
|
|
1395
|
+
|
|
1396
|
+
def _parse_c_style_for(self) -> ASTNode:
|
|
1397
|
+
"""Parse C-style for loop: for (init; condition; update) { }"""
|
|
1398
|
+
# Parse init statement
|
|
1399
|
+
init = None
|
|
1400
|
+
if not self._check(TokenType.SEMICOLON):
|
|
1401
|
+
# Check if it's a typed declaration: int i = 0
|
|
1402
|
+
if self._check(TokenType.KEYWORD) and self._peek().value in ('int', 'float', 'string', 'bool', 'dynamic'):
|
|
1403
|
+
type_name = self._advance().value
|
|
1404
|
+
var_name = self._advance().value
|
|
1405
|
+
self._expect(TokenType.EQUALS)
|
|
1406
|
+
value = self._parse_expression()
|
|
1407
|
+
init = ASTNode('c_for_init', value={
|
|
1408
|
+
'type': type_name,
|
|
1409
|
+
'var': var_name,
|
|
1410
|
+
'value': value
|
|
1411
|
+
})
|
|
1412
|
+
else:
|
|
1413
|
+
# Simple assignment: i = 0
|
|
1414
|
+
var_name = self._advance().value
|
|
1415
|
+
self._expect(TokenType.EQUALS)
|
|
1416
|
+
value = self._parse_expression()
|
|
1417
|
+
init = ASTNode('c_for_init', value={
|
|
1418
|
+
'type': None,
|
|
1419
|
+
'var': var_name,
|
|
1420
|
+
'value': value
|
|
1421
|
+
})
|
|
1422
|
+
|
|
1423
|
+
self._expect(TokenType.SEMICOLON)
|
|
1424
|
+
|
|
1425
|
+
# Parse condition
|
|
1426
|
+
condition = None
|
|
1427
|
+
if not self._check(TokenType.SEMICOLON):
|
|
1428
|
+
condition = self._parse_expression()
|
|
1429
|
+
|
|
1430
|
+
self._expect(TokenType.SEMICOLON)
|
|
1431
|
+
|
|
1432
|
+
# Parse update statement
|
|
1433
|
+
update = None
|
|
1434
|
+
if not self._check(TokenType.PAREN_END):
|
|
1435
|
+
# Could be: i = i + 1, i++, ++i, i += 1
|
|
1436
|
+
update = self._parse_c_for_update()
|
|
1437
|
+
|
|
1438
|
+
self._expect(TokenType.PAREN_END)
|
|
1439
|
+
|
|
1440
|
+
node = ASTNode('c_for', value={
|
|
1441
|
+
'init': init,
|
|
1442
|
+
'condition': condition,
|
|
1443
|
+
'update': update
|
|
1444
|
+
}, children=[])
|
|
1445
|
+
|
|
1446
|
+
self._expect(TokenType.BLOCK_START)
|
|
1447
|
+
|
|
1448
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
1449
|
+
stmt = self._parse_statement()
|
|
1450
|
+
if stmt:
|
|
1451
|
+
node.children.append(stmt)
|
|
1452
|
+
|
|
1453
|
+
self._expect(TokenType.BLOCK_END)
|
|
1454
|
+
return node
|
|
1455
|
+
|
|
1456
|
+
def _parse_c_for_update(self) -> ASTNode:
|
|
1457
|
+
"""Parse the update part of a C-style for loop.
|
|
1458
|
+
|
|
1459
|
+
Supports: i = i + 1, i++, ++i, i += 1, i -= 1
|
|
1460
|
+
"""
|
|
1461
|
+
# Check for prefix increment/decrement: ++i or --i
|
|
1462
|
+
if self._check(TokenType.PLUS) or self._check(TokenType.MINUS):
|
|
1463
|
+
op_token = self._advance()
|
|
1464
|
+
# Check for double operator (++ or --)
|
|
1465
|
+
if self._check(op_token.type):
|
|
1466
|
+
self._advance()
|
|
1467
|
+
var_name = self._advance().value
|
|
1468
|
+
op = 'increment' if op_token.type == TokenType.PLUS else 'decrement'
|
|
1469
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': op})
|
|
1470
|
+
|
|
1471
|
+
# Regular variable assignment or postfix
|
|
1472
|
+
var_name = self._advance().value
|
|
1473
|
+
|
|
1474
|
+
# Check for postfix increment/decrement: i++ or i--
|
|
1475
|
+
if self._check(TokenType.PLUS):
|
|
1476
|
+
self._advance()
|
|
1477
|
+
if self._check(TokenType.PLUS):
|
|
1478
|
+
self._advance()
|
|
1479
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
|
|
1480
|
+
else:
|
|
1481
|
+
# i += value
|
|
1482
|
+
if self._check(TokenType.EQUALS):
|
|
1483
|
+
self._advance()
|
|
1484
|
+
value = self._parse_expression()
|
|
1485
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'add', 'value': value})
|
|
1486
|
+
elif self._check(TokenType.MINUS):
|
|
1487
|
+
self._advance()
|
|
1488
|
+
if self._check(TokenType.MINUS):
|
|
1489
|
+
self._advance()
|
|
1490
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
|
|
1491
|
+
else:
|
|
1492
|
+
# i -= value
|
|
1493
|
+
if self._check(TokenType.EQUALS):
|
|
1494
|
+
self._advance()
|
|
1495
|
+
value = self._parse_expression()
|
|
1496
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'subtract', 'value': value})
|
|
1497
|
+
|
|
1498
|
+
# Regular assignment: i = expression
|
|
1499
|
+
if self._check(TokenType.EQUALS):
|
|
1500
|
+
self._advance()
|
|
1501
|
+
value = self._parse_expression()
|
|
1502
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'assign', 'value': value})
|
|
1503
|
+
|
|
1504
|
+
# Just the variable (shouldn't happen but handle it)
|
|
1505
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'none'})
|
|
1506
|
+
|
|
1507
|
+
def _parse_python_style_for(self) -> ASTNode:
|
|
1508
|
+
"""Parse Python-style for loop: for (i in range(start, end)) { }"""
|
|
1326
1509
|
var_name = self._advance().value
|
|
1327
|
-
self._expect(TokenType.KEYWORD)
|
|
1328
|
-
|
|
1510
|
+
self._expect(TokenType.KEYWORD) # 'in'
|
|
1511
|
+
|
|
1512
|
+
# 'range' can be keyword or identifier
|
|
1513
|
+
if self._check(TokenType.KEYWORD) and self._peek().value == 'range':
|
|
1514
|
+
self._advance() # consume 'range' keyword
|
|
1515
|
+
elif self._check(TokenType.IDENTIFIER) and self._peek().value == 'range':
|
|
1516
|
+
self._advance() # consume 'range' identifier
|
|
1517
|
+
else:
|
|
1518
|
+
self.error(f"Expected 'range', got {self._peek().value}")
|
|
1519
|
+
|
|
1329
1520
|
self._expect(TokenType.PAREN_START)
|
|
1330
1521
|
start = self._parse_expression()
|
|
1331
1522
|
self._expect(TokenType.COMMA)
|
|
1332
1523
|
end = self._parse_expression()
|
|
1524
|
+
|
|
1525
|
+
# Optional step parameter: range(start, end, step)
|
|
1526
|
+
step = None
|
|
1527
|
+
if self._check(TokenType.COMMA):
|
|
1528
|
+
self._advance() # consume comma
|
|
1529
|
+
step = self._parse_expression()
|
|
1530
|
+
|
|
1333
1531
|
self._expect(TokenType.PAREN_END)
|
|
1334
1532
|
self._expect(TokenType.PAREN_END)
|
|
1335
1533
|
|
|
1336
|
-
node = ASTNode('for', value={'var': var_name, 'start': start, 'end': end}, children=[])
|
|
1534
|
+
node = ASTNode('for', value={'var': var_name, 'start': start, 'end': end, 'step': step}, children=[])
|
|
1337
1535
|
self._expect(TokenType.BLOCK_START)
|
|
1338
1536
|
|
|
1339
1537
|
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
@@ -1787,6 +1985,31 @@ class CSSLParser:
|
|
|
1787
1985
|
break
|
|
1788
1986
|
return node
|
|
1789
1987
|
|
|
1988
|
+
if self._check(TokenType.SHARED_REF):
|
|
1989
|
+
# $<name> shared object reference
|
|
1990
|
+
token = self._advance()
|
|
1991
|
+
node = ASTNode('shared_ref', value=token.value, line=token.line, column=token.column)
|
|
1992
|
+
# Check for member access, calls, indexing
|
|
1993
|
+
while True:
|
|
1994
|
+
if self._match(TokenType.PAREN_START):
|
|
1995
|
+
args = []
|
|
1996
|
+
while not self._check(TokenType.PAREN_END):
|
|
1997
|
+
args.append(self._parse_expression())
|
|
1998
|
+
if not self._check(TokenType.PAREN_END):
|
|
1999
|
+
self._expect(TokenType.COMMA)
|
|
2000
|
+
self._expect(TokenType.PAREN_END)
|
|
2001
|
+
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2002
|
+
elif self._match(TokenType.DOT):
|
|
2003
|
+
member = self._advance().value
|
|
2004
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
2005
|
+
elif self._match(TokenType.BRACKET_START):
|
|
2006
|
+
index = self._parse_expression()
|
|
2007
|
+
self._expect(TokenType.BRACKET_END)
|
|
2008
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
2009
|
+
else:
|
|
2010
|
+
break
|
|
2011
|
+
return node
|
|
2012
|
+
|
|
1790
2013
|
if self._check(TokenType.NUMBER):
|
|
1791
2014
|
return ASTNode('literal', value=self._advance().value)
|
|
1792
2015
|
|
|
@@ -1822,16 +2045,81 @@ class CSSLParser:
|
|
|
1822
2045
|
return ASTNode('literal', value=None)
|
|
1823
2046
|
|
|
1824
2047
|
def _parse_module_reference(self) -> ASTNode:
|
|
1825
|
-
|
|
1826
|
-
parts.append(self._advance().value)
|
|
2048
|
+
"""Parse @name, handling method calls and property access.
|
|
1827
2049
|
|
|
1828
|
-
|
|
1829
|
-
|
|
2050
|
+
@name alone -> module_ref
|
|
2051
|
+
@name.method() -> call with member_access
|
|
2052
|
+
@name.property -> member_access
|
|
2053
|
+
"""
|
|
2054
|
+
# Get base name
|
|
2055
|
+
name = self._advance().value
|
|
2056
|
+
node = ASTNode('module_ref', value=name)
|
|
1830
2057
|
|
|
1831
|
-
|
|
2058
|
+
# Continue to handle member access, calls, and indexing
|
|
2059
|
+
while True:
|
|
2060
|
+
if self._match(TokenType.DOT):
|
|
2061
|
+
member = self._advance().value
|
|
2062
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
2063
|
+
elif self._match(TokenType.PAREN_START):
|
|
2064
|
+
# Function call
|
|
2065
|
+
args = []
|
|
2066
|
+
while not self._check(TokenType.PAREN_END):
|
|
2067
|
+
args.append(self._parse_expression())
|
|
2068
|
+
if not self._check(TokenType.PAREN_END):
|
|
2069
|
+
self._expect(TokenType.COMMA)
|
|
2070
|
+
self._expect(TokenType.PAREN_END)
|
|
2071
|
+
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2072
|
+
elif self._match(TokenType.BRACKET_START):
|
|
2073
|
+
# Index access
|
|
2074
|
+
index = self._parse_expression()
|
|
2075
|
+
self._expect(TokenType.BRACKET_END)
|
|
2076
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
2077
|
+
else:
|
|
2078
|
+
break
|
|
2079
|
+
|
|
2080
|
+
return node
|
|
1832
2081
|
|
|
1833
2082
|
def _parse_identifier_or_call(self) -> ASTNode:
|
|
1834
2083
|
name = self._advance().value
|
|
2084
|
+
|
|
2085
|
+
# Check for type generic instantiation: stack<string>, vector<int>, etc.
|
|
2086
|
+
# This creates a new instance of the type with the specified element type
|
|
2087
|
+
if name in TYPE_GENERICS and self._check(TokenType.COMPARE_LT):
|
|
2088
|
+
self._advance() # consume <
|
|
2089
|
+
element_type = 'dynamic'
|
|
2090
|
+
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
2091
|
+
element_type = self._advance().value
|
|
2092
|
+
self._expect(TokenType.COMPARE_GT) # consume >
|
|
2093
|
+
return ASTNode('type_instantiation', value={
|
|
2094
|
+
'type': name,
|
|
2095
|
+
'element_type': element_type
|
|
2096
|
+
})
|
|
2097
|
+
|
|
2098
|
+
# Check for type-parameterized function call: OpenFind<string>(0)
|
|
2099
|
+
if name in TYPE_PARAM_FUNCTIONS and self._check(TokenType.COMPARE_LT):
|
|
2100
|
+
self._advance() # consume <
|
|
2101
|
+
type_param = 'dynamic'
|
|
2102
|
+
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
2103
|
+
type_param = self._advance().value
|
|
2104
|
+
self._expect(TokenType.COMPARE_GT) # consume >
|
|
2105
|
+
|
|
2106
|
+
# Must be followed by ()
|
|
2107
|
+
if self._check(TokenType.PAREN_START):
|
|
2108
|
+
self._advance() # consume (
|
|
2109
|
+
args = []
|
|
2110
|
+
while not self._check(TokenType.PAREN_END):
|
|
2111
|
+
args.append(self._parse_expression())
|
|
2112
|
+
if not self._check(TokenType.PAREN_END):
|
|
2113
|
+
self._expect(TokenType.COMMA)
|
|
2114
|
+
self._expect(TokenType.PAREN_END)
|
|
2115
|
+
|
|
2116
|
+
# Return as typed function call
|
|
2117
|
+
return ASTNode('typed_call', value={
|
|
2118
|
+
'name': name,
|
|
2119
|
+
'type_param': type_param,
|
|
2120
|
+
'args': args
|
|
2121
|
+
})
|
|
2122
|
+
|
|
1835
2123
|
node = ASTNode('identifier', value=name)
|
|
1836
2124
|
|
|
1837
2125
|
while True:
|