tsolc 0.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.
tsolc-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: tsolc
3
+ Version: 0.1.0
4
+ Summary: TSol Compiler - Transpiles Sol language to C
5
+ Author-email: stefand-0 <stefan.dragan95@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/stefand-0/tsol
8
+ Project-URL: Repository, https://github.com/stefand-0/tsol
9
+ Keywords: compiler,transpiler,sol,c,tsol
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Compilers
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
tsolc-0.1.0/README.md ADDED
File without changes
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tsolc"
7
+ version = "0.1.0"
8
+ description = "TSol Compiler - Transpiles Sol language to C"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [
12
+ {name = "stefand-0", email = "stefan.dragan95@gmail.com"}
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Software Development :: Compilers",
23
+ ]
24
+ keywords = ["compiler", "transpiler", "sol", "c", "tsol"]
25
+ requires-python = ">=3.10"
26
+
27
+ [project.scripts]
28
+ tsolc = "transpiler.main:main"
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/stefand-0/tsol"
32
+ Repository = "https://github.com/stefand-0/tsol"
tsolc-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,1238 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Sol Compiler - Transpiles Sol language to C, then compiles and runs with clang.
4
+
5
+ Usage:
6
+ python sol.py <filename.sol> [--run] [--keep-c]
7
+
8
+ Supports:
9
+ get("path/to/file.sol") -- import another Sol file
10
+ """
11
+
12
+ import sys
13
+ import os
14
+ import stat
15
+ import subprocess
16
+ from enum import Enum, auto
17
+ from typing import List, Dict, Optional, Tuple, Any
18
+ from dataclasses import dataclass
19
+
20
+ # =============================================================================
21
+ # TOKEN DEFINITIONS
22
+ # =============================================================================
23
+
24
+ class TokenType(Enum):
25
+ IDENTIFIER = auto()
26
+ TYPE = auto()
27
+ NUMBER = auto()
28
+ FLOAT = auto()
29
+ STRING = auto()
30
+ CHAR = auto()
31
+
32
+ PUB = auto()
33
+ IN = auto()
34
+ OUT = auto()
35
+ IF = auto()
36
+ ELSEIF = auto()
37
+ ELSE = auto()
38
+ FOR = auto()
39
+ END = auto()
40
+ STRUCT = auto()
41
+ GET = auto()
42
+ MAIN = auto()
43
+ RETURN = auto()
44
+ BREAK = auto()
45
+ CONTINUE = auto()
46
+
47
+ ASSIGN = auto()
48
+ COMMA = auto()
49
+ LBRACE = auto()
50
+ RBRACE = auto()
51
+ LPAREN = auto()
52
+ RPAREN = auto()
53
+ SEMICOLON = auto()
54
+ LBRACKET = auto()
55
+ RBRACKET = auto()
56
+ PLUSPLUS = auto()
57
+ PLUS = auto()
58
+ MINUS = auto()
59
+ MULTIPLY = auto()
60
+ DIVIDE = auto()
61
+ MODULO = auto()
62
+ EQUAL = auto()
63
+ NOTEQUAL = auto()
64
+ LESS = auto()
65
+ LESSEQUAL = auto()
66
+ GREATER = auto()
67
+ GREATEREQUAL = auto()
68
+ AND = auto()
69
+ OR = auto()
70
+ NOT = auto()
71
+ DOT = auto()
72
+ COLON = auto()
73
+ ARROW = auto()
74
+
75
+ EOF = auto()
76
+
77
+ KEYWORDS = {
78
+ 'pub': TokenType.PUB,
79
+ 'in': TokenType.IN,
80
+ 'out': TokenType.OUT,
81
+ 'if': TokenType.IF,
82
+ 'elseif': TokenType.ELSEIF,
83
+ 'else': TokenType.ELSE,
84
+ 'for': TokenType.FOR,
85
+ 'end': TokenType.END,
86
+ 'struct': TokenType.STRUCT,
87
+ 'get': TokenType.GET,
88
+ 'main': TokenType.MAIN,
89
+ 'return': TokenType.RETURN,
90
+ 'break': TokenType.BREAK,
91
+ 'continue': TokenType.CONTINUE,
92
+ }
93
+
94
+ TYPES = {
95
+ 'int', 'float', 'char', 'bool', 'string', 'void',
96
+ 'int8', 'int16', 'int32', 'int64',
97
+ 'uint8', 'uint16', 'uint32', 'uint64',
98
+ 'double', 'long', 'short', 'byte',
99
+ }
100
+
101
+ class Token:
102
+ def __init__(self, type: TokenType, value: Any = None, line: int = 0, column: int = 0):
103
+ self.type = type
104
+ self.value = value
105
+ self.line = line
106
+ self.column = column
107
+
108
+ def __repr__(self):
109
+ return f"Token({self.type.name}, {self.value!r}, line={self.line}, col={self.column})"
110
+
111
+ # =============================================================================
112
+ # LEXER
113
+ # =============================================================================
114
+
115
+ class Lexer:
116
+ def __init__(self, source: str):
117
+ self.source = source
118
+ self.position = 0
119
+ self.line = 1
120
+ self.column = 1
121
+ self.tokens = []
122
+
123
+ def error(self, msg: str):
124
+ raise SyntaxError(f"{msg} at line {self.line}, column {self.column}")
125
+
126
+ def advance(self) -> str:
127
+ char = self.source[self.position]
128
+ self.position += 1
129
+ if char == '\n':
130
+ self.line += 1
131
+ self.column = 1
132
+ else:
133
+ self.column += 1
134
+ return char
135
+
136
+ def peek(self, offset: int = 0) -> str:
137
+ pos = self.position + offset
138
+ if pos >= len(self.source):
139
+ return '\0'
140
+ return self.source[pos]
141
+
142
+ def add_token(self, type: TokenType, value: Any = None):
143
+ self.tokens.append(Token(type, value, self.line, self.column))
144
+
145
+ def tokenize(self) -> List[Token]:
146
+ while self.position < len(self.source):
147
+ char = self.peek()
148
+
149
+ if char.isspace():
150
+ self.advance()
151
+ continue
152
+
153
+ if char == '/' and self.peek(1) == '/':
154
+ while self.position < len(self.source) and self.peek() != '\n':
155
+ self.advance()
156
+ continue
157
+
158
+ if char == '/' and self.peek(1) == '*':
159
+ self.advance(); self.advance()
160
+ while self.position < len(self.source) - 1:
161
+ if self.peek() == '*' and self.peek(1) == '/':
162
+ self.advance(); self.advance()
163
+ break
164
+ self.advance()
165
+ continue
166
+
167
+ if char == '-' and self.peek(1) == '>':
168
+ self.advance(); self.advance()
169
+ self.add_token(TokenType.ASSIGN, '->')
170
+ continue
171
+
172
+ if char == '+' and self.peek(1) == '+':
173
+ self.advance(); self.advance()
174
+ self.add_token(TokenType.PLUSPLUS, '++')
175
+ continue
176
+
177
+ if char == '&' and self.peek(1) == '&':
178
+ self.advance(); self.advance()
179
+ self.add_token(TokenType.AND, '&&')
180
+ continue
181
+
182
+ if char == '|' and self.peek(1) == '|':
183
+ self.advance(); self.advance()
184
+ self.add_token(TokenType.OR, '||')
185
+ continue
186
+
187
+ if char == '=' and self.peek(1) == '=':
188
+ self.advance(); self.advance()
189
+ self.add_token(TokenType.EQUAL, '==')
190
+ continue
191
+
192
+ if char == '!' and self.peek(1) == '=':
193
+ self.advance(); self.advance()
194
+ self.add_token(TokenType.NOTEQUAL, '!=')
195
+ continue
196
+
197
+ if char == '<' and self.peek(1) == '=':
198
+ self.advance(); self.advance()
199
+ self.add_token(TokenType.LESSEQUAL, '<=')
200
+ continue
201
+
202
+ if char == '>' and self.peek(1) == '=':
203
+ self.advance(); self.advance()
204
+ self.add_token(TokenType.GREATEREQUAL, '>=')
205
+ continue
206
+
207
+ if char == '=' and self.peek(1) == '>':
208
+ self.advance(); self.advance()
209
+ self.add_token(TokenType.ARROW, '=>')
210
+ continue
211
+
212
+ single_tokens = {
213
+ ',': TokenType.COMMA,
214
+ '{': TokenType.LBRACE,
215
+ '}': TokenType.RBRACE,
216
+ '(': TokenType.LPAREN,
217
+ ')': TokenType.RPAREN,
218
+ ';': TokenType.SEMICOLON,
219
+ '[': TokenType.LBRACKET,
220
+ ']': TokenType.RBRACKET,
221
+ '+': TokenType.PLUS,
222
+ '-': TokenType.MINUS,
223
+ '*': TokenType.MULTIPLY,
224
+ '/': TokenType.DIVIDE,
225
+ '%': TokenType.MODULO,
226
+ '<': TokenType.LESS,
227
+ '>': TokenType.GREATER,
228
+ '!': TokenType.NOT,
229
+ '.': TokenType.DOT,
230
+ ':': TokenType.COLON,
231
+ }
232
+ if char in single_tokens:
233
+ self.advance()
234
+ self.add_token(single_tokens[char], char)
235
+ continue
236
+
237
+ if char == '"':
238
+ self.advance()
239
+ start_line, start_col = self.line, self.column
240
+ value = ""
241
+ while self.position < len(self.source) and self.peek() != '"':
242
+ if self.peek() == '\\':
243
+ self.advance()
244
+ esc = self.advance()
245
+ value += {'n': '\n', 't': '\t', 'r': '\r', '\\': '\\', '"': '"', '0': '\0'}.get(esc, esc)
246
+ else:
247
+ value += self.advance()
248
+ if self.position >= len(self.source):
249
+ self.error("Unterminated string literal")
250
+ self.advance()
251
+ self.tokens.append(Token(TokenType.STRING, value, start_line, start_col))
252
+ continue
253
+
254
+ if char == "'":
255
+ self.advance()
256
+ start_line, start_col = self.line, self.column
257
+ value = ""
258
+ if self.peek() == '\\':
259
+ self.advance()
260
+ esc = self.advance()
261
+ value = {'n': '\n', 't': '\t', 'r': '\r', '\\': '\\', '"': '"', '0': '\0', "'": "'"}.get(esc, esc)
262
+ else:
263
+ value = self.advance()
264
+ if self.peek() != "'":
265
+ self.error("Unterminated char literal")
266
+ self.advance()
267
+ self.tokens.append(Token(TokenType.CHAR, value, start_line, start_col))
268
+ continue
269
+
270
+ if char.isdigit():
271
+ start_line, start_col = self.line, self.column
272
+ value = ""
273
+ while self.position < len(self.source) and self.peek().isdigit():
274
+ value += self.advance()
275
+ if self.peek() == '.' and self.peek(1).isdigit():
276
+ value += self.advance()
277
+ while self.position < len(self.source) and self.peek().isdigit():
278
+ value += self.advance()
279
+ self.tokens.append(Token(TokenType.FLOAT, float(value), start_line, start_col))
280
+ else:
281
+ self.tokens.append(Token(TokenType.NUMBER, int(value), start_line, start_col))
282
+ continue
283
+
284
+ if char.isalpha() or char == '_':
285
+ start_line, start_col = self.line, self.column
286
+ value = ""
287
+ while self.position < len(self.source) and (self.peek().isalnum() or self.peek() == '_'):
288
+ value += self.advance()
289
+ if value in KEYWORDS:
290
+ self.tokens.append(Token(KEYWORDS[value], value, start_line, start_col))
291
+ elif value in TYPES:
292
+ self.tokens.append(Token(TokenType.TYPE, value, start_line, start_col))
293
+ else:
294
+ self.tokens.append(Token(TokenType.IDENTIFIER, value, start_line, start_col))
295
+ continue
296
+
297
+ self.error(f"Unexpected character: {char!r}")
298
+
299
+ self.add_token(TokenType.EOF, None)
300
+ return self.tokens
301
+
302
+ # =============================================================================
303
+ # AST NODES
304
+ # =============================================================================
305
+
306
+ @dataclass
307
+ class Program:
308
+ declarations: List[Any]
309
+
310
+ @dataclass
311
+ class ImportStmt:
312
+ path: str
313
+
314
+ @dataclass
315
+ class FuncDecl:
316
+ name: str
317
+ params: List[Tuple[str, str]]
318
+ return_type: Optional[str]
319
+ body: 'Block'
320
+ is_pub: bool = False
321
+
322
+ @dataclass
323
+ class StructDecl:
324
+ name: str
325
+ fields: List[Tuple[str, str]]
326
+ is_pub: bool = False
327
+
328
+ @dataclass
329
+ class Block:
330
+ statements: List[Any]
331
+
332
+ @dataclass
333
+ class VarDecl:
334
+ name: str
335
+ var_type: str
336
+ initializer: Optional[Any] = None
337
+
338
+ @dataclass
339
+ class Assignment:
340
+ target: Any
341
+ value: Any
342
+
343
+ @dataclass
344
+ class IfStmt:
345
+ condition: Any
346
+ then_branch: Block
347
+ elseifs: List[Tuple[Any, Block]]
348
+ else_branch: Optional[Block]
349
+
350
+ @dataclass
351
+ class ForStmt:
352
+ init: Optional[Any]
353
+ condition: Optional[Any]
354
+ increment: Optional[Any]
355
+ body: Block
356
+
357
+ @dataclass
358
+ class ForEachStmt:
359
+ var_name: str
360
+ iterable: Any
361
+ body: Block
362
+
363
+ @dataclass
364
+ class ReturnStmt:
365
+ value: Optional[Any]
366
+
367
+ @dataclass
368
+ class BreakStmt:
369
+ pass
370
+
371
+ @dataclass
372
+ class ContinueStmt:
373
+ pass
374
+
375
+ @dataclass
376
+ class ExprStmt:
377
+ expr: Any
378
+
379
+ @dataclass
380
+ class BinaryOp:
381
+ op: str
382
+ left: Any
383
+ right: Any
384
+
385
+ @dataclass
386
+ class UnaryOp:
387
+ op: str
388
+ operand: Any
389
+
390
+ @dataclass
391
+ class CallExpr:
392
+ name: str
393
+ args: List[Any]
394
+
395
+ @dataclass
396
+ class GetExpr:
397
+ obj: Any
398
+ field: str
399
+
400
+ @dataclass
401
+ class IndexExpr:
402
+ obj: Any
403
+ index: Any
404
+
405
+ @dataclass
406
+ class Identifier:
407
+ name: str
408
+
409
+ @dataclass
410
+ class NumberLiteral:
411
+ value: int
412
+
413
+ @dataclass
414
+ class FloatLiteral:
415
+ value: float
416
+
417
+ @dataclass
418
+ class StringLiteral:
419
+ value: str
420
+
421
+ @dataclass
422
+ class CharLiteral:
423
+ value: str
424
+
425
+ @dataclass
426
+ class BoolLiteral:
427
+ value: bool
428
+
429
+ @dataclass
430
+ class ArrayLiteral:
431
+ elements: List[Any]
432
+
433
+ # =============================================================================
434
+ # PARSER
435
+ # =============================================================================
436
+
437
+ class Parser:
438
+ def __init__(self, tokens: List[Token]):
439
+ self.tokens = tokens
440
+ self.position = 0
441
+
442
+ def current(self) -> Token:
443
+ return self.tokens[self.position]
444
+
445
+ def peek(self, offset: int = 0) -> Token:
446
+ pos = self.position + offset
447
+ if pos >= len(self.tokens):
448
+ return self.tokens[-1]
449
+ return self.tokens[pos]
450
+
451
+ def advance(self) -> Token:
452
+ tok = self.current()
453
+ if self.position < len(self.tokens) - 1:
454
+ self.position += 1
455
+ return tok
456
+
457
+ def expect(self, type: TokenType, msg: str = None) -> Token:
458
+ if self.current().type != type:
459
+ raise SyntaxError(
460
+ f"Expected {type.name} but got {self.current().type.name}"
461
+ f"{(' (' + msg + ')') if msg else ''} at line {self.current().line}"
462
+ )
463
+ return self.advance()
464
+
465
+ def match(self, *types: TokenType) -> bool:
466
+ return self.current().type in types
467
+
468
+ def parse_identifier(self) -> Token:
469
+ if self.match(TokenType.IDENTIFIER, TokenType.MAIN, TokenType.GET):
470
+ return self.advance()
471
+ raise SyntaxError(f"Expected identifier but got {self.current().type.name} at line {self.current().line}")
472
+
473
+ def parse(self) -> Program:
474
+ declarations = []
475
+ while not self.match(TokenType.EOF):
476
+ declarations.append(self.parse_declaration())
477
+ return Program(declarations)
478
+
479
+ def parse_declaration(self):
480
+ is_pub = False
481
+ if self.match(TokenType.PUB):
482
+ self.advance()
483
+ is_pub = True
484
+
485
+ if self.match(TokenType.STRUCT):
486
+ return self.parse_struct_decl(is_pub)
487
+ elif self.match(TokenType.GET):
488
+ return self.parse_import_stmt()
489
+ else:
490
+ return self.parse_func_decl(is_pub)
491
+
492
+ def parse_import_stmt(self) -> ImportStmt:
493
+ self.expect(TokenType.GET)
494
+ self.expect(TokenType.LPAREN)
495
+ path = self.expect(TokenType.STRING).value
496
+ self.expect(TokenType.RPAREN)
497
+ self.expect(TokenType.SEMICOLON)
498
+ return ImportStmt(path)
499
+
500
+ def parse_struct_decl(self, is_pub: bool) -> StructDecl:
501
+ self.expect(TokenType.STRUCT)
502
+ name = self.parse_identifier().value
503
+ self.expect(TokenType.LBRACE)
504
+ fields = []
505
+ while not self.match(TokenType.RBRACE):
506
+ # C-style: type name;
507
+ ftype = self.parse_type()
508
+ fname = self.parse_identifier().value
509
+ self.expect(TokenType.SEMICOLON)
510
+ fields.append((fname, ftype))
511
+ self.expect(TokenType.RBRACE)
512
+ return StructDecl(name, fields, is_pub)
513
+
514
+ def parse_func_decl(self, is_pub: bool) -> FuncDecl:
515
+ name = self.parse_identifier().value
516
+ self.expect(TokenType.LPAREN)
517
+ params = []
518
+ if not self.match(TokenType.RPAREN):
519
+ params.append(self.parse_param())
520
+ while self.match(TokenType.COMMA):
521
+ self.advance()
522
+ params.append(self.parse_param())
523
+ self.expect(TokenType.RPAREN)
524
+
525
+ return_type = None
526
+ if self.match(TokenType.OUT):
527
+ self.advance()
528
+ return_type = self.parse_type()
529
+
530
+ body = self.parse_block()
531
+ return FuncDecl(name, params, return_type, body, is_pub)
532
+
533
+ def parse_param(self) -> Tuple[str, str]:
534
+ if self.match(TokenType.IN):
535
+ self.advance()
536
+ # C-style: type name
537
+ ptype = self.parse_type()
538
+ name = self.parse_identifier().value
539
+ return (name, ptype)
540
+
541
+ def parse_type(self) -> str:
542
+ if self.match(TokenType.TYPE):
543
+ base = self.advance().value
544
+ elif self.match(TokenType.IDENTIFIER):
545
+ base = self.advance().value
546
+ else:
547
+ raise SyntaxError(f"Expected type at line {self.current().line}")
548
+ if self.match(TokenType.LBRACKET):
549
+ self.advance()
550
+ self.expect(TokenType.RBRACKET)
551
+ base += "[]"
552
+ return base
553
+
554
+ def parse_block(self) -> Block:
555
+ self.expect(TokenType.LBRACE)
556
+ statements = []
557
+ while not self.match(TokenType.RBRACE):
558
+ statements.append(self.parse_statement())
559
+ self.expect(TokenType.RBRACE)
560
+ return Block(statements)
561
+
562
+ def parse_statement(self):
563
+ if self.match(TokenType.IF):
564
+ return self.parse_if_stmt()
565
+ if self.match(TokenType.FOR):
566
+ return self.parse_for_stmt()
567
+ if self.match(TokenType.OUT):
568
+ return self.parse_return_stmt()
569
+ if self.match(TokenType.RETURN):
570
+ return self.parse_return_stmt()
571
+ if self.match(TokenType.BREAK):
572
+ self.advance()
573
+ self.expect(TokenType.SEMICOLON)
574
+ return BreakStmt()
575
+ if self.match(TokenType.CONTINUE):
576
+ self.advance()
577
+ self.expect(TokenType.SEMICOLON)
578
+ return ContinueStmt()
579
+ if self.match(TokenType.GET):
580
+ return self.parse_import_stmt()
581
+
582
+ # Variable declaration: type name -> value; OR name type -> value;
583
+ if self.match(TokenType.IDENTIFIER, TokenType.MAIN, TokenType.GET, TokenType.TYPE):
584
+ if self.peek(1).type == TokenType.TYPE or self.peek(1).type == TokenType.IDENTIFIER:
585
+ return self.parse_var_decl()
586
+ expr = self.parse_expression()
587
+ if self.match(TokenType.ASSIGN):
588
+ self.advance()
589
+ value = self.parse_expression()
590
+ self.expect(TokenType.SEMICOLON)
591
+ return Assignment(expr, value)
592
+ self.expect(TokenType.SEMICOLON)
593
+ return ExprStmt(expr)
594
+
595
+ expr = self.parse_expression()
596
+ self.expect(TokenType.SEMICOLON)
597
+ return ExprStmt(expr)
598
+
599
+ def parse_var_decl(self) -> VarDecl:
600
+ # C-style: type name -> value; (primary)
601
+ # Also supports: name type -> value; (legacy)
602
+ if self.match(TokenType.TYPE):
603
+ vtype = self.parse_type()
604
+ name = self.parse_identifier().value
605
+ else:
606
+ name = self.parse_identifier().value
607
+ vtype = self.parse_type()
608
+ init = None
609
+ if self.match(TokenType.ASSIGN):
610
+ self.advance()
611
+ init = self.parse_expression()
612
+ self.expect(TokenType.SEMICOLON)
613
+ return VarDecl(name, vtype, init)
614
+
615
+ def parse_if_stmt(self) -> IfStmt:
616
+ self.expect(TokenType.IF)
617
+ condition = self.parse_expression()
618
+ then_branch = self.parse_block()
619
+ elseifs = []
620
+ while self.match(TokenType.ELSEIF):
621
+ self.advance()
622
+ cond = self.parse_expression()
623
+ block = self.parse_block()
624
+ elseifs.append((cond, block))
625
+ else_branch = None
626
+ if self.match(TokenType.ELSE):
627
+ self.advance()
628
+ else_branch = self.parse_block()
629
+ self.expect(TokenType.END)
630
+ return IfStmt(condition, then_branch, elseifs, else_branch)
631
+
632
+ def parse_for_stmt(self):
633
+ self.expect(TokenType.FOR)
634
+ if self.match(TokenType.IDENTIFIER, TokenType.MAIN, TokenType.GET) and self.peek(1).type == TokenType.IN:
635
+ var_name = self.parse_identifier().value
636
+ self.advance() # consume 'in'
637
+ iterable = self.parse_expression()
638
+ body = self.parse_block()
639
+ self.expect(TokenType.END)
640
+ return ForEachStmt(var_name, iterable, body)
641
+
642
+ init = None
643
+ if not self.match(TokenType.SEMICOLON):
644
+ if (self.match(TokenType.IDENTIFIER, TokenType.MAIN, TokenType.GET, TokenType.TYPE) and
645
+ (self.peek(1).type == TokenType.TYPE or self.peek(1).type == TokenType.IDENTIFIER)):
646
+ init = self.parse_var_decl()
647
+ else:
648
+ init = self.parse_expression()
649
+ if self.match(TokenType.ASSIGN):
650
+ self.advance()
651
+ val = self.parse_expression()
652
+ init = Assignment(init, val)
653
+ self.expect(TokenType.SEMICOLON)
654
+
655
+ if init is None or isinstance(init, (VarDecl, Assignment)):
656
+ if init is None:
657
+ self.expect(TokenType.SEMICOLON)
658
+
659
+ condition = None
660
+ if not self.match(TokenType.SEMICOLON):
661
+ condition = self.parse_expression()
662
+ self.expect(TokenType.SEMICOLON)
663
+
664
+ increment = None
665
+ if not self.match(TokenType.LBRACE):
666
+ increment = self.parse_expression()
667
+ if self.match(TokenType.ASSIGN):
668
+ self.advance()
669
+ val = self.parse_expression()
670
+ increment = Assignment(increment, val)
671
+
672
+ body = self.parse_block()
673
+ self.expect(TokenType.END)
674
+ return ForStmt(init, condition, increment, body)
675
+
676
+ def parse_return_stmt(self) -> ReturnStmt:
677
+ if self.match(TokenType.OUT):
678
+ self.advance()
679
+ elif self.match(TokenType.RETURN):
680
+ self.advance()
681
+ value = None
682
+ if not self.match(TokenType.SEMICOLON):
683
+ value = self.parse_expression()
684
+ self.expect(TokenType.SEMICOLON)
685
+ return ReturnStmt(value)
686
+
687
+ def parse_expression(self):
688
+ return self.parse_or()
689
+
690
+ def parse_or(self):
691
+ left = self.parse_and()
692
+ while self.match(TokenType.OR):
693
+ op = self.advance().value
694
+ right = self.parse_and()
695
+ left = BinaryOp(op, left, right)
696
+ return left
697
+
698
+ def parse_and(self):
699
+ left = self.parse_equality()
700
+ while self.match(TokenType.AND):
701
+ op = self.advance().value
702
+ right = self.parse_equality()
703
+ left = BinaryOp(op, left, right)
704
+ return left
705
+
706
+ def parse_equality(self):
707
+ left = self.parse_comparison()
708
+ while self.match(TokenType.EQUAL, TokenType.NOTEQUAL):
709
+ op = self.advance().value
710
+ right = self.parse_comparison()
711
+ left = BinaryOp(op, left, right)
712
+ return left
713
+
714
+ def parse_comparison(self):
715
+ left = self.parse_term()
716
+ while self.match(TokenType.LESS, TokenType.LESSEQUAL, TokenType.GREATER, TokenType.GREATEREQUAL):
717
+ op = self.advance().value
718
+ right = self.parse_term()
719
+ left = BinaryOp(op, left, right)
720
+ return left
721
+
722
+ def parse_term(self):
723
+ left = self.parse_factor()
724
+ while self.match(TokenType.PLUS, TokenType.MINUS):
725
+ op = self.advance().value
726
+ right = self.parse_factor()
727
+ left = BinaryOp(op, left, right)
728
+ return left
729
+
730
+ def parse_factor(self):
731
+ left = self.parse_unary()
732
+ while self.match(TokenType.MULTIPLY, TokenType.DIVIDE, TokenType.MODULO):
733
+ op = self.advance().value
734
+ right = self.parse_unary()
735
+ left = BinaryOp(op, left, right)
736
+ return left
737
+
738
+ def parse_unary(self):
739
+ if self.match(TokenType.NOT, TokenType.MINUS):
740
+ op = self.advance().value
741
+ operand = self.parse_unary()
742
+ return UnaryOp(op, operand)
743
+ return self.parse_postfix()
744
+
745
+ def parse_postfix(self):
746
+ expr = self.parse_primary()
747
+ while True:
748
+ if self.match(TokenType.LPAREN):
749
+ expr = self.parse_call(expr)
750
+ elif self.match(TokenType.LBRACKET):
751
+ self.advance()
752
+ index = self.parse_expression()
753
+ self.expect(TokenType.RBRACKET)
754
+ expr = IndexExpr(expr, index)
755
+ elif self.match(TokenType.DOT):
756
+ self.advance()
757
+ field = self.parse_identifier().value
758
+ expr = GetExpr(expr, field)
759
+ elif self.match(TokenType.PLUSPLUS):
760
+ self.advance()
761
+ expr = UnaryOp('++', expr)
762
+ else:
763
+ break
764
+ return expr
765
+
766
+ def parse_call(self, callee):
767
+ self.expect(TokenType.LPAREN)
768
+ args = []
769
+ if not self.match(TokenType.RPAREN):
770
+ args.append(self.parse_expression())
771
+ while self.match(TokenType.COMMA):
772
+ self.advance()
773
+ args.append(self.parse_expression())
774
+ self.expect(TokenType.RPAREN)
775
+ name = callee.name if isinstance(callee, Identifier) else None
776
+ return CallExpr(name, args)
777
+
778
+ def parse_primary(self):
779
+ if self.match(TokenType.NUMBER):
780
+ return NumberLiteral(self.advance().value)
781
+ if self.match(TokenType.FLOAT):
782
+ return FloatLiteral(self.advance().value)
783
+ if self.match(TokenType.STRING):
784
+ return StringLiteral(self.advance().value)
785
+ if self.match(TokenType.CHAR):
786
+ return CharLiteral(self.advance().value)
787
+ if self.match(TokenType.IDENTIFIER, TokenType.MAIN, TokenType.GET):
788
+ return Identifier(self.advance().value)
789
+ if self.match(TokenType.LPAREN):
790
+ self.advance()
791
+ expr = self.parse_expression()
792
+ self.expect(TokenType.RPAREN)
793
+ return expr
794
+ if self.match(TokenType.LBRACKET):
795
+ return self.parse_array_literal()
796
+ raise SyntaxError(f"Unexpected token {self.current().type.name} at line {self.current().line}")
797
+
798
+ def parse_array_literal(self):
799
+ self.expect(TokenType.LBRACKET)
800
+ elements = []
801
+ if not self.match(TokenType.RBRACKET):
802
+ elements.append(self.parse_expression())
803
+ while self.match(TokenType.COMMA):
804
+ self.advance()
805
+ elements.append(self.parse_expression())
806
+ self.expect(TokenType.RBRACKET)
807
+ return ArrayLiteral(elements)
808
+
809
+ # =============================================================================
810
+ # C CODE GENERATOR
811
+ # =============================================================================
812
+
813
+ class CGenerator:
814
+ def __init__(self):
815
+ self.output = []
816
+ self.indent_level = 0
817
+ self.structs = {}
818
+ self.functions = {}
819
+ self.local_vars = []
820
+
821
+ def indent(self):
822
+ return " " * self.indent_level
823
+
824
+ def emit(self, line: str = ""):
825
+ if line:
826
+ self.output.append(self.indent() + line)
827
+ else:
828
+ self.output.append("")
829
+
830
+ def generate(self, program: Program) -> str:
831
+ # Deduplicate while building lookup tables
832
+ seen_structs = set()
833
+ seen_funcs = set()
834
+ for decl in program.declarations:
835
+ if isinstance(decl, StructDecl):
836
+ if decl.name not in seen_structs:
837
+ seen_structs.add(decl.name)
838
+ self.structs[decl.name] = decl
839
+ elif isinstance(decl, FuncDecl):
840
+ if decl.name not in seen_funcs:
841
+ seen_funcs.add(decl.name)
842
+ self.functions[decl.name] = decl
843
+
844
+ self.emit("#include <stdio.h>")
845
+ self.emit("#include <stdlib.h>")
846
+ self.emit("#include <string.h>")
847
+ self.emit("#include <stdint.h>")
848
+ self.emit("#include <stdbool.h>")
849
+ self.emit("")
850
+
851
+ # Struct declarations first
852
+ for name in self.structs:
853
+ self.emit_struct_decl(self.structs[name])
854
+ self.emit("")
855
+
856
+ # Forward declarations
857
+ for name in self.functions:
858
+ self.emit_forward_decl(self.functions[name])
859
+ self.emit("")
860
+
861
+ # Function definitions
862
+ for name in self.functions:
863
+ self.emit_func_decl(self.functions[name])
864
+
865
+ return "\n".join(self.output)
866
+
867
+ def emit_forward_decl(self, decl: FuncDecl):
868
+ rtype = self.map_type(decl.return_type) if decl.return_type else "void"
869
+ params = ", ".join(f"{self.map_type(t)} {n}" for n, t in decl.params) if decl.params else "void"
870
+ self.emit(f"{rtype} {decl.name}({params});")
871
+
872
+ def emit_struct_decl(self, decl: StructDecl):
873
+ self.emit(f"typedef struct {decl.name} {decl.name};")
874
+ self.emit(f"struct {decl.name} {{")
875
+ self.indent_level += 1
876
+ for fname, ftype in decl.fields:
877
+ self.emit(f"{self.map_type(ftype)} {fname};")
878
+ self.indent_level -= 1
879
+ self.emit("};")
880
+
881
+ def emit_func_decl(self, decl: FuncDecl):
882
+ rtype = self.map_type(decl.return_type) if decl.return_type else "void"
883
+ params = ", ".join(f"{self.map_type(t)} {n}" for n, t in decl.params) if decl.params else "void"
884
+ self.emit(f"{rtype} {decl.name}({params}) {{")
885
+ self.indent_level += 1
886
+ self.local_vars = [{}]
887
+ for stmt in decl.body.statements:
888
+ self.emit_stmt(stmt)
889
+ self.indent_level -= 1
890
+ self.emit("}")
891
+ self.emit("")
892
+
893
+ def map_type(self, sol_type: str) -> str:
894
+ if sol_type is None:
895
+ return "void"
896
+ if sol_type.endswith("[]"):
897
+ base = sol_type[:-2]
898
+ return f"{self.map_type(base)}*"
899
+ mapping = {
900
+ 'int': 'int',
901
+ 'float': 'float',
902
+ 'double': 'double',
903
+ 'char': 'char',
904
+ 'bool': 'bool',
905
+ 'string': 'char*',
906
+ 'void': 'void',
907
+ 'int8': 'int8_t',
908
+ 'int16': 'int16_t',
909
+ 'int32': 'int32_t',
910
+ 'int64': 'int64_t',
911
+ 'uint8': 'uint8_t',
912
+ 'uint16': 'uint16_t',
913
+ 'uint32': 'uint32_t',
914
+ 'uint64': 'uint64_t',
915
+ 'long': 'long',
916
+ 'short': 'short',
917
+ 'byte': 'unsigned char',
918
+ }
919
+ return mapping.get(sol_type, sol_type)
920
+
921
+ def emit_stmt(self, stmt):
922
+ if isinstance(stmt, VarDecl):
923
+ self.emit_var_decl(stmt)
924
+ elif isinstance(stmt, Assignment):
925
+ target = self.emit_expr(stmt.target)
926
+ value = self.emit_expr(stmt.value)
927
+ self.emit(f"{target} = {value};")
928
+ elif isinstance(stmt, IfStmt):
929
+ self.emit_if_stmt(stmt)
930
+ elif isinstance(stmt, ForStmt):
931
+ self.emit_for_stmt(stmt)
932
+ elif isinstance(stmt, ForEachStmt):
933
+ self.emit_foreach_stmt(stmt)
934
+ elif isinstance(stmt, ReturnStmt):
935
+ if stmt.value:
936
+ self.emit(f"return {self.emit_expr(stmt.value)};")
937
+ else:
938
+ self.emit("return;")
939
+ elif isinstance(stmt, BreakStmt):
940
+ self.emit("break;")
941
+ elif isinstance(stmt, ContinueStmt):
942
+ self.emit("continue;")
943
+ elif isinstance(stmt, ExprStmt):
944
+ self.emit(f"{self.emit_expr(stmt.expr)};")
945
+ elif isinstance(stmt, Block):
946
+ self.emit_block(stmt)
947
+ else:
948
+ raise RuntimeError(f"Unknown statement type: {type(stmt)}")
949
+
950
+ def _emit_var_init(self, stmt: VarDecl) -> str:
951
+ """Generate the initialization part of a variable declaration."""
952
+ ctype = self.map_type(stmt.var_type)
953
+ if stmt.initializer:
954
+ if isinstance(stmt.initializer, ArrayLiteral):
955
+ elements = ", ".join(self.emit_expr(e) for e in stmt.initializer.elements)
956
+ base_type = self.map_type(stmt.var_type[:-2]) # strip "[]"
957
+ return f"{ctype} {stmt.name} = ({base_type}[]){{{elements}}}"
958
+ else:
959
+ init = self.emit_expr(stmt.initializer)
960
+ if isinstance(stmt.initializer, NumberLiteral):
961
+ init = self.emit_unsigned_literal(stmt.initializer.value, stmt.var_type)
962
+ return f"{ctype} {stmt.name} = {init}"
963
+ else:
964
+ return f"{ctype} {stmt.name}"
965
+
966
+ def emit_var_decl(self, stmt: VarDecl):
967
+ self.emit(self._emit_var_init(stmt) + ";")
968
+ self.local_vars[-1][stmt.name] = stmt.var_type
969
+
970
+ def emit_unsigned_literal(self, value: int, sol_type: str) -> str:
971
+ if sol_type in ('uint8', 'uint16', 'uint32'):
972
+ return f"{value}U"
973
+ if sol_type == 'uint64':
974
+ return f"{value}ULL"
975
+ return str(value)
976
+
977
+ def emit_if_stmt(self, stmt: IfStmt):
978
+ cond = self.emit_expr(stmt.condition)
979
+ self.emit(f"if ({cond}) {{")
980
+ self.indent_level += 1
981
+ for s in stmt.then_branch.statements:
982
+ self.emit_stmt(s)
983
+ self.indent_level -= 1
984
+ self.emit("}")
985
+ for cond, block in stmt.elseifs:
986
+ c = self.emit_expr(cond)
987
+ self.emit(f"else if ({c}) {{")
988
+ self.indent_level += 1
989
+ for s in block.statements:
990
+ self.emit_stmt(s)
991
+ self.indent_level -= 1
992
+ self.emit("}")
993
+ if stmt.else_branch:
994
+ self.emit("else {")
995
+ self.indent_level += 1
996
+ for s in stmt.else_branch.statements:
997
+ self.emit_stmt(s)
998
+ self.indent_level -= 1
999
+ self.emit("}")
1000
+
1001
+ def emit_for_stmt(self, stmt: ForStmt):
1002
+ parts = []
1003
+ if stmt.init:
1004
+ if isinstance(stmt.init, VarDecl):
1005
+ parts.append(self._emit_var_init(stmt.init))
1006
+ elif isinstance(stmt.init, Assignment):
1007
+ parts.append(f"{self.emit_expr(stmt.init.target)} = {self.emit_expr(stmt.init.value)}")
1008
+ else:
1009
+ parts.append(self.emit_expr(stmt.init))
1010
+ else:
1011
+ parts.append("")
1012
+ parts.append(self.emit_expr(stmt.condition) if stmt.condition else "")
1013
+ if stmt.increment:
1014
+ if isinstance(stmt.increment, Assignment):
1015
+ parts.append(f"{self.emit_expr(stmt.increment.target)} = {self.emit_expr(stmt.increment.value)}")
1016
+ else:
1017
+ parts.append(self.emit_expr(stmt.increment))
1018
+ else:
1019
+ parts.append("")
1020
+ self.emit(f"for ({parts[0]}; {parts[1]}; {parts[2]}) {{")
1021
+ self.indent_level += 1
1022
+ for s in stmt.body.statements:
1023
+ self.emit_stmt(s)
1024
+ self.indent_level -= 1
1025
+ self.emit("}")
1026
+
1027
+ def emit_foreach_stmt(self, stmt: ForEachStmt):
1028
+ self.emit(f"// foreach: {stmt.var_name} in iterable")
1029
+ self.emit("{")
1030
+ self.indent_level += 1
1031
+ for s in stmt.body.statements:
1032
+ self.emit_stmt(s)
1033
+ self.indent_level -= 1
1034
+ self.emit("}")
1035
+
1036
+ def emit_block(self, block: Block):
1037
+ self.emit("{")
1038
+ self.indent_level += 1
1039
+ for stmt in block.statements:
1040
+ self.emit_stmt(stmt)
1041
+ self.indent_level -= 1
1042
+ self.emit("}")
1043
+
1044
+ def emit_expr(self, expr) -> str:
1045
+ if isinstance(expr, NumberLiteral):
1046
+ return str(expr.value)
1047
+ if isinstance(expr, FloatLiteral):
1048
+ return str(expr.value)
1049
+ if isinstance(expr, StringLiteral):
1050
+ escaped = expr.value.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\t', '\\t')
1051
+ return f'"{escaped}"'
1052
+ if isinstance(expr, CharLiteral):
1053
+ return f"'{expr.value}'"
1054
+ if isinstance(expr, BoolLiteral):
1055
+ return "true" if expr.value else "false"
1056
+ if isinstance(expr, Identifier):
1057
+ return expr.name
1058
+ if isinstance(expr, BinaryOp):
1059
+ left = self.emit_expr(expr.left)
1060
+ right = self.emit_expr(expr.right)
1061
+ return f"{left} {expr.op} {right}"
1062
+ if isinstance(expr, UnaryOp):
1063
+ operand = self.emit_expr(expr.operand)
1064
+ if expr.op == '++':
1065
+ return f"{operand}++"
1066
+ return f"{expr.op}{operand}"
1067
+ if isinstance(expr, CallExpr):
1068
+ args = ", ".join(self.emit_expr(a) for a in expr.args)
1069
+ return f"{expr.name}({args})"
1070
+ if isinstance(expr, GetExpr):
1071
+ obj = self.emit_expr(expr.obj)
1072
+ return f"{obj}.{expr.field}"
1073
+ if isinstance(expr, IndexExpr):
1074
+ obj = self.emit_expr(expr.obj)
1075
+ index = self.emit_expr(expr.index)
1076
+ return f"{obj}[{index}]"
1077
+ if isinstance(expr, ArrayLiteral):
1078
+ elements = ", ".join(self.emit_expr(e) for e in expr.elements)
1079
+ return f"{{{elements}}}"
1080
+ raise RuntimeError(f"Unknown expression type: {type(expr)}")
1081
+
1082
+ # =============================================================================
1083
+ # IMPORT RESOLVER
1084
+ # =============================================================================
1085
+
1086
+ class ImportResolver:
1087
+ """Resolves get("path") imports by recursively loading and merging Sol files."""
1088
+
1089
+ def __init__(self, base_dir: str):
1090
+ self.base_dir = base_dir
1091
+ self.loaded = set()
1092
+ self.all_declarations = []
1093
+ self.seen_funcs = set()
1094
+ self.seen_structs = set()
1095
+
1096
+ def resolve(self, source_path: str) -> Program:
1097
+ self._load_file(source_path)
1098
+ return Program(self.all_declarations)
1099
+
1100
+ def _resolve_path(self, path: str) -> str:
1101
+ if os.path.isabs(path):
1102
+ return path
1103
+ return os.path.normpath(os.path.join(self.base_dir, path))
1104
+
1105
+ def _load_file(self, source_path: str):
1106
+ abs_path = self._resolve_path(source_path)
1107
+ abs_path = os.path.abspath(abs_path)
1108
+
1109
+ if abs_path in self.loaded:
1110
+ return
1111
+ self.loaded.add(abs_path)
1112
+
1113
+ if not os.path.exists(abs_path):
1114
+ raise FileNotFoundError(f"Import not found: {source_path} (resolved to {abs_path})")
1115
+
1116
+ with open(abs_path, 'r') as f:
1117
+ source = f.read()
1118
+
1119
+ lexer = Lexer(source)
1120
+ tokens = lexer.tokenize()
1121
+
1122
+ parser = Parser(tokens)
1123
+ ast = parser.parse()
1124
+
1125
+ file_dir = os.path.dirname(abs_path)
1126
+
1127
+ for decl in ast.declarations:
1128
+ if isinstance(decl, ImportStmt):
1129
+ imported_path = decl.path
1130
+ if not os.path.isabs(imported_path):
1131
+ imported_path = os.path.join(file_dir, imported_path)
1132
+ self._load_file(imported_path)
1133
+ elif isinstance(decl, FuncDecl):
1134
+ if decl.name not in self.seen_funcs:
1135
+ self.seen_funcs.add(decl.name)
1136
+ self.all_declarations.append(decl)
1137
+ elif isinstance(decl, StructDecl):
1138
+ if decl.name not in self.seen_structs:
1139
+ self.seen_structs.add(decl.name)
1140
+ self.all_declarations.append(decl)
1141
+ else:
1142
+ self.all_declarations.append(decl)
1143
+
1144
+ # =============================================================================
1145
+ # DRIVER
1146
+ # =============================================================================
1147
+
1148
+ def find_c_compiler() -> str:
1149
+ for compiler in ['clang', 'gcc', 'cc']:
1150
+ try:
1151
+ subprocess.run([compiler, '--version'], capture_output=True, check=False)
1152
+ return compiler
1153
+ except FileNotFoundError:
1154
+ continue
1155
+ raise RuntimeError("No C compiler found (tried: clang, gcc, cc). Please install clang or gcc.")
1156
+
1157
+ def compile_sol(source_path: str, run: bool = False, keep_c: bool = False) -> str:
1158
+ base_dir = os.path.dirname(os.path.abspath(source_path)) or os.getcwd()
1159
+
1160
+ resolver = ImportResolver(base_dir)
1161
+ ast = resolver.resolve(source_path)
1162
+
1163
+ generator = CGenerator()
1164
+ c_code = generator.generate(ast)
1165
+
1166
+ base = os.path.splitext(source_path)[0]
1167
+ c_path = base + ".c"
1168
+ with open(c_path, 'w') as f:
1169
+ f.write(c_code)
1170
+ print(f"Generated C code: {c_path}")
1171
+
1172
+ compiler = find_c_compiler()
1173
+ exe_path = base
1174
+ if sys.platform == 'win32':
1175
+ exe_path += ".exe"
1176
+
1177
+ cmd = [compiler, c_path, "-o", exe_path, "-Wall", "-Wextra", "-std=c11"]
1178
+ print(f"Running: {' '.join(cmd)}")
1179
+ result = subprocess.run(cmd, capture_output=True, text=True)
1180
+ if result.returncode != 0:
1181
+ print("Compilation failed:")
1182
+ print(result.stderr)
1183
+ raise RuntimeError("Compilation failed")
1184
+
1185
+ # FIX: Make executable on Unix-like systems — catch ALL exceptions, OR permissions
1186
+ if sys.platform != 'win32':
1187
+ try:
1188
+ current_mode = os.stat(exe_path).st_mode
1189
+ os.chmod(exe_path, current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
1190
+ print(f"Set executable permissions on {exe_path}")
1191
+ except Exception as e:
1192
+ print(f"Warning: Could not chmod {exe_path}: {e}")
1193
+
1194
+ print(f"Compiled executable: {exe_path}")
1195
+
1196
+ if not keep_c:
1197
+ os.remove(c_path)
1198
+ print(f"Removed intermediate C file: {c_path}")
1199
+
1200
+ if run:
1201
+ # Ensure executable permissions before running, then run with ./ prefix
1202
+ if sys.platform != 'win32':
1203
+ try:
1204
+ current_mode = os.stat(exe_path).st_mode
1205
+ if not (current_mode & stat.S_IXUSR):
1206
+ os.chmod(exe_path, current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
1207
+ print(f"Set executable permissions before running")
1208
+ except Exception as e:
1209
+ print(f"Warning: Could not ensure executable permissions: {e}")
1210
+
1211
+ print(f"\n--- Running {exe_path} ---")
1212
+ result = subprocess.run([f"./{os.path.basename(exe_path)}"],
1213
+ capture_output=True, text=True,
1214
+ cwd=os.path.dirname(exe_path) or '.')
1215
+ print(result.stdout, end="")
1216
+ if result.stderr:
1217
+ print("stderr:", result.stderr, end="")
1218
+ print(f"--- Exit code: {result.returncode} ---")
1219
+
1220
+ return exe_path
1221
+
1222
+ def main():
1223
+ if len(sys.argv) < 2:
1224
+ print(__doc__)
1225
+ sys.exit(1)
1226
+
1227
+ source_path = sys.argv[1]
1228
+ run = '--run' in sys.argv
1229
+ keep_c = '--keep-c' in sys.argv
1230
+
1231
+ if not os.path.exists(source_path):
1232
+ print(f"Error: File not found: {source_path}")
1233
+ sys.exit(1)
1234
+
1235
+ compile_sol(source_path, run=run, keep_c=keep_c)
1236
+
1237
+ if __name__ == "__main__":
1238
+ main()
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: tsolc
3
+ Version: 0.1.0
4
+ Summary: TSol Compiler - Transpiles Sol language to C
5
+ Author-email: stefand-0 <stefan.dragan95@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/stefand-0/tsol
8
+ Project-URL: Repository, https://github.com/stefand-0/tsol
9
+ Keywords: compiler,transpiler,sol,c,tsol
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Compilers
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ transpiler/main.py
4
+ tsolc.egg-info/PKG-INFO
5
+ tsolc.egg-info/SOURCES.txt
6
+ tsolc.egg-info/dependency_links.txt
7
+ tsolc.egg-info/entry_points.txt
8
+ tsolc.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tsolc = transpiler.main:main
@@ -0,0 +1 @@
1
+ transpiler