obsidian-lang 0.0.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Obsidian Language Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: obsidian-lang
3
+ Version: 0.0.1
4
+ Summary: Obsidian is a compiled programming language that combines memory safety, zero-cost abstractions, and minimalist syntax. Built for developers who want the speed of Rust without the boilerplate.
5
+ Author: Obsidian Language Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/example/obsidian-lang
8
+ Project-URL: Repository, https://github.com/example/obsidian-lang
9
+ Project-URL: Bug Tracker, https://github.com/example/obsidian-lang/issues
10
+ Keywords: programming-language,compiler,transpiler,rust-like,toy-language
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Compilers
21
+ Classifier: Topic :: Software Development :: Interpreters
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # Obsidian Language v0.0.1
28
+
29
+ **Obsidian** is a compiled programming language that combines memory safety, zero-cost abstractions, and minimalist syntax. Built for developers who want the speed of Rust without the boilerplate.
30
+
31
+ This is a toy implementation (v0.0.1) written in Python. It features a lexer, parser, AST, and a transpiler to Python (which simulates "compilation" by generating and executing equivalent Python code). Published on PyPI as `obsidian-lang`.
32
+
33
+ ## Features (v0.0.1)
34
+ - Minimalist syntax inspired by Rust
35
+ - Variables with `let`
36
+ - Functions with `fn`
37
+ - Basic expressions + comparison operators (< > <= >= == !=)
38
+ - Control flow with `if` / `else`
39
+ - Function calls (including built-in `println`)
40
+
41
+ ## Installation
42
+ ```bash
43
+ pip install obsidian-lang
44
+ ```
45
+
46
+ ## Usage
47
+ ```bash
48
+ # After pip install obsidian-lang
49
+ obsidian examples/hello.obs --show-code
50
+
51
+ # Or from source (development)
52
+ PYTHONPATH=. python -m obsidian.main examples/hello.obs --show-code
53
+ ```
54
+
55
+ ## Example (hello.obs)
56
+ ```
57
+ fn main() {
58
+ let x = 5;
59
+ let y = x + 3;
60
+ println(y);
61
+ println("Hello from Obsidian!");
62
+ }
63
+ ```
@@ -0,0 +1,37 @@
1
+ # Obsidian Language v0.0.1
2
+
3
+ **Obsidian** is a compiled programming language that combines memory safety, zero-cost abstractions, and minimalist syntax. Built for developers who want the speed of Rust without the boilerplate.
4
+
5
+ This is a toy implementation (v0.0.1) written in Python. It features a lexer, parser, AST, and a transpiler to Python (which simulates "compilation" by generating and executing equivalent Python code). Published on PyPI as `obsidian-lang`.
6
+
7
+ ## Features (v0.0.1)
8
+ - Minimalist syntax inspired by Rust
9
+ - Variables with `let`
10
+ - Functions with `fn`
11
+ - Basic expressions + comparison operators (< > <= >= == !=)
12
+ - Control flow with `if` / `else`
13
+ - Function calls (including built-in `println`)
14
+
15
+ ## Installation
16
+ ```bash
17
+ pip install obsidian-lang
18
+ ```
19
+
20
+ ## Usage
21
+ ```bash
22
+ # After pip install obsidian-lang
23
+ obsidian examples/hello.obs --show-code
24
+
25
+ # Or from source (development)
26
+ PYTHONPATH=. python -m obsidian.main examples/hello.obs --show-code
27
+ ```
28
+
29
+ ## Example (hello.obs)
30
+ ```
31
+ fn main() {
32
+ let x = 5;
33
+ let y = x + 3;
34
+ println(y);
35
+ println("Hello from Obsidian!");
36
+ }
37
+ ```
@@ -0,0 +1,7 @@
1
+ """
2
+ Obsidian Programming Language
3
+ v0.0.1a - A toy implementation in Python
4
+ """
5
+
6
+ __version__ = "0.0.1a"
7
+ __description__ = "Obsidian is a compiled programming language that combines memory safety, zero-cost abstractions, and minimalist syntax. Built for developers who want to speed of Rust without the boilerplate."
@@ -0,0 +1,59 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List, Optional, Union
3
+
4
+ class ASTNode: pass
5
+
6
+ @dataclass
7
+ class Program(ASTNode):
8
+ functions: List['Function'] = field(default_factory=list)
9
+
10
+ @dataclass
11
+ class Function(ASTNode):
12
+ name: str
13
+ params: List[str]
14
+ body: List['Statement']
15
+
16
+ @dataclass
17
+ class Statement(ASTNode): pass
18
+
19
+ @dataclass
20
+ class LetStatement(Statement):
21
+ name: str
22
+ value: 'Expression'
23
+
24
+ @dataclass
25
+ class ReturnStatement(Statement):
26
+ value: Optional['Expression'] = None
27
+
28
+ @dataclass
29
+ class IfStatement(Statement):
30
+ condition: 'Expression'
31
+ then_block: List[Statement]
32
+ else_block: Optional[List[Statement]] = None
33
+
34
+ @dataclass
35
+ class ExpressionStatement(Statement):
36
+ expression: 'Expression'
37
+
38
+ @dataclass
39
+ class Expression(ASTNode): pass
40
+
41
+ @dataclass
42
+ class Literal(Expression):
43
+ value: Union[int, float, str]
44
+ typ: str
45
+
46
+ @dataclass
47
+ class Identifier(Expression):
48
+ name: str
49
+
50
+ @dataclass
51
+ class BinaryOp(Expression):
52
+ left: Expression
53
+ op: str
54
+ right: Expression
55
+
56
+ @dataclass
57
+ class Call(Expression):
58
+ callee: str
59
+ args: List[Expression]
@@ -0,0 +1,90 @@
1
+ from enum import Enum, auto
2
+ from dataclasses import dataclass
3
+ from typing import List
4
+
5
+ class TokenType(Enum):
6
+ FN = auto(); LET = auto(); RETURN = auto(); IF = auto(); ELSE = auto()
7
+ NUMBER = auto(); STRING = auto(); IDENTIFIER = auto()
8
+ LPAREN = auto(); RPAREN = auto(); LBRACE = auto(); RBRACE = auto()
9
+ COMMA = auto(); SEMICOLON = auto(); COLON = auto(); ASSIGN = auto()
10
+ PLUS = auto(); MINUS = auto(); STAR = auto(); SLASH = auto()
11
+ LT = auto(); GT = auto(); LTE = auto(); GTE = auto(); EQ = auto(); NEQ = auto()
12
+ EOF = auto()
13
+
14
+ @dataclass
15
+ class Token:
16
+ type: TokenType
17
+ value: str
18
+ line: int
19
+ column: int
20
+
21
+ class Lexer:
22
+ def __init__(self, source: str):
23
+ self.source = source
24
+ self.tokens: List[Token] = []
25
+ self.pos = 0
26
+ self.line = 1
27
+ self.column = 1
28
+ self.keywords = {'fn':TokenType.FN,'let':TokenType.LET,'return':TokenType.RETURN,'if':TokenType.IF,'else':TokenType.ELSE}
29
+
30
+ def _peek(self): return self.source[self.pos] if self.pos < len(self.source) else '\0'
31
+ def _advance(self):
32
+ ch = self._peek(); self.pos += 1
33
+ if ch == '\n': self.line += 1; self.column = 1
34
+ else: self.column += 1
35
+ return ch
36
+ def _skip_ws(self):
37
+ while self._peek() in ' \t\r\n': self._advance()
38
+
39
+ def tokenize(self):
40
+ self.tokens = []
41
+ while self.pos < len(self.source):
42
+ self._skip_ws()
43
+ if self.pos >= len(self.source): break
44
+ sl, sc = self.line, self.column
45
+ ch = self._advance()
46
+ if ch.isdigit():
47
+ num = ch
48
+ while self._peek().isdigit() or self._peek() == '.': num += self._advance()
49
+ self.tokens.append(Token(TokenType.NUMBER, num, sl, sc))
50
+ elif ch.isalpha() or ch == '_':
51
+ ident = ch
52
+ while self._peek().isalnum() or self._peek() == '_': ident += self._advance()
53
+ tt = self.keywords.get(ident, TokenType.IDENTIFIER)
54
+ self.tokens.append(Token(tt, ident, sl, sc))
55
+ elif ch == '"':
56
+ s = ''
57
+ while self._peek() != '"' and self._peek() != '\0':
58
+ if self._peek() == '\\':
59
+ self._advance()
60
+ s += {'n':'\n','t':'\t'}.get(self._peek(), self._peek()); self._advance()
61
+ else: s += self._advance()
62
+ if self._peek() == '"': self._advance()
63
+ self.tokens.append(Token(TokenType.STRING, s, sl, sc))
64
+ elif ch=='(': self.tokens.append(Token(TokenType.LPAREN,'(',sl,sc))
65
+ elif ch==')': self.tokens.append(Token(TokenType.RPAREN,')',sl,sc))
66
+ elif ch=='{': self.tokens.append(Token(TokenType.LBRACE,'{',sl,sc))
67
+ elif ch=='}': self.tokens.append(Token(TokenType.RBRACE,'}',sl,sc))
68
+ elif ch==',': self.tokens.append(Token(TokenType.COMMA,',',sl,sc))
69
+ elif ch==';': self.tokens.append(Token(TokenType.SEMICOLON,';',sl,sc))
70
+ elif ch==':': self.tokens.append(Token(TokenType.COLON,':',sl,sc))
71
+ elif ch=='+': self.tokens.append(Token(TokenType.PLUS,'+',sl,sc))
72
+ elif ch=='-': self.tokens.append(Token(TokenType.MINUS,'-',sl,sc))
73
+ elif ch=='*': self.tokens.append(Token(TokenType.STAR,'*',sl,sc))
74
+ elif ch=='/': self.tokens.append(Token(TokenType.SLASH,'/',sl,sc))
75
+ elif ch=='<':
76
+ if self._peek()=='=': self._advance(); self.tokens.append(Token(TokenType.LTE,'<=',sl,sc))
77
+ else: self.tokens.append(Token(TokenType.LT,'<',sl,sc))
78
+ elif ch=='>':
79
+ if self._peek()=='=': self._advance(); self.tokens.append(Token(TokenType.GTE,'>=',sl,sc))
80
+ else: self.tokens.append(Token(TokenType.GT,'>',sl,sc))
81
+ elif ch=='=':
82
+ if self._peek()=='=': self._advance(); self.tokens.append(Token(TokenType.EQ,'==',sl,sc))
83
+ else: self.tokens.append(Token(TokenType.ASSIGN,'=',sl,sc))
84
+ elif ch=='!':
85
+ if self._peek()=='=': self._advance(); self.tokens.append(Token(TokenType.NEQ,'!=',sl,sc))
86
+ else: raise SyntaxError("!")
87
+ else:
88
+ raise SyntaxError(f"Unexpected '{ch}' at {sl}:{sc}")
89
+ self.tokens.append(Token(TokenType.EOF, '', self.line, self.column))
90
+ return self.tokens
@@ -0,0 +1,66 @@
1
+ import sys
2
+ import argparse
3
+ from pathlib import Path
4
+ from .lexer import Lexer
5
+ from .parser import Parser
6
+ from .transpiler import Transpiler
7
+ from . import __version__, __description__
8
+
9
+ def run_obsidian_file(filepath: str, execute: bool = True, show_code: bool = False):
10
+ path = Path(filepath)
11
+ if not path.exists():
12
+ print(f"Error: File not found: {filepath}")
13
+ sys.exit(1)
14
+ source = path.read_text(encoding="utf-8")
15
+ print(f"Obsidian v{__version__} - Processing {filepath}")
16
+ print("Parsing source...")
17
+ lexer = Lexer(source)
18
+ try:
19
+ tokens = lexer.tokenize()
20
+ except Exception as e:
21
+ print(f"Lexing error: {e}")
22
+ sys.exit(1)
23
+ parser = Parser(tokens)
24
+ try:
25
+ ast = parser.parse()
26
+ except Exception as e:
27
+ print(f"Parsing error: {e}")
28
+ sys.exit(1)
29
+ print(f"Successfully parsed {len(ast.functions)} function(s)")
30
+ transpiler = Transpiler()
31
+ try:
32
+ python_code = transpiler.transpile(ast)
33
+ except Exception as e:
34
+ print(f"Transpilation error: {e}")
35
+ sys.exit(1)
36
+ if show_code:
37
+ print("\n--- Generated Python Code ---")
38
+ print(python_code)
39
+ print("--- End of generated code ---\n")
40
+ if execute:
41
+ print("Executing (transpiled) program...\n")
42
+ try:
43
+ namespace = {"__name__": "__main__"}
44
+ exec(python_code, namespace)
45
+ except Exception as e:
46
+ print(f"Runtime error: {e}")
47
+ sys.exit(1)
48
+ print("\nProgram finished successfully.")
49
+
50
+ def main():
51
+ parser = argparse.ArgumentParser(description=__description__, prog="obsidian")
52
+ parser.add_argument("file", nargs="?", help="Obsidian source file (.obs)")
53
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
54
+ parser.add_argument("--show-code", action="store_true", help="Print the generated Python code")
55
+ parser.add_argument("--no-exec", action="store_true", help="Do not execute the program, only transpile")
56
+ args = parser.parse_args()
57
+ if not args.file:
58
+ print("Obsidian Language v0.0.1a")
59
+ print(__description__)
60
+ print("\nUsage: python -m obsidian.main <file.obs> [--show-code] [--no-exec]")
61
+ print("Example: python -m obsidian.main examples/hello.obs")
62
+ sys.exit(0)
63
+ run_obsidian_file(args.file, execute=not args.no_exec, show_code=args.show_code)
64
+
65
+ if __name__ == "__main__":
66
+ main()
@@ -0,0 +1,127 @@
1
+ from typing import List, Optional
2
+ from .lexer import Token, TokenType
3
+ from .ast import *
4
+
5
+ class Parser:
6
+ def __init__(self, tokens: List[Token]):
7
+ self.tokens = tokens
8
+ self.pos = 0
9
+
10
+ def _cur(self): return self.tokens[self.pos]
11
+ def _adv(self):
12
+ t = self._cur()
13
+ if self.pos < len(self.tokens)-1: self.pos += 1
14
+ return t
15
+ def _eat(self, tt: TokenType):
16
+ t = self._cur()
17
+ if t.type != tt: raise SyntaxError(f"Expected {tt.name} got {t.type.name}")
18
+ return self._adv()
19
+
20
+ def parse(self) -> Program:
21
+ p = Program()
22
+ while self._cur().type != TokenType.EOF:
23
+ if self._cur().type == TokenType.FN: p.functions.append(self._fn())
24
+ else: raise SyntaxError("Only fn at top level")
25
+ return p
26
+
27
+ def _fn(self):
28
+ self._eat(TokenType.FN)
29
+ name = self._eat(TokenType.IDENTIFIER).value
30
+ self._eat(TokenType.LPAREN)
31
+ params = []
32
+ if self._cur().type != TokenType.RPAREN:
33
+ while True:
34
+ params.append(self._eat(TokenType.IDENTIFIER).value)
35
+ if self._cur().type == TokenType.COMMA: self._adv()
36
+ else: break
37
+ self._eat(TokenType.RPAREN)
38
+ self._eat(TokenType.LBRACE)
39
+ body = []
40
+ while self._cur().type not in (TokenType.RBRACE, TokenType.EOF):
41
+ body.append(self._stmt())
42
+ self._eat(TokenType.RBRACE)
43
+ return Function(name, params, body)
44
+
45
+ def _stmt(self):
46
+ t = self._cur()
47
+ if t.type == TokenType.LET: return self._let()
48
+ if t.type == TokenType.RETURN: return self._ret()
49
+ if t.type == TokenType.IF: return self._if()
50
+ e = self._expr()
51
+ if self._cur().type == TokenType.SEMICOLON: self._adv()
52
+ return ExpressionStatement(e)
53
+
54
+ def _let(self):
55
+ self._eat(TokenType.LET)
56
+ name = self._eat(TokenType.IDENTIFIER).value
57
+ self._eat(TokenType.ASSIGN)
58
+ val = self._expr()
59
+ self._eat(TokenType.SEMICOLON)
60
+ return LetStatement(name, val)
61
+
62
+ def _ret(self):
63
+ self._eat(TokenType.RETURN)
64
+ val = self._expr() if self._cur().type != TokenType.SEMICOLON else None
65
+ self._eat(TokenType.SEMICOLON)
66
+ return ReturnStatement(val)
67
+
68
+ def _if(self):
69
+ self._eat(TokenType.IF)
70
+ cond = self._expr()
71
+ self._eat(TokenType.LBRACE)
72
+ thenb = []
73
+ while self._cur().type not in (TokenType.RBRACE, TokenType.EOF): thenb.append(self._stmt())
74
+ self._eat(TokenType.RBRACE)
75
+ elseb = None
76
+ if self._cur().type == TokenType.ELSE:
77
+ self._adv(); self._eat(TokenType.LBRACE)
78
+ elseb = []
79
+ while self._cur().type not in (TokenType.RBRACE, TokenType.EOF): elseb.append(self._stmt())
80
+ self._eat(TokenType.RBRACE)
81
+ return IfStatement(cond, thenb, elseb)
82
+
83
+ def _expr(self): return self._bin(0)
84
+
85
+ def _bin(self, minp):
86
+ left = self._prim()
87
+ while True:
88
+ t = self._cur()
89
+ prec = 0; op = None
90
+ if t.type == TokenType.STAR: prec,op = 3,'*'
91
+ elif t.type == TokenType.SLASH: prec,op=3,'/'
92
+ elif t.type == TokenType.PLUS: prec,op=2,'+'
93
+ elif t.type == TokenType.MINUS: prec,op=2,'-'
94
+ elif t.type == TokenType.LT: prec,op=1,'<'
95
+ elif t.type == TokenType.GT: prec,op=1,'>'
96
+ elif t.type == TokenType.LTE: prec,op=1,'<='
97
+ elif t.type == TokenType.GTE: prec,op=1,'>='
98
+ elif t.type == TokenType.EQ: prec,op=1,'=='
99
+ elif t.type == TokenType.NEQ: prec,op=1,'!='
100
+ else: break
101
+ if prec < minp: break
102
+ self._adv()
103
+ right = self._bin(prec+1)
104
+ left = BinaryOp(left, op, right)
105
+ return left
106
+
107
+ def _prim(self):
108
+ t = self._adv()
109
+ if t.type == TokenType.NUMBER:
110
+ v = int(t.value) if '.' not in t.value else float(t.value)
111
+ return Literal(v, 'int' if '.' not in t.value else 'float')
112
+ if t.type == TokenType.STRING: return Literal(t.value, 'string')
113
+ if t.type == TokenType.IDENTIFIER:
114
+ if self._cur().type == TokenType.LPAREN:
115
+ self._adv()
116
+ args = []
117
+ if self._cur().type != TokenType.RPAREN:
118
+ while True:
119
+ args.append(self._expr())
120
+ if self._cur().type == TokenType.COMMA: self._adv()
121
+ else: break
122
+ self._eat(TokenType.RPAREN)
123
+ return Call(t.value, args)
124
+ return Identifier(t.value)
125
+ if t.type == TokenType.LPAREN:
126
+ e = self._expr(); self._eat(TokenType.RPAREN); return e
127
+ raise SyntaxError(f"bad primary {t.type.name}")
@@ -0,0 +1,64 @@
1
+ from typing import List
2
+ from .ast import *
3
+
4
+ class Transpiler:
5
+ def __init__(self):
6
+ self.indent = 0
7
+ self.out: List[str] = []
8
+ def emit(self, s): self.out.append(" " * self.indent + s)
9
+
10
+ def transpile(self, prog: Program) -> str:
11
+ self.out = []
12
+ self.emit("# Generated by Obsidian v0.0.1")
13
+ self.emit("def println(*args): print(*args)")
14
+ self.emit("")
15
+ for f in prog.functions:
16
+ self._fn(f)
17
+ if any(f.name == "main" for f in prog.functions):
18
+ self.emit("")
19
+ self.emit("if __name__ == '__main__':")
20
+ self.indent += 1
21
+ self.emit("main()")
22
+ self.indent -= 1
23
+ return "\n".join(self.out)
24
+
25
+ def _fn(self, f: Function):
26
+ ps = ", ".join(f.params)
27
+ self.emit(f"def {f.name}({ps}):")
28
+ self.indent += 1
29
+ for s in f.body: self._stmt(s)
30
+ if not f.body: self.emit("pass")
31
+ self.indent -= 1
32
+ self.emit("")
33
+
34
+ def _stmt(self, s: Statement):
35
+ if isinstance(s, LetStatement):
36
+ self.emit(f"{s.name} = {self._expr(s.value)}")
37
+ elif isinstance(s, ReturnStatement):
38
+ self.emit("return " + (self._expr(s.value) if s.value is not None else ""))
39
+ elif isinstance(s, IfStatement):
40
+ self.emit(f"if {self._expr(s.condition)}:")
41
+ self.indent += 1
42
+ for ss in s.then_block: self._stmt(ss)
43
+ self.indent -= 1
44
+ if s.else_block:
45
+ self.emit("else:")
46
+ self.indent += 1
47
+ for ss in s.else_block: self._stmt(ss)
48
+ self.indent -= 1
49
+ elif isinstance(s, ExpressionStatement):
50
+ self.emit(self._expr(s.expression))
51
+ else:
52
+ self.emit("# unknown")
53
+
54
+ def _expr(self, e: Expression) -> str:
55
+ if isinstance(e, Literal):
56
+ return repr(e.value) if e.typ == "string" else str(e.value)
57
+ if isinstance(e, Identifier):
58
+ return e.name
59
+ if isinstance(e, BinaryOp):
60
+ return f"({self._expr(e.left)} {e.op} {self._expr(e.right)})"
61
+ if isinstance(e, Call):
62
+ args = ", ".join(self._expr(a) for a in e.args)
63
+ return f"{e.callee}({args})"
64
+ raise RuntimeError(f"Transpiler: unknown expression {type(e)}")
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: obsidian-lang
3
+ Version: 0.0.1
4
+ Summary: Obsidian is a compiled programming language that combines memory safety, zero-cost abstractions, and minimalist syntax. Built for developers who want the speed of Rust without the boilerplate.
5
+ Author: Obsidian Language Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/example/obsidian-lang
8
+ Project-URL: Repository, https://github.com/example/obsidian-lang
9
+ Project-URL: Bug Tracker, https://github.com/example/obsidian-lang/issues
10
+ Keywords: programming-language,compiler,transpiler,rust-like,toy-language
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Compilers
21
+ Classifier: Topic :: Software Development :: Interpreters
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # Obsidian Language v0.0.1
28
+
29
+ **Obsidian** is a compiled programming language that combines memory safety, zero-cost abstractions, and minimalist syntax. Built for developers who want the speed of Rust without the boilerplate.
30
+
31
+ This is a toy implementation (v0.0.1) written in Python. It features a lexer, parser, AST, and a transpiler to Python (which simulates "compilation" by generating and executing equivalent Python code). Published on PyPI as `obsidian-lang`.
32
+
33
+ ## Features (v0.0.1)
34
+ - Minimalist syntax inspired by Rust
35
+ - Variables with `let`
36
+ - Functions with `fn`
37
+ - Basic expressions + comparison operators (< > <= >= == !=)
38
+ - Control flow with `if` / `else`
39
+ - Function calls (including built-in `println`)
40
+
41
+ ## Installation
42
+ ```bash
43
+ pip install obsidian-lang
44
+ ```
45
+
46
+ ## Usage
47
+ ```bash
48
+ # After pip install obsidian-lang
49
+ obsidian examples/hello.obs --show-code
50
+
51
+ # Or from source (development)
52
+ PYTHONPATH=. python -m obsidian.main examples/hello.obs --show-code
53
+ ```
54
+
55
+ ## Example (hello.obs)
56
+ ```
57
+ fn main() {
58
+ let x = 5;
59
+ let y = x + 3;
60
+ println(y);
61
+ println("Hello from Obsidian!");
62
+ }
63
+ ```
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ obsidian/__init__.py
5
+ obsidian/ast.py
6
+ obsidian/lexer.py
7
+ obsidian/main.py
8
+ obsidian/parser.py
9
+ obsidian/transpiler.py
10
+ obsidian_lang.egg-info/PKG-INFO
11
+ obsidian_lang.egg-info/SOURCES.txt
12
+ obsidian_lang.egg-info/dependency_links.txt
13
+ obsidian_lang.egg-info/entry_points.txt
14
+ obsidian_lang.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ obsidian = obsidian.main:main
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "obsidian-lang"
7
+ version = "0.0.1"
8
+ authors = [
9
+ { name = "Obsidian Language Team" },
10
+ ]
11
+ description = "Obsidian is a compiled programming language that combines memory safety, zero-cost abstractions, and minimalist syntax. Built for developers who want the speed of Rust without the boilerplate."
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ license = { text = "MIT" }
15
+ keywords = ["programming-language", "compiler", "transpiler", "rust-like", "toy-language"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Software Development :: Compilers",
27
+ "Topic :: Software Development :: Interpreters",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/example/obsidian-lang"
32
+ Repository = "https://github.com/example/obsidian-lang"
33
+ "Bug Tracker" = "https://github.com/example/obsidian-lang/issues"
34
+
35
+ [project.scripts]
36
+ obsidian = "obsidian.main:main"
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["."]
40
+ include = ["obsidian*"]
41
+ exclude = ["examples*", "tests*"]
42
+
43
+ [tool.setuptools.package-data]
44
+ obsidian = ["*.py"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+