diff --git a/README.md b/README.md index c8769f3..3bde055 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,198 @@ # Selenium compiler Selenium is a small esoteric language with a lunar / poetic surface and a strict, C-like core. +It compiles to C via a Python compiler. -## Features - -- strongly typed -- semicolon-terminated statements -- functions -- variables and constants -- `eclipse` / `shadow` for if / else -- `tide` for while -- `orbit` for for -- `switch` / `case` / `default` for switch statements -- `break` and `continue` for loops and switch -- prefix `++` and `--` operators -- ternary conditional `?:` -- bitwise operators `<<` `>>` `&` `|` `^` -- `whisper` for printing -- `read_int`, `read_float`, `read_bool`, `read_char` for input -- explicit `cast(type, expr)` conversions - -## Syntax sketch +--- + +## Installation + +```sh +pip install -e . +``` + +This installs the `seleniumc` command. + +--- + +## Usage + +```sh +# Compile to a C file +seleniumc hello.sel -o hello.c + +# Compile, run with gcc, and execute immediately +seleniumc hello.sel --run + +# Use a different C compiler +seleniumc hello.sel --run --cc clang +``` + +--- + +## Language reference + +### Types + +| Selenium | C equivalent | Notes | +|----------|-------------|-------| +| `int` | `int` | 32-bit integer | +| `float` | `double` | 64-bit float | +| `bool` | `_Bool` | `true` / `false` literals | +| `char` | `char` | single-quoted character | +| `string` | `const char *` | double-quoted literal | +| `void` | `void` | return type only | + +### Comments ```selenium -wax int moon = 3; -seal int tide = 8; +// single line comment +/* block + comment */ +``` +### Variables + +```selenium +wax int x = 5; // mutable +seal int y = 10; // immutable (const) +``` + +Variables are declared with `wax` (mutable) or `seal` (immutable), followed by type and name. +Top-level declarations become C globals. Declarations inside blocks are local. + +### Functions + +```selenium ritual add(int a, int b) -> int { return a + b; }; +``` -eclipse (moon < tide) { - whisper moon; +Functions are defined with `ritual`, take typed parameters, and specify a return type after `->`. +Recursion is supported. Functions may be called before they are defined. + +### I/O + +```selenium +whisper expr; // print any type, followed by newline +wax int x = read_int(); // read int from stdin +wax float f = read_float(); +wax bool b = read_bool(); +wax char c = read_char(); +``` + +### Control flow + +```selenium +// if / else +eclipse (condition) { + ... } shadow { - whisper tide; + ... }; -tide (moon < 10) { - whisper moon; - moon = moon + 1; +// while +tide (condition) { + ... }; -orbit (wax int i = 0; i < 5; i = i + 1) { - whisper i; +// for +orbit (wax int i = 0; i < 10; i = i + 1) { + ... }; -switch (moon) { - case 1: { - whisper "one"; - break; - }; - case 3: { - whisper "three"; - break; - }; - default: { - whisper "other"; - break; - }; +// switch +switch (expr) { + case 1: { whisper "one"; break; }; + case 2: { whisper "two"; break; }; + default: { whisper "other"; break; }; }; +``` -wax int max = moon > tide ? moon : tide; -whisper max; +`break` exits a loop or switch; `continue` skips to the next loop iteration. + +### Operators -wax int shifted = moon << 2; -whisper shifted; +| Category | Operators | +|----------|-----------| +| Arithmetic | `+` `-` `*` `/` `%` | +| Comparison | `<` `<=` `>` `>=` `==` `!=` | +| Logical | `&&` `\|\|` `!` | +| Bitwise | `&` `\|` `^` `<<` `>>` | +| Prefix | `++` `--` | +| Ternary | `cond ? then : else` | -wax int input = read_int(); -whisper input; +### Cast -whisper add(moon, tide); +```selenium +wax float f = cast(float, 5); +wax int i = cast(int, 3.14); ``` -## Build a C file +Explicit type conversion between numeric types, bool, and char. + +--- -```bash -python -m selenium.main examples/hello.sel -o out.c -gcc out.c -o out -./out +## Examples + +### Hello, World! + +```selenium +whisper "Hello, World!"; ``` -Or install it as a script: +### Functions -```bash -pip install -e . -seleniumc examples/hello.sel -o out.c +```selenium +ritual fact(int n) -> int { + eclipse (n <= 1) { + return 1; + } shadow { + return n * fact(n - 1); + }; +}; + +whisper fact(10); +``` + +### While loop + +```selenium +wax int i = 0; +tide (i < 5) { + whisper i; + i = i + 1; +}; +``` + +### Ternary + +```selenium +seal int a = 3; +seal int b = 7; +wax int max = a > b ? a : b; +whisper max; +``` + +--- + +## Project layout + +``` +selenium/ + lexer.py – tokeniser (keywords, literals, operators) + parser.py – recursive-descent parser → AST + ast.py – AST node types + sema.py – type checker and semantic analyser + codegen_c.py – C code generator + main.py – CLI entry point +examples/ – sample .sel programs ``` ## Notes -- The compiler is intentionally strict. -- No implicit type coercion. -- Top-level `wax`, `seal`, and statements are emitted into `main`. -- Function definitions become normal C functions. +- The compiler is intentionally strict — no implicit type coercion. +- All types must be declared explicitly. +- Top-level `wax`/`seal` declarations are emitted as C globals; everything else runs in `main`. +- Function definitions can appear anywhere at the top level; forward calls are supported. diff --git a/examples/hello.sel b/examples/hello.sel index daede0c..972314f 100644 --- a/examples/hello.sel +++ b/examples/hello.sel @@ -1,9 +1,13 @@ -seal string title = "Selenium"; +// hello.sel - demonstrate functions, constants, and variables + +seal string greeting = "Hello from Selenium!"; ritual greet(string name) -> void { - whisper title; + whisper greeting; whisper name; }; -wax int moon = 3; -whisper moon; +greet("lunar traveller"); + +wax int count = 3; +whisper count; diff --git a/examples/hello_world.sel b/examples/hello_world.sel new file mode 100644 index 0000000..a99a1c7 --- /dev/null +++ b/examples/hello_world.sel @@ -0,0 +1,2 @@ +// hello_world.sel - the simplest Selenium program +whisper "Hello, World!"; diff --git a/pyproject.toml b/pyproject.toml index 233cbe2..10840d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "selenium-compiler" version = "0.1.0" description = "An esoteric strongly typed language that compiles Selenium to C." requires-python = ">=3.10" -authors = [{name = "OpenAI"}] +authors = [{name = "ewanc26"}] [project.scripts] seleniumc = "selenium.main:main" diff --git a/selenium/main.py b/selenium/main.py index 009479d..c7e3491 100644 --- a/selenium/main.py +++ b/selenium/main.py @@ -1,7 +1,9 @@ from __future__ import annotations import argparse +import subprocess import sys +import tempfile from pathlib import Path from .codegen_c import CCodeGenerator, CodegenContext @@ -23,19 +25,69 @@ def compile_source(source: str) -> str: def main(argv: list[str] | None = None) -> int: - parser = argparse.ArgumentParser(prog="seleniumc", description="Compile Selenium source to C") - parser.add_argument("input", help="Input .sel file") - parser.add_argument("-o", "--output", required=True, help="Output C file") - args = parser.parse_args(argv) + ap = argparse.ArgumentParser( + prog="seleniumc", + description="Compile Selenium source to C (optionally compile & run)", + ) + ap.add_argument("input", help="Input .sel file") + ap.add_argument("-o", "--output", help="Output C file (omit when using --run)") + ap.add_argument( + "--run", + action="store_true", + help="Compile with gcc and execute immediately (requires gcc on PATH)", + ) + ap.add_argument( + "--cc", + default="gcc", + metavar="CC", + help="C compiler to use with --run (default: gcc)", + ) + args = ap.parse_args(argv) + + if not args.run and args.output is None: + ap.error("Either -o/--output or --run must be specified") input_path = Path(args.input) - output_path = Path(args.output) try: source = input_path.read_text(encoding="utf-8") c_code = compile_source(source) + except OSError as exc: + print(f"seleniumc: {exc}", file=sys.stderr) + return 1 + except (ParseError, SemanticError, CompileError) as exc: + print(f"seleniumc: {exc}", file=sys.stderr) + return 1 + + if args.run: + with tempfile.TemporaryDirectory() as tmpdir: + tmp = Path(tmpdir) + c_file = tmp / "out.c" + exe_file = tmp / "out" + c_file.write_text(c_code, encoding="utf-8") + try: + result = subprocess.run( + [args.cc, str(c_file), "-o", str(exe_file)], + capture_output=True, + text=True, + ) + except FileNotFoundError: + print( + f"seleniumc: compiler '{args.cc}' not found — install it or pass a different --cc", + file=sys.stderr, + ) + return 1 + if result.returncode != 0: + print(f"seleniumc: C compilation failed:\n{result.stderr}", file=sys.stderr) + return 1 + run_result = subprocess.run([str(exe_file)]) + return run_result.returncode + + # Normal file output + output_path = Path(args.output) + try: output_path.write_text(c_code, encoding="utf-8") - except (OSError, ParseError, SemanticError, CompileError) as exc: + except OSError as exc: print(f"seleniumc: {exc}", file=sys.stderr) return 1 return 0