cpyte 2.0.2__tar.gz → 2.1.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.
Files changed (27) hide show
  1. {cpyte-2.0.2 → cpyte-2.1.0}/PKG-INFO +1 -1
  2. {cpyte-2.0.2 → cpyte-2.1.0}/pyproject.toml +1 -1
  3. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/astparse.py +82 -4
  4. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/bytecoding.py +24 -14
  5. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/compiling.py +30 -11
  6. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/extension_hooks.py +16 -3
  7. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/lexar.py +1 -0
  8. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/linker.py +9 -5
  9. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/mainpie.py +21 -12
  10. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/semantic_analasis.py +80 -1
  11. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte.egg-info/PKG-INFO +1 -1
  12. {cpyte-2.0.2 → cpyte-2.1.0}/readme.md +0 -0
  13. {cpyte-2.0.2 → cpyte-2.1.0}/setup.cfg +0 -0
  14. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/__init__.py +0 -0
  15. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/__main__.py +0 -0
  16. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/_bignum_bc.py +0 -0
  17. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/_runtime_bc.py +0 -0
  18. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/clib.py +0 -0
  19. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/generate_bc.py +0 -0
  20. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/lsp_server.py +0 -0
  21. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte/package_manifest.py +0 -0
  22. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte.egg-info/SOURCES.txt +0 -0
  23. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte.egg-info/dependency_links.txt +0 -0
  24. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte.egg-info/entry_points.txt +0 -0
  25. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte.egg-info/requires.txt +0 -0
  26. {cpyte-2.0.2 → cpyte-2.1.0}/source/cpyte.egg-info/top_level.txt +0 -0
  27. {cpyte-2.0.2 → cpyte-2.1.0}/test/test_bignum_jit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpyte
3
- Version: 2.0.2
3
+ Version: 2.1.0
4
4
  Summary: The Cpyte programming language compiler
5
5
  Author: Hoang Duy Tung
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cpyte"
7
- version = "2.0.2"
7
+ version = "2.1.0"
8
8
  description = "The Cpyte programming language compiler"
9
9
  readme = "readme.md"
10
10
  requires-python = ">=3.11"
@@ -116,11 +116,12 @@ class Index:
116
116
  return f'Index({self.obj}, {self.index})'
117
117
 
118
118
  class Attr:
119
- __slots__ = ('obj', 'name', '_token')
119
+ __slots__ = ('obj', 'name', '_token', '_enum_member_value')
120
120
  def __init__(self, obj, name: str, token=None):
121
121
  self.obj = obj
122
122
  self.name = name
123
123
  self._token = token
124
+ self._enum_member_value = None
124
125
  def __repr__(self):
125
126
  return f'Attr({self.obj}, {self.name})'
126
127
 
@@ -426,6 +427,10 @@ def _parse_standard_statement(tokens: list[Token], pos: int):
426
427
  node, pos = parse_try(tokens, pos)
427
428
  elif tok.type == TokenType.KEYWORD and tok.value == 'raise':
428
429
  node, pos = parse_raise(tokens, pos)
430
+ elif tok.type == TokenType.KEYWORD and tok.value == 'enum':
431
+ node, pos = parse_enum(tokens, pos)
432
+ elif tok.type == TokenType.KEYWORD and tok.value == 'type':
433
+ node, pos = parse_type_alias(tokens, pos)
429
434
  elif tok.type == TokenType.IDENTIFIER and pos + 1 < len(tokens):
430
435
  if tok.value in _TYPE_NAMES or _looks_like_type(tokens, pos):
431
436
  try:
@@ -1158,8 +1163,8 @@ def parse_statement(tokens: list[Token], pos: int):
1158
1163
  if tok.type == TokenType.KEYWORD and tok.value == 'switch':
1159
1164
  return parse_switch(tokens, pos)
1160
1165
 
1161
- if tok.type == TokenType.KEYWORD and tok.value in ('while', 'for', 'class', 'struct', 'import'):
1162
- handler = {'while': parse_while, 'for': parse_for, 'class': parse_class, 'struct': parse_struct_def, 'import': parse_import}[tok.value]
1166
+ if tok.type == TokenType.KEYWORD and tok.value in ('while', 'for', 'class', 'struct', 'import', 'enum', 'type'):
1167
+ handler = {'while': parse_while, 'for': parse_for, 'class': parse_class, 'struct': parse_struct_def, 'import': parse_import, 'enum': parse_enum, 'type': parse_type_alias}[tok.value]
1163
1168
  return handler(tokens, pos)
1164
1169
 
1165
1170
  if tok.type == TokenType.KEYWORD and tok.value == 'try':
@@ -1268,8 +1273,26 @@ class Switch:
1268
1273
  self._token = token
1269
1274
  def __repr__(self):
1270
1275
  return f'Switch({self.value}, {self.cases})'
1276
+
1277
+
1278
+ class EnumDef:
1279
+ __slots__ = ('name', 'members', '_token')
1280
+ def __init__(self, name: str, members: list, token=None):
1281
+ self.name = name
1282
+ self.members = members
1283
+ self._token = token
1271
1284
  def __repr__(self):
1272
- return f'Switch({self.value}, {self.cases})'
1285
+ return f'EnumDef({self.name}, {self.members})'
1286
+
1287
+
1288
+ class TypeAlias:
1289
+ __slots__ = ('name', 'target_type', '_token')
1290
+ def __init__(self, name: str, target_type, token=None):
1291
+ self.name = name
1292
+ self.target_type = target_type
1293
+ self._token = token
1294
+ def __repr__(self):
1295
+ return f'TypeAlias({self.name}, {self.target_type})'
1273
1296
 
1274
1297
 
1275
1298
  def parse_switch(tokens: list[Token], pos: int):
@@ -1348,6 +1371,61 @@ def parse_generic_params(tokens: list[Token], pos: int):
1348
1371
  return params, pos + 1
1349
1372
 
1350
1373
 
1374
+ def parse_enum(tokens: list[Token], pos: int):
1375
+ tok = tokens[pos]
1376
+ pos += 1
1377
+ if pos >= len(tokens) or tokens[pos].type != TokenType.IDENTIFIER:
1378
+ raise ParseError('Expected enum name', tokens[pos] if pos < len(tokens) else None)
1379
+ name = tokens[pos].value
1380
+ pos += 1
1381
+ if pos >= len(tokens) or tokens[pos].type != TokenType.COLON:
1382
+ raise ParseError('Expected ":" after enum name', tokens[pos] if pos < len(tokens) else None)
1383
+ pos += 1
1384
+ if pos >= len(tokens) or tokens[pos].type != TokenType.NEWLINE:
1385
+ raise ParseError('Expected newline after ":"', tokens[pos] if pos < len(tokens) else None)
1386
+ pos += 1
1387
+ if pos >= len(tokens) or tokens[pos].type != TokenType.INDENT:
1388
+ raise ParseError('Expected indented block', tokens[pos] if pos < len(tokens) else None)
1389
+ pos += 1
1390
+
1391
+ members = []
1392
+ while pos < len(tokens) and tokens[pos].type not in (TokenType.DEDENT, TokenType.EOF):
1393
+ if tokens[pos].type != TokenType.IDENTIFIER:
1394
+ raise ParseError('Expected enum member name', tokens[pos])
1395
+ member_name = tokens[pos].value
1396
+ pos += 1
1397
+ member_value = None
1398
+ if pos < len(tokens) and tokens[pos].type == TokenType.KEYWORD and tokens[pos].value == '=':
1399
+ pos += 1
1400
+ member_value, pos = parse_expression(tokens, pos)
1401
+ members.append({'name': member_name, 'value': member_value, '_token': tokens[pos - 1]})
1402
+ if pos < len(tokens) and tokens[pos].type == TokenType.NEWLINE:
1403
+ pos += 1
1404
+
1405
+ if pos < len(tokens) and tokens[pos].type == TokenType.DEDENT:
1406
+ pos += 1
1407
+
1408
+ return EnumDef(name, members, token=tok), pos
1409
+
1410
+
1411
+ def parse_type_alias(tokens: list[Token], pos: int):
1412
+ tok = tokens[pos]
1413
+ pos += 1
1414
+ if pos >= len(tokens) or tokens[pos].type != TokenType.IDENTIFIER:
1415
+ raise ParseError('Expected type alias name', tokens[pos] if pos < len(tokens) else None)
1416
+ name = tokens[pos].value
1417
+ pos += 1
1418
+ if pos >= len(tokens) or tokens[pos].type != TokenType.EQUAL:
1419
+ raise ParseError('Expected "="', tokens[pos] if pos < len(tokens) else None)
1420
+ pos += 1
1421
+ target_type, pos = parse_type(tokens, pos)
1422
+ target_type_str = _type_to_str(target_type) if isinstance(target_type, tuple) else target_type
1423
+ _expect_newline(tokens, pos, tok)
1424
+ if pos < len(tokens) and tokens[pos].type == TokenType.NEWLINE:
1425
+ pos += 1
1426
+ return TypeAlias(name, target_type_str, token=tok), pos
1427
+
1428
+
1351
1429
  def parse_def(tokens: list[Token], pos: int):
1352
1430
  tok = tokens[pos]
1353
1431
  pos += 1
@@ -107,6 +107,15 @@ class LLVM:
107
107
 
108
108
  def __init__(self, no_userspace=False, enable_extensions=True):
109
109
  self.module = ir.Module("main")
110
+ try:
111
+ import llvmlite.binding as _binding
112
+ _binding.initialize_native_target()
113
+ _target = _binding.Target.from_default_triple()
114
+ _tm = _target.create_target_machine()
115
+ self.module.triple = _tm.triple
116
+ self.module.data_layout = _tm.target_data.get_data_layout()
117
+ except Exception:
118
+ pass
110
119
  self.builder = None
111
120
  self.functions = {}
112
121
  self.global_vars = {}
@@ -674,6 +683,9 @@ class LLVM:
674
683
  return self.builder.gep(obj, [idx], inbounds=True)
675
684
 
676
685
  def emit_attr(self, node: Attr):
686
+ enum_val = getattr(node, '_enum_member_value', None)
687
+ if enum_val is not None:
688
+ return ir.Constant(ir.IntType(32), enum_val)
677
689
  ptr = self._emit_lvalue_attr(node)
678
690
  return self.builder.load(ptr)
679
691
 
@@ -1067,6 +1079,18 @@ class LLVM:
1067
1079
  cmp = self.builder.call(self.functions['bigint_cmp'], [left, right])
1068
1080
  return self.builder.icmp_signed('>=', cmp, ir.Constant(_i64, 0))
1069
1081
 
1082
+ # String equality via strcmp (content, not pointer comparison)
1083
+ if node.op in (TokenType.EQ_EQ, TokenType.NOT_EQ):
1084
+ left_str = getattr(node.left, 'inferred_type', None) == 'str'
1085
+ right_str = getattr(node.right, 'inferred_type', None) == 'str'
1086
+ if left_str and right_str:
1087
+ cmp = self.builder.call(self._strcmp_fn, [left, right])
1088
+ zero = ir.Constant(_i32, 0)
1089
+ if node.op == TokenType.EQ_EQ:
1090
+ return self.builder.icmp_signed('==', cmp, zero)
1091
+ else:
1092
+ return self.builder.icmp_signed('!=', cmp, zero)
1093
+
1070
1094
  left, right = self._promote(left, right)
1071
1095
  is_float = isinstance(left.type, ir.DoubleType) or isinstance(right.type, ir.DoubleType)
1072
1096
  if is_float:
@@ -1096,20 +1120,6 @@ class LLVM:
1096
1120
  case TokenType.NOT_EQ:
1097
1121
  return self.builder.fcmp_ordered('!=', left, right)
1098
1122
 
1099
- # String equality via strcmp (content, not pointer comparison)
1100
- if node.op in (TokenType.EQ_EQ, TokenType.NOT_EQ):
1101
- left_str = getattr(node.left, 'inferred_type', None) == 'str'
1102
- right_str = getattr(node.right, 'inferred_type', None) == 'str'
1103
- if left_str and right_str:
1104
- fnty = ir.FunctionType(_i32, [_i8ptr, _i8ptr])
1105
- fn = ir.Function(self.module, fnty, '__cpy_strcmp')
1106
- cmp = self.builder.call(fn, [left, right])
1107
- zero = ir.Constant(_i32, 0)
1108
- if node.op == TokenType.EQ_EQ:
1109
- return self.builder.icmp_signed('==', cmp, zero)
1110
- else:
1111
- return self.builder.icmp_signed('!=', cmp, zero)
1112
-
1113
1123
  match node.op:
1114
1124
  case TokenType.PLUS:
1115
1125
  return self.builder.add(left, right)
@@ -82,13 +82,15 @@ def optimize(mod, opt_level=3):
82
82
  npm.run(mod, pb)
83
83
 
84
84
 
85
- def _compile_sources_object(src_files, target_triple=None):
85
+ def _compile_sources_object(src_files, target_triple=None, pic=False):
86
86
  objs = []
87
87
  for src in src_files:
88
88
  obj = src.rsplit('.', 1)[0] + '.o'
89
89
  cmd = ['clang', '-c', '-O3', '-o', obj, src]
90
90
  if target_triple:
91
91
  cmd.extend(['-target', target_triple])
92
+ if pic:
93
+ cmd.append('-fPIC')
92
94
  r = subprocess.run(cmd, capture_output=True, text=True)
93
95
  if r.returncode != 0:
94
96
  print(f'error compiling {src}: {r.stderr}', file=__import__('sys').stderr)
@@ -110,7 +112,7 @@ def _maybe_compile(module):
110
112
  return prog, src_files
111
113
  return module, None
112
114
 
113
- def run_jit(module, opt_level=3, src_files=None, no_userspace=False):
115
+ def run_jit(module, opt_level=3, src_files=None, no_userspace=False, pic=False):
114
116
  module, src_files_auto = _maybe_compile(module)
115
117
  if src_files_auto is not None:
116
118
  src_files = src_files_auto
@@ -128,16 +130,15 @@ def run_jit(module, opt_level=3, src_files=None, no_userspace=False):
128
130
  with open(src) as f:
129
131
  src_ir = f.read()
130
132
  else:
131
- ll = src.rsplit('.', 1)[0] + '.ll'
132
133
  r = subprocess.run(
133
134
  ['clang', '-S', '-emit-llvm', '-O0', '-target', target.triple,
134
- '-fno-stack-protector', '-o', ll, src],
135
- capture_output=True, text=True
136
- )
135
+ '-fno-stack-protector', '-o', '-', src],
136
+ capture_output=True, text=True)
137
+
137
138
  if r.returncode != 0:
138
139
  print(f'error compiling {src}: {r.stderr}', file=__import__('sys').stderr)
139
140
  raise SystemExit(1)
140
- src_ir = open(ll).read()
141
+ src_ir = r.stdout
141
142
  src_ir = _remove_probe_stack_ir(src_ir)
142
143
  src_mod = binding.parse_assembly(src_ir)
143
144
  binding.link_modules(mod, src_mod)
@@ -232,6 +233,15 @@ def run_jit(module, opt_level=3, src_files=None, no_userspace=False):
232
233
  _map_libc_fn(engine, mod, 'memcpy', None, ctypes.c_void_p,
233
234
  argtypes=[ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int])
234
235
 
236
+ try:
237
+ fn = mod.get_function('strcmp')
238
+ cfunctype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p)
239
+ cfn = cfunctype(_libc.strcmp)
240
+ _callbacks.append(cfn)
241
+ engine.add_global_mapping(fn, ctypes.cast(cfn, ctypes.c_void_p).value)
242
+ except NameError:
243
+ pass
244
+
235
245
  try:
236
246
  fn = mod.get_function('__cpy_strcmp')
237
247
  cfunctype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p)
@@ -271,7 +281,7 @@ def _map_libc_fn(engine, mod, name, argtype, restype, argtypes=None):
271
281
  pass
272
282
 
273
283
 
274
- def run_aot(module, output="program.o", opt_level=3, src_files=None, no_userspace=False):
284
+ def run_aot(module, output="program.o", opt_level=3, src_files=None, no_userspace=False, pic=False):
275
285
  llvm_ir = str(module)
276
286
  import llvmlite.binding as binding
277
287
  binding.initialize_native_target()
@@ -285,7 +295,10 @@ def run_aot(module, output="program.o", opt_level=3, src_files=None, no_userspac
285
295
  mod.verify()
286
296
 
287
297
  target = binding.Target.from_default_triple()
288
- target_machine = target.create_target_machine()
298
+ if pic:
299
+ target_machine = target.create_target_machine(reloc='pic')
300
+ else:
301
+ target_machine = target.create_target_machine()
289
302
 
290
303
  obj = target_machine.emit_object(mod)
291
304
 
@@ -295,8 +308,11 @@ def run_aot(module, output="program.o", opt_level=3, src_files=None, no_userspac
295
308
  objs = [output]
296
309
  for src in (src_files or []):
297
310
  src_obj = src.rsplit('.', 1)[0] + '.o'
311
+ cmd = ['clang', '-c', '-O3', '-o', src_obj, src]
312
+ if pic:
313
+ cmd.append('-fPIC')
298
314
  r = subprocess.run(
299
- ['clang', '-c', '-O3', '-o', src_obj, src],
315
+ cmd,
300
316
  capture_output=True, text=True
301
317
  )
302
318
  if r.returncode != 0:
@@ -306,8 +322,11 @@ def run_aot(module, output="program.o", opt_level=3, src_files=None, no_userspac
306
322
 
307
323
  if not no_userspace:
308
324
  runtime_obj = output + '.runtime.o'
325
+ cmd = ['clang', '-c', '-O3', '-o', runtime_obj, _RUNTIME_C]
326
+ if pic:
327
+ cmd.append('-fPIC')
309
328
  r = subprocess.run(
310
- ['clang', '-c', '-O3', '-o', runtime_obj, _RUNTIME_C],
329
+ cmd,
311
330
  capture_output=True, text=True
312
331
  )
313
332
  if r.returncode == 0:
@@ -209,8 +209,13 @@ class HookRegistry:
209
209
  """Get the current compiler context."""
210
210
  return self._context.copy()
211
211
 
212
+ def _is_duplicate(self, existing_list, package_name, hook_path) -> bool:
213
+ return any(r.package_name == package_name and r.hook_path == hook_path for r in existing_list)
214
+
212
215
  def register_lexer_hook(self, hook: LexerHook, priority: int = 0) -> None:
213
216
  """Register a lexer hook."""
217
+ if self._is_duplicate(self._lexer_hooks, hook.package_name, hook.hook_path):
218
+ return
214
219
  registration = HookRegistration(
215
220
  hook=hook,
216
221
  priority=priority,
@@ -219,9 +224,11 @@ class HookRegistry:
219
224
  )
220
225
  self._lexer_hooks.append(registration)
221
226
  self._lexer_hooks.sort(key=lambda r: r.priority, reverse=True)
222
-
227
+
223
228
  def register_parser_hook(self, hook: ParserHook, priority: int = 0) -> None:
224
229
  """Register a parser hook."""
230
+ if self._is_duplicate(self._parser_hooks, hook.package_name, hook.hook_path):
231
+ return
225
232
  registration = HookRegistration(
226
233
  hook=hook,
227
234
  priority=priority,
@@ -230,9 +237,11 @@ class HookRegistry:
230
237
  )
231
238
  self._parser_hooks.append(registration)
232
239
  self._parser_hooks.sort(key=lambda r: r.priority, reverse=True)
233
-
240
+
234
241
  def register_semantic_hook(self, hook: SemanticHook, priority: int = 0) -> None:
235
242
  """Register a semantic analysis hook."""
243
+ if self._is_duplicate(self._semantic_hooks, hook.package_name, hook.hook_path):
244
+ return
236
245
  registration = HookRegistration(
237
246
  hook=hook,
238
247
  priority=priority,
@@ -244,6 +253,8 @@ class HookRegistry:
244
253
 
245
254
  def register_codegen_hook(self, hook: CodegenHook, priority: int = 0) -> None:
246
255
  """Register a code generation hook."""
256
+ if self._is_duplicate(self._codegen_hooks, hook.package_name, hook.hook_path):
257
+ return
247
258
  registration = HookRegistration(
248
259
  hook=hook,
249
260
  priority=priority,
@@ -252,9 +263,11 @@ class HookRegistry:
252
263
  )
253
264
  self._codegen_hooks.append(registration)
254
265
  self._codegen_hooks.sort(key=lambda r: r.priority, reverse=True)
255
-
266
+
256
267
  def register_runtime_hook(self, hook: RuntimeHook, priority: int = 0) -> None:
257
268
  """Register a runtime hook."""
269
+ if self._is_duplicate(self._runtime_hooks, hook.package_name, hook.hook_path):
270
+ return
258
271
  registration = HookRegistration(
259
272
  hook=hook,
260
273
  priority=priority,
@@ -72,6 +72,7 @@ _BASE_KEYWORDS = {
72
72
  'let',
73
73
  'try', 'except', 'raise',
74
74
  'asm',
75
+ 'enum', 'type',
75
76
  }
76
77
 
77
78
  # Additional keywords registered by packages
@@ -35,13 +35,15 @@ class Linker:
35
35
  def cc(self):
36
36
  return self._cc
37
37
 
38
- def compile_c(self, src, output=None, opt_level=3, debug=False):
38
+ def compile_c(self, src, output=None, opt_level=3, debug=False, pic=False):
39
39
  if output is None:
40
40
  base = src.rsplit('.', 1)[0] if '.' in src else src
41
41
  output = base + '.o'
42
42
  cmd = [self._cc, '-c']
43
43
  if debug:
44
44
  cmd.append('-g')
45
+ if pic:
46
+ cmd.append('-fPIC')
45
47
  if opt_level is not None:
46
48
  cmd.append(f'-O{opt_level}')
47
49
  cmd.extend(['-o', output, src])
@@ -52,10 +54,12 @@ class Linker:
52
54
  return output
53
55
 
54
56
  def link(self, objects, output, libraries=None, library_paths=None,
55
- shared=False, debug=False, opt_level=3, frameworks=None):
57
+ shared=False, debug=False, opt_level=3, frameworks=None, pic=False):
56
58
  cmd = [self._cc]
57
59
  if shared:
58
60
  cmd.append('-shared')
61
+ if pic:
62
+ cmd.append('-fPIC')
59
63
  if debug:
60
64
  cmd.append('-g')
61
65
  if opt_level is not None:
@@ -77,13 +81,13 @@ class Linker:
77
81
 
78
82
 
79
83
  def build(objects, output=None, libraries=None, library_paths=None,
80
- shared=False, debug=False, opt_level=3, cc=None, frameworks=None):
84
+ shared=False, debug=False, opt_level=3, cc=None, frameworks=None, pic=False):
81
85
  linker = Linker(cc)
82
86
 
83
87
  final_objects = []
84
88
  for src in objects:
85
89
  if src.endswith('.c'):
86
- obj = linker.compile_c(src, opt_level=opt_level, debug=debug)
90
+ obj = linker.compile_c(src, opt_level=opt_level, debug=debug, pic=pic)
87
91
  final_objects.append(obj)
88
92
  else:
89
93
  final_objects.append(src)
@@ -100,5 +104,5 @@ def build(objects, output=None, libraries=None, library_paths=None,
100
104
  libraries=libraries, library_paths=library_paths,
101
105
  shared=shared,
102
106
  debug=debug, opt_level=opt_level,
103
- frameworks=frameworks,
107
+ frameworks=frameworks, pic=pic,
104
108
  )
@@ -283,9 +283,9 @@ def _collect_frameworks(nodes):
283
283
  return list(set(frameworks))
284
284
 
285
285
 
286
- def cmd_build(args, tab_size=4, strict=False, no_userspace=False):
286
+ def cmd_build(args, tab_size=4, strict=False, no_userspace=False, pic=False):
287
287
  if not args:
288
- print('Usage: cpy build [--output O] [--debug] [--opt N] [--no-userspace] <source.cpy>', file=sys.stderr)
288
+ print('Usage: cpy build [--output O] [--debug] [--opt N] [--no-userspace] [--pic] <source.cpy>', file=sys.stderr)
289
289
  sys.exit(1)
290
290
 
291
291
  output = None
@@ -308,6 +308,9 @@ def cmd_build(args, tab_size=4, strict=False, no_userspace=False):
308
308
  elif a == '--no-userspace':
309
309
  no_userspace = True
310
310
  i += 1
311
+ elif a == '--pic':
312
+ pic = True
313
+ i += 1
311
314
  elif a == '--opt' and i + 1 < len(args):
312
315
  opt = int(args[i + 1])
313
316
  i += 2
@@ -319,7 +322,7 @@ def cmd_build(args, tab_size=4, strict=False, no_userspace=False):
319
322
  sys.exit(1)
320
323
 
321
324
  if not src_file:
322
- print('Usage: cpy build [--output O] [--debug] [--opt N] <source.cpy>', file=sys.stderr)
325
+ print('Usage: cpy build [--output O] [--debug] [--opt N] [--pic] <source.cpy>', file=sys.stderr)
323
326
  sys.exit(1)
324
327
 
325
328
  with open(src_file) as f:
@@ -345,7 +348,10 @@ def cmd_build(args, tab_size=4, strict=False, no_userspace=False):
345
348
  mod.verify()
346
349
 
347
350
  target = binding.Target.from_default_triple()
348
- target_machine = target.create_target_machine()
351
+ if pic:
352
+ target_machine = target.create_target_machine(reloc='pic')
353
+ else:
354
+ target_machine = target.create_target_machine()
349
355
  obj = target_machine.emit_object(mod)
350
356
  with open(obj_file, 'wb') as f:
351
357
  f.write(obj)
@@ -354,16 +360,16 @@ def cmd_build(args, tab_size=4, strict=False, no_userspace=False):
354
360
  objs = [obj_file]
355
361
  for src in (src_files or []):
356
362
  src_obj = src.rsplit('.', 1)[0] + '.o'
357
- l.compile_c(src, output=src_obj, opt_level=opt, debug=debug)
363
+ l.compile_c(src, output=src_obj, opt_level=opt, debug=debug, pic=pic)
358
364
  objs.append(src_obj)
359
365
 
360
366
  if not no_userspace:
361
367
  runtime_obj = out_base + '.runtime.o'
362
- l.compile_c(_RUNTIME_C, output=runtime_obj, opt_level=opt, debug=debug)
368
+ l.compile_c(_RUNTIME_C, output=runtime_obj, opt_level=opt, debug=debug, pic=pic)
363
369
  objs.append(runtime_obj)
364
370
 
365
371
  executable = output or out_base
366
- l.link(objs, executable, opt_level=opt, debug=debug, frameworks=frameworks)
372
+ l.link(objs, executable, opt_level=opt, debug=debug, frameworks=frameworks, pic=pic)
367
373
  print(f'Wrote {executable}')
368
374
 
369
375
 
@@ -374,6 +380,7 @@ def main():
374
380
 
375
381
  strict = False
376
382
  no_userspace = False
383
+ pic = False
377
384
  while args and args[0].startswith('--'):
378
385
  flag = args.pop(0)
379
386
  if flag == '--tab-size':
@@ -382,6 +389,8 @@ def main():
382
389
  strict = True
383
390
  elif flag == '--no-userspace':
384
391
  no_userspace = True
392
+ elif flag == '--pic':
393
+ pic = True
385
394
  elif flag == '--ast':
386
395
  mode = 'ast'
387
396
  elif flag == '--emit-llvm':
@@ -395,12 +404,12 @@ def main():
395
404
  sys.exit(1)
396
405
 
397
406
  if not args:
398
- print('Usage: cpy [--tab-size N] [--strict] [--no-userspace] [--ast|--emit-llvm|--jit|--aot] <source file>', file=sys.stderr)
399
- print(' cpy build [--output O] [--debug] [--opt N] [--no-userspace] <source.cpy>', file=sys.stderr)
407
+ print('Usage: cpy [--tab-size N] [--strict] [--no-userspace] [--pic] [--ast|--emit-llvm|--jit|--aot] <source file>', file=sys.stderr)
408
+ print(' cpy build [--output O] [--debug] [--opt N] [--no-userspace] [--pic] <source.cpy>', file=sys.stderr)
400
409
  sys.exit(1)
401
410
 
402
411
  if args[0] == 'build':
403
- cmd_build(args[1:], tab_size=tab_size, strict=strict, no_userspace=no_userspace)
412
+ cmd_build(args[1:], tab_size=tab_size, strict=strict, no_userspace=no_userspace, pic=pic)
404
413
  return
405
414
 
406
415
  with open(args[0]) as f:
@@ -419,10 +428,10 @@ def main():
419
428
  elif mode == 'aot':
420
429
  out_base = args[0].rsplit('.', 1)[0] if '.' in args[0] else 'program'
421
430
  obj_file = 'program.o'
422
- run_aot(prog, output=obj_file, src_files=src_files, no_userspace=no_userspace)
431
+ run_aot(prog, output=obj_file, src_files=src_files, no_userspace=no_userspace, pic=pic)
423
432
  print(f'Wrote {out_base}')
424
433
  else:
425
- run_jit(prog, src_files=src_files, no_userspace=no_userspace)
434
+ run_jit(prog, src_files=src_files, no_userspace=no_userspace, pic=pic)
426
435
 
427
436
 
428
437
  if __name__ == '__main__':
@@ -27,6 +27,7 @@ from .astparse import (
27
27
  VarDecl, Break, Continue, Switch, Import, While,
28
28
  NewExpr, Deref, AddrOf, SizeOf, StructDef, Field, Input,
29
29
  InputStr, Signed67, Try, Raise, ExceptHandler, InlineAsm,
30
+ EnumDef, TypeAlias,
30
31
  parse_file, ParseError,
31
32
  )
32
33
  from .clib import resolve_library, parse_header_file, parse_c_source
@@ -360,6 +361,12 @@ class SemanticAnalyzer:
360
361
  return None
361
362
  if sym.const_value is not None:
362
363
  node.const_value = sym.const_value
364
+ if sym.kind in ('enum', 'struct'):
365
+ node.inferred_type = node.name
366
+ return node.name
367
+ if sym.kind == 'type_alias':
368
+ node.inferred_type = sym.type
369
+ return sym.type
363
370
  node.inferred_type = sym.type
364
371
  return sym.type
365
372
 
@@ -581,6 +588,17 @@ class SemanticAnalyzer:
581
588
  obj_t = self._infer_type(node.obj)
582
589
  if obj_t:
583
590
  lookup_t = obj_t[:-1] if obj_t.endswith('*') else obj_t
591
+ sym = self.current_scope.lookup(lookup_t)
592
+ if sym and sym.kind == 'enum':
593
+ member_sym = self.current_scope.lookup(f'{lookup_t}.{node.name}')
594
+ if member_sym and member_sym.kind == 'enum_member':
595
+ node._enum_member_value = member_sym.const_value
596
+ return 'int'
597
+ self.error(
598
+ f'enum `{lookup_t}` has no member `{node.name}`',
599
+ node
600
+ )
601
+ return None
584
602
  struct_sym = self.current_scope.lookup(lookup_t)
585
603
  if struct_sym and struct_sym.kind == 'struct' and struct_sym.node:
586
604
  for field in struct_sym.node.fields:
@@ -707,6 +725,10 @@ class SemanticAnalyzer:
707
725
  self._visit_import(node)
708
726
  elif isinstance(node, StructDef):
709
727
  self._visit_struct(node, scope)
728
+ elif isinstance(node, EnumDef):
729
+ self._visit_enum(node, scope)
730
+ elif isinstance(node, TypeAlias):
731
+ self._visit_type_alias(node, scope)
710
732
  elif isinstance(node, Try):
711
733
  self._visit_try(node, scope)
712
734
  elif isinstance(node, Raise):
@@ -1160,7 +1182,8 @@ class SemanticAnalyzer:
1160
1182
  self._infer_type(node.target)
1161
1183
 
1162
1184
  def _visit_vardecl(self, node: VarDecl, scope: Scope | None = None):
1163
- val_type = node.var_type
1185
+ val_type = self._resolve_type_alias(node.var_type)
1186
+ node.var_type = val_type
1164
1187
  s = scope or self.current_scope
1165
1188
  existing = s.lookup_local(node.name)
1166
1189
  if existing:
@@ -1283,6 +1306,54 @@ class SemanticAnalyzer:
1283
1306
  for field in node.fields:
1284
1307
  struct_scope.define(field.name, Symbol('field', field.type_expr, node))
1285
1308
 
1309
+ def _visit_enum(self, node: EnumDef, scope: Scope | None = None):
1310
+ s = scope or self.globals
1311
+ existing = s.lookup_local(node.name)
1312
+ if existing:
1313
+ self.error(f'redefinition of enum `{node.name}`', node)
1314
+ return
1315
+ s.define(node.name, Symbol('enum', None, node))
1316
+ for i, member in enumerate(node.members):
1317
+ if member['value'] is not None:
1318
+ val = self._eval_const_expr(member['value'])
1319
+ member['_const_value'] = val
1320
+ else:
1321
+ member['_const_value'] = i
1322
+ member['_auto_index'] = i
1323
+ sym = Symbol('enum_member', node.name, node)
1324
+ sym.const_value = member['_const_value']
1325
+ s.define(f'{node.name}.{member["name"]}', sym)
1326
+
1327
+ def _visit_type_alias(self, node: TypeAlias, scope: Scope | None = None):
1328
+ s = scope or self.globals
1329
+ existing = s.lookup_local(node.name)
1330
+ if existing:
1331
+ self.error(f'redefinition of type alias `{node.name}`', node)
1332
+ return
1333
+ s.define(node.name, Symbol('type_alias', node.target_type, node))
1334
+
1335
+ def _eval_const_expr(self, node):
1336
+ if isinstance(node, Number):
1337
+ return node.value
1338
+ if isinstance(node, UnaryOp):
1339
+ val = self._eval_const_expr(node.operand)
1340
+ if node.op.name == 'MINUS':
1341
+ return -val
1342
+ if node.op.name == 'NOT':
1343
+ return not val
1344
+ if isinstance(node, BinOp):
1345
+ left = self._eval_const_expr(node.left)
1346
+ right = self._eval_const_expr(node.right)
1347
+ match node.op.name:
1348
+ case 'PLUS': return left + right
1349
+ case 'MINUS': return left - right
1350
+ case 'STAR': return left * right
1351
+ case 'SLASH': return left // right
1352
+ case 'PERCENT': return left % right
1353
+ if isinstance(node, Variable):
1354
+ return node.const_value
1355
+ return None
1356
+
1286
1357
  def _visit_while(self, node, scope: Scope | None = None):
1287
1358
  if isinstance(node, While):
1288
1359
  self._infer_type(node.cond)
@@ -1314,6 +1385,14 @@ class SemanticAnalyzer:
1314
1385
  self._loop_depth -= 1
1315
1386
  self.locals = old_locals
1316
1387
 
1388
+ def _resolve_type_alias(self, type_name):
1389
+ if type_name is None:
1390
+ return None
1391
+ sym = self.current_scope.lookup(type_name)
1392
+ if sym and sym.kind == 'type_alias':
1393
+ return self._resolve_type_alias(sym.type)
1394
+ return type_name
1395
+
1317
1396
 
1318
1397
  def analyze(source: str, nodes: list, strict: bool = False, workspace_root: str | None = None, enable_extensions: bool = True) -> str | None:
1319
1398
  analyzer = SemanticAnalyzer(source, strict=strict, workspace_root=workspace_root, enable_extensions=enable_extensions)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpyte
3
- Version: 2.0.2
3
+ Version: 2.1.0
4
4
  Summary: The Cpyte programming language compiler
5
5
  Author: Hoang Duy Tung
6
6
  License: MIT
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
File without changes