commit e5202c9c85226119e137c5990f625ea0b92daf84 Author: retoor Date: Tue Nov 25 16:06:53 2025 +0100 Finished. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..adb410d --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ + +all: test examples + +test: + python3 nano.py test.nano + +examples: + python3 nano.py examples/*.nano diff --git a/README.md b/README.md new file mode 100644 index 0000000..4fac1cf --- /dev/null +++ b/README.md @@ -0,0 +1,171 @@ +# nano + +A minimal interpreted programming language implemented in Python. + +**Author:** retoor + +## Overview + +nano is a dynamically typed scripting language with C-like syntax. The interpreter is implemented in approximately 500 lines of Python code. The language supports object-oriented programming with classes, constructors, destructors, and methods. + +## Usage + +```bash +python nano.py +``` + +## Language Features + +### Data Types + +| Type | Example | +|---------|------------------------------| +| int | `42` | +| float | `3.14` | +| string | `"hello"` or `'hello'` | +| array | `{1, 2, 3}` | +| null | `null` | +| object | `new ClassName()` | + +### Operators + +**Arithmetic:** `+`, `-`, `*`, `/`, `%` + +**Comparison:** `==`, `!=`, `<`, `>`, `<=`, `>=` + +**Logical:** `&&`, `||` + +**Assignment:** `=`, `+=`, `-=`, `*=`, `/=` + +**Increment/Decrement:** `++`, `--` + +**Pointer:** `&` (address-of), `*` (dereference) + +### Control Flow + +``` +if (condition) { + // statements +} else { + // statements +} + +while (condition) { + // statements +} + +for (init; condition; update) { + // statements +} +``` + +### Classes + +``` +class ClassName { + property = initialValue; + + ClassName(this, param1, param2) { + this.property = param1; + } + + ~ClassName(this) { + // destructor + } + + method(this, arg) { + return this.property + arg; + } +} + +obj = new ClassName(value1, value2); +obj.method(arg); +``` + +### Functions + +Methods support default parameters, variable arguments, and keyword arguments: + +``` +method(this, required, optional = "default", *args, **kwargs) { + // body +} +``` + +### Built-in Functions + +| Function | Description | +|-------------|--------------------------------------| +| `print()` | Output values to console | +| `len()` | Return length of string or array | +| `str()` | Convert value to string | +| `int()` | Convert value to integer | +| `bool()` | Convert value to boolean (0 or 1) | +| `typeof()` | Return type name as string | + +### String Methods + +| Method | Description | +|--------------|--------------------------------------| +| `.substr(start, length)` | Extract substring | +| `.split(delimiter)` | Split into array | +| `.count(substr)` | Count occurrences | +| `.indexOf(substr)` | Find first index | +| `.toUpper()` | Convert to uppercase | +| `.toLower()` | Convert to lowercase | +| `.trim()` | Remove whitespace | +| `.replace(old, new)` | Replace substring | +| `.length` | String length property | + +### Array Methods + +| Method | Description | +|------------------------|--------------------------------| +| `.push(value)` | Append element | +| `.pop()` | Remove and return last element | +| `.join(separator)` | Join elements into string | +| `.indexOf(value)` | Find index of element | +| `.slice(start, end)` | Extract subarray | +| `.length` | Array length property | + +### Comments + +``` +// single line comment + +/* multi-line + comment */ +``` + +## Examples + +The `examples/` directory contains demonstrations of language features: + +| File | Description | +|-------------------|------------------------------------------| +| `arrays.nano` | Array operations and iteration | +| `classes.nano` | Class definitions and object creation | +| `control_flow.nano` | Conditionals and loops | +| `fibonacci.nano` | Fibonacci sequence calculation | +| `functions.nano` | Method definitions and recursion | +| `json.nano` | JSON encoder/decoder implementation | +| `objects.nano` | Object composition and state management | +| `operators.nano` | Arithmetic and logical operators | +| `pointers.nano` | Pointer operations and references | +| `strings.nano` | String manipulation methods | +| `sudoku.nano` | Sudoku solver using backtracking | +| `types.nano` | Type system and coercion | + +## Implementation + +The interpreter consists of: + +- **Tokenizer:** Converts source code into tokens using regular expressions +- **Parser:** Constructs syntax structures from token streams +- **Runtime:** Executes parsed statements with scope management + +Classes are stored as `NanoClass` objects containing properties, methods, constructor, and destructor definitions. Instances are `NanoObject` objects with their own property copies. + +## License + +MIT - Like always. diff --git a/examples/arrays.nano b/examples/arrays.nano new file mode 100644 index 0000000..2da6c2f --- /dev/null +++ b/examples/arrays.nano @@ -0,0 +1,101 @@ +print("=== Arrays Demo ==="); +print(""); + +print("--- Array Literals ---"); +empty = {}; +print("Empty array length:", len(empty)); + +numbers = {1, 2, 3, 4, 5}; +print("Numbers:", numbers[0], numbers[1], numbers[2], numbers[3], numbers[4]); +print("Length:", len(numbers)); + +mixed = {42, "hello", 3.14, null}; +print("Mixed array:"); +print(" int:", mixed[0]); +print(" str:", mixed[1]); +print(" float:", mixed[2]); +print(" null:", mixed[3]); + +print(""); +print("--- Array Indexing ---"); +fruits = {"apple", "banana", "cherry", "date"}; +print("First element:", fruits[0]); +print("Last element:", fruits[3]); + +print(""); +print("--- Array Modification ---"); +arr = {10, 20, 30}; +print("Before:", arr[0], arr[1], arr[2]); +arr[1] = 25; +print("After arr[1] = 25:", arr[0], arr[1], arr[2]); + +print(""); +print("--- Array Methods ---"); +stack = {}; +stack.push(1); +stack.push(2); +stack.push(3); +print("Stack length:", len(stack)); + +popped = stack.pop(); +print("Popped:", popped); +print("After pop length:", len(stack)); + +print(""); +print("--- Array Join ---"); +words = {"Hello", "World"}; +sentence = words.join(" "); +print("Joined with space:", sentence); + +nums = {1, 2, 3}; +csv = nums.join(","); +print("Joined with comma:", csv); + +print(""); +print("--- Array indexOf ---"); +items = {"apple", "banana", "cherry"}; +idx = items.indexOf("banana"); +print("Index of 'banana':", idx); + +idx = items.indexOf("grape"); +print("Index of 'grape' (not found):", idx); + +print(""); +print("--- Array Slice ---"); +source = {0, 1, 2, 3, 4, 5}; +sliced = source.slice(2, 4); +print("slice(2, 4):", sliced[0], sliced[1]); + +print(""); +print("--- Iterating Arrays ---"); +colors = {"red", "green", "blue"}; +print("Colors:"); +for (i = 0; i < len(colors); i++) { + print(" ", colors[i]); +} + +print(""); +print("--- Building Arrays ---"); +squares = {}; +for (i = 1; i <= 5; i++) { + squares.push(i * i); +} +print("Squares of 1-5:"); +for (i = 0; i < len(squares); i++) { + print(" ", squares[i]); +} + +print(""); +print("--- Nested Arrays ---"); +matrix = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} +}; +print("3x3 Matrix:"); +for (row = 0; row < len(matrix); row++) { + print(" ", matrix[row][0], matrix[row][1], matrix[row][2]); +} + +print(""); +print("All array tests completed"); diff --git a/examples/classes.nano b/examples/classes.nano new file mode 100644 index 0000000..3996a03 --- /dev/null +++ b/examples/classes.nano @@ -0,0 +1,139 @@ +class Animal { + name = "Unknown"; + age = 0; + _species = "Animal"; + + Animal(this, name, age) { + this.name = name; + this.age = age; + print("Animal created:", this.name); + } + + ~Animal(this) { + print("Animal destroyed:", this.name); + } + + speak(this) { + print(this.name, "makes a sound"); + } + + getInfo(this) { + return this.name + " is " + str(this.age) + " years old"; + } + + birthday(this) { + this.age++; + print(this.name, "is now", this.age, "years old"); + } +} + +class Dog { + name = ""; + age = 0; + breed = "Unknown"; + _tricks = null; + + Dog(this, name, age, breed = "Mixed") { + this.name = name; + this.age = age; + this.breed = breed; + this._tricks = {}; + print("Dog created:", this.name, "the", this.breed); + } + + speak(this) { + print(this.name, "says: Woof!"); + } + + fetch(this, item) { + print(this.name, "fetches the", item); + } + + learnTrick(this, trick) { + this._tricks.push(trick); + print(this.name, "learned:", trick); + } + + performTricks(this) { + if (len(this._tricks) == 0) { + print(this.name, "doesn't know any tricks yet"); + return; + } + print(this.name + "'s tricks:"); + for (i = 0; i < len(this._tricks); i++) { + print(" -", this._tricks[i]); + } + } +} + +class Cat { + name = ""; + lives = 9; + __secretThoughts = "I am plotting world domination"; + + Cat(this, name) { + this.name = name; + print("Cat created:", this.name); + } + + speak(this) { + print(this.name, "says: Meow!"); + } + + loseLife(this) { + if (this.lives > 0) { + this.lives--; + print(this.name, "lost a life. Lives remaining:", this.lives); + } else { + print(this.name, "has no more lives!"); + } + } +} + +print("=== Class System Demo ==="); +print(""); + +print("--- Creating Animals ---"); +animal = new Animal("Generic", 5); +animal.speak(); +print(animal.getInfo()); +animal.birthday(); + +print(""); +print("--- Dog Class ---"); +dog = new Dog("Buddy", 3, "Golden Retriever"); +dog.speak(); +dog.fetch("ball"); +dog.learnTrick("sit"); +dog.learnTrick("roll over"); +dog.learnTrick("play dead"); +dog.performTricks(); + +print(""); +print("--- Cat Class ---"); +cat = new Cat("Whiskers"); +cat.speak(); +print("Cat has", cat.lives, "lives"); +cat.loseLife(); +cat.loseLife(); +print("Cat now has", cat.lives, "lives"); + +print(""); +print("--- Default Parameters ---"); +mutt = new Dog("Max", 2); +print("Breed:", mutt.breed); + +print(""); +print("--- Property Access ---"); +print("Dog name:", dog.name); +print("Dog age:", dog.age); +print("Dog breed:", dog.breed); + +dog.age = 4; +print("Updated dog age:", dog.age); + +print(""); +print("--- Convention-based Privacy ---"); +print("_species (single underscore):", animal._species); +cat.__secretThoughts = "Actually, I just want treats"; +print("__secretThoughts (double underscore):", cat.__secretThoughts); diff --git a/examples/control_flow.nano b/examples/control_flow.nano new file mode 100644 index 0000000..3f69c44 --- /dev/null +++ b/examples/control_flow.nano @@ -0,0 +1,143 @@ +print("=== Control Flow Demo ==="); +print(""); + +print("--- If Statement ---"); +x = 10; +if (x > 5) { + print("x is greater than 5"); +} + +print(""); +print("--- If-Else Statement ---"); +y = 3; +if (y > 5) { + print("y is greater than 5"); +} else { + print("y is not greater than 5"); +} + +print(""); +print("--- If-Else If-Else Chain ---"); +score = 85; +print("Score:", score); +if (score >= 90) { + print("Grade: A"); +} else { + if (score >= 80) { + print("Grade: B"); + } else { + if (score >= 70) { + print("Grade: C"); + } else { + if (score >= 60) { + print("Grade: D"); + } else { + print("Grade: F"); + } + } + } +} + +print(""); +print("--- Nested If ---"); +a = 10; +b = 20; +if (a > 0) { + if (b > 0) { + print("Both a and b are positive"); + } +} + +print(""); +print("--- While Loop ---"); +print("Counting from 1 to 5:"); +i = 1; +while (i <= 5) { + print(" i =", i); + i++; +} + +print(""); +print("--- While with Break Condition ---"); +sum = 0; +n = 1; +while (n <= 100) { + sum += n; + if (sum > 50) { + print("Sum exceeded 50 at n =", n); + print("Final sum:", sum); + n = 101; + } + n++; +} + +print(""); +print("--- For Loop ---"); +print("For loop 0 to 4:"); +for (i = 0; i < 5; i++) { + print(" iteration", i); +} + +print(""); +print("--- For Loop with Step ---"); +print("Counting by 2s:"); +for (i = 0; i <= 10; i += 2) { + print(" i =", i); +} + +print(""); +print("--- Nested Loops ---"); +print("Multiplication table (3x3):"); +for (i = 1; i <= 3; i++) { + row = ""; + for (j = 1; j <= 3; j++) { + row += str(i * j); + if (j < 3) { + row += "\t"; + } + } + print(row); +} + +print(""); +print("--- Loop with Array ---"); +colors = {"red", "green", "blue", "yellow"}; +print("Colors:"); +for (i = 0; i < len(colors); i++) { + print(" " + str(i) + ":", colors[i]); +} + +print(""); +print("--- Countdown ---"); +print("Countdown:"); +for (i = 5; i >= 1; i--) { + print(" ", i); +} +print(" Blast off!"); + +print(""); +print("--- While True Pattern ---"); +attempts = 0; +maxAttempts = 5; +while (1) { + attempts++; + print("Attempt", attempts); + if (attempts >= maxAttempts) { + print("Max attempts reached"); + break; + } +} + +print(""); +print("--- Complex Condition ---"); +age = 25; +hasLicense = 1; +if (age >= 18 && hasLicense) { + print("Can drive"); +} + +isMember = 0; +hasDiscount = 1; +if (isMember || hasDiscount) { + print("Eligible for discount"); +} diff --git a/examples/fibonacci.nano b/examples/fibonacci.nano new file mode 100644 index 0000000..876a9a0 --- /dev/null +++ b/examples/fibonacci.nano @@ -0,0 +1,26 @@ +class Fibonacci { + Fibonacci(this) { + } + + calc(this, n) { + if (n <= 1) { + return n; + } + a = 0; + b = 1; + for (i = 2; i <= n; i++) { + temp = a + b; + a = b; + b = temp; + } + return b; + } +} + +print("Fibonacci Demo"); +fib = new Fibonacci(); + +for (n = 0; n <= 10; n++) { + result = fib.calc(n); + print("fib(" + str(n) + ") =", result); +} diff --git a/examples/functions.nano b/examples/functions.nano new file mode 100644 index 0000000..4ff0862 --- /dev/null +++ b/examples/functions.nano @@ -0,0 +1,254 @@ +print("=== Functions Demo ==="); +print(""); + +print("--- Methods in Classes ---"); +class Calculator { + Calculator(this) { + } + + add(this, a, b) { + return a + b; + } + + subtract(this, a, b) { + return a - b; + } + + multiply(this, a, b) { + return a * b; + } + + divide(this, a, b) { + if (b == 0) { + return 0; + } + return a / b; + } +} + +calc = new Calculator(); +print("add(5, 3) =", calc.add(5, 3)); +print("subtract(10, 4) =", calc.subtract(10, 4)); +print("multiply(6, 7) =", calc.multiply(6, 7)); +print("divide(20, 4) =", calc.divide(20, 4)); + +print(""); +print("--- Required Parameters ---"); +class Greeter { + Greeter(this) { + } + + greet(this, name) { + print("Hello,", name + "!"); + } +} + +greeter = new Greeter(); +greeter.greet("Alice"); +greeter.greet("Bob"); + +print(""); +print("--- Optional Parameters with Defaults ---"); +class ConfigurableGreeter { + ConfigurableGreeter(this) { + } + + greet(this, name, greeting = "Hello", punctuation = "!") { + print(greeting + ",", name + punctuation); + } +} + +cg = new ConfigurableGreeter(); +cg.greet("Alice"); +cg.greet("Bob", "Hi"); +cg.greet("Charlie", "Hey", "?"); + +print(""); +print("--- Null Default Values ---"); +class Formatter { + Formatter(this) { + } + + format(this, value, prefix = null, suffix = null) { + result = ""; + if (prefix) { + result += prefix; + } + result += str(value); + if (suffix) { + result += suffix; + } + return result; + } +} + +fmt = new Formatter(); +print("No prefix/suffix:", fmt.format(42)); +print("With prefix:", fmt.format(42, "$")); +print("With both:", fmt.format(42, "$", ".00")); + +print(""); +print("--- Variable Arguments (*args) ---"); +class VarArgs { + VarArgs(this) { + } + + printAll(this, *args) { + print("Received", len(args), "arguments:"); + for (i = 0; i < len(args); i++) { + print(" arg[" + str(i) + "] =", args[i]); + } + } + + sum(this, *numbers) { + total = 0; + for (i = 0; i < len(numbers); i++) { + total += numbers[i]; + } + return total; + } + + first(this, required, *rest) { + print("Required:", required); + print("Rest:", len(rest), "items"); + } +} + +va = new VarArgs(); +va.printAll(1, 2, 3); +va.printAll("a", "b", "c", "d", "e"); + +print("sum(1,2,3,4,5) =", va.sum(1, 2, 3, 4, 5)); + +va.first("mandatory", "extra1", "extra2"); + +print(""); +print("--- Keyword Arguments (**kwargs) ---"); +class KwArgs { + KwArgs(this) { + } + + configure(this, **options) { + print("Configuration options:"); + print(" (kwargs captured as dict)"); + } +} + +kw = new KwArgs(); +kw.configure(); + +print(""); +print("--- Return Values ---"); +class Math { + Math(this) { + } + + square(this, n) { + return n * n; + } + + factorial(this, n) { + if (n <= 1) { + return 1; + } + return n * this.factorial(n - 1); + } + + abs(this, n) { + if (n < 0) { + return 0 - n; + } + return n; + } + + max(this, a, b) { + if (a > b) { + return a; + } + return b; + } + + min(this, a, b) { + if (a < b) { + return a; + } + return b; + } +} + +math = new Math(); +print("square(7) =", math.square(7)); +print("factorial(5) =", math.factorial(5)); +print("abs(-42) =", math.abs(-42)); +print("max(10, 20) =", math.max(10, 20)); +print("min(10, 20) =", math.min(10, 20)); + +print(""); +print("--- Recursion ---"); +class Recursive { + Recursive(this) { + } + + fibonacci(this, n) { + if (n <= 1) { + return n; + } + return this.fibonacci(n - 1) + this.fibonacci(n - 2); + } + + gcd(this, a, b) { + if (b == 0) { + return a; + } + return this.gcd(b, a % b); + } + + power(this, base, exp) { + if (exp == 0) { + return 1; + } + return base * this.power(base, exp - 1); + } +} + +rec = new Recursive(); +print("fibonacci(10) =", rec.fibonacci(10)); +print("gcd(48, 18) =", rec.gcd(48, 18)); +print("power(2, 8) =", rec.power(2, 8)); + +print(""); +print("--- Method Chaining ---"); +class StringBuilder { + buffer = ""; + + StringBuilder(this) { + } + + append(this, text) { + this.buffer += text; + return this; + } + + appendLine(this, text) { + this.buffer += text + "\n"; + return this; + } + + toString(this) { + return this.buffer; + } + + clear(this) { + this.buffer = ""; + return this; + } +} + +sb = new StringBuilder(); +sb.append("Hello").append(" ").append("World").append("!"); +print("Result:", sb.toString()); + +sb.clear(); +sb.appendLine("Line 1").appendLine("Line 2").append("Line 3"); +print("Multi-line:"); +print(sb.toString()); diff --git a/examples/json.nano b/examples/json.nano new file mode 100644 index 0000000..7a0acb0 --- /dev/null +++ b/examples/json.nano @@ -0,0 +1,157 @@ +print("=== JSON Encoder Demo ==="); +print(""); + +class JSONEncoder { + JSONEncoder(this) { + } + + encode(this, value) { + t = typeof(value); + if (t == "null") { + return "null"; + } + if (t == "int" || t == "float") { + return str(value); + } + if (t == "str") { + return "\"" + value + "\""; + } + if (t == "array") { + return this.encodeArray(value); + } + return "null"; + } + + encodeArray(this, arr) { + result = "["; + for (i = 0; i < len(arr); i++) { + if (i > 0) { + result += ","; + } + result += this.encode(arr[i]); + } + result += "]"; + return result; + } +} + +encoder = new JSONEncoder(); + +print("Encoding primitives:"); +print(" null ->", encoder.encode(null)); +print(" 42 ->", encoder.encode(42)); +print(" 3.14 ->", encoder.encode(3.14)); +print(" \"hello\" ->", encoder.encode("hello")); + +print(""); +print("Encoding arrays:"); +arr = {1, 2, 3}; +print(" {1, 2, 3} ->", encoder.encodeArray(arr)); + +mixed = {1, "two", 3}; +print(" {1, \"two\", 3} ->", encoder.encodeArray(mixed)); + +print(""); +print("=== JSON Decoder Demo ==="); +print(""); + +class JSONDecoder { + input = ""; + pos = 0; + + JSONDecoder(this) { + } + + decode(this, jsonStr) { + this.input = jsonStr; + this.pos = 0; + return this.parseValue(); + } + + parseValue(this) { + this.skipWhitespace(); + if (this.pos >= this.input.length) { + return null; + } + c = this.input.substr(this.pos, 1); + if (c == "[") { + return this.parseArray(); + } + if (c == "n") { + this.pos += 4; + return null; + } + return this.parseNumber(); + } + + parseNumber(this) { + start = this.pos; + while (this.pos < this.input.length) { + c = this.input.substr(this.pos, 1); + isDigit = (c == "0" || c == "1" || c == "2" || c == "3" || c == "4" || c == "5" || c == "6" || c == "7" || c == "8" || c == "9" || c == "-"); + if (isDigit == 0) { + break; + } + this.pos++; + } + numStr = this.input.substr(start, this.pos - start); + return int(numStr); + } + + parseArray(this) { + this.pos++; + result = {}; + this.skipWhitespace(); + if (this.pos < this.input.length) { + c = this.input.substr(this.pos, 1); + if (c == "]") { + this.pos++; + return result; + } + } + while (this.pos < this.input.length) { + value = this.parseValue(); + result.push(value); + this.skipWhitespace(); + if (this.pos >= this.input.length) { + break; + } + c = this.input.substr(this.pos, 1); + if (c == "]") { + this.pos++; + break; + } + if (c == ",") { + this.pos++; + } + } + return result; + } + + skipWhitespace(this) { + while (this.pos < this.input.length) { + c = this.input.substr(this.pos, 1); + if (c != " ") { + break; + } + this.pos++; + } + } +} + +decoder = new JSONDecoder(); + +print("Decoding numbers:"); +print(" \"123\" ->", decoder.decode("123")); +print(" \"456\" ->", decoder.decode("456")); + +print(""); +print("Decoding arrays:"); +decoded = decoder.decode("[1,2,3]"); +print(" \"[1,2,3]\" -> length:", len(decoded)); +print(" element 0:", decoded[0]); +print(" element 1:", decoded[1]); +print(" element 2:", decoded[2]); + +print(""); +print("All JSON tests completed"); diff --git a/examples/objects.nano b/examples/objects.nano new file mode 100644 index 0000000..0fafb7d --- /dev/null +++ b/examples/objects.nano @@ -0,0 +1,220 @@ +print("=== Objects Demo ==="); +print(""); + +print("--- Object Instantiation ---"); +class Point { + x = 0; + y = 0; + + Point(this, x, y) { + this.x = x; + this.y = y; + } + + toString(this) { + return "(" + str(this.x) + ", " + str(this.y) + ")"; + } + + distanceFromOrigin(this) { + return this.x * this.x + this.y * this.y; + } +} + +p1 = new Point(3, 4); +p2 = new Point(10, 20); + +print("Point 1:", p1.toString()); +print("Point 2:", p2.toString()); + +print(""); +print("--- Property Access ---"); +print("p1.x =", p1.x); +print("p1.y =", p1.y); +print("p2.x =", p2.x); +print("p2.y =", p2.y); + +print(""); +print("--- Property Modification ---"); +p1.x = 100; +p1.y = 200; +print("After modification, p1:", p1.toString()); + +print(""); +print("--- Method Calls ---"); +print("p2.distanceFromOrigin():", p2.distanceFromOrigin()); + +print(""); +print("--- Multiple Objects ---"); +class Rectangle { + width = 0; + height = 0; + + Rectangle(this, w, h) { + this.width = w; + this.height = h; + } + + area(this) { + return this.width * this.height; + } + + perimeter(this) { + return 2 * (this.width + this.height); + } +} + +rect1 = new Rectangle(5, 10); +rect2 = new Rectangle(3, 7); +rect3 = new Rectangle(8, 8); + +print("Rectangle 1: " + str(rect1.width) + "x" + str(rect1.height)); +print(" Area:", rect1.area()); +print(" Perimeter:", rect1.perimeter()); + +print("Rectangle 2: " + str(rect2.width) + "x" + str(rect2.height)); +print(" Area:", rect2.area()); +print(" Perimeter:", rect2.perimeter()); + +print("Rectangle 3: " + str(rect3.width) + "x" + str(rect3.height)); +print(" Area:", rect3.area()); +print(" Perimeter:", rect3.perimeter()); + +print(""); +print("--- Object Composition ---"); +class Circle { + center = null; + radius = 0; + + Circle(this, centerX, centerY, radius) { + this.center = new Point(centerX, centerY); + this.radius = radius; + } + + describe(this) { + print("Circle at", this.center.toString(), "with radius", this.radius); + } +} + +circle = new Circle(5, 5, 10); +circle.describe(); +print("Center x:", circle.center.x); +print("Center y:", circle.center.y); + +print(""); +print("--- Object Arrays ---"); +class Student { + name = ""; + grade = 0; + + Student(this, name, grade) { + this.name = name; + this.grade = grade; + } +} + +students = {}; +students.push(new Student("Alice", 95)); +students.push(new Student("Bob", 87)); +students.push(new Student("Charlie", 92)); +students.push(new Student("Diana", 88)); + +print("Student Grades:"); +totalGrade = 0; +for (i = 0; i < len(students); i++) { + print(" " + students[i].name + ":", students[i].grade); + totalGrade += students[i].grade; +} +average = totalGrade / len(students); +print("Average grade:", average); + +print(""); +print("--- Object State ---"); +class BankAccount { + owner = ""; + balance = 0; + transactions = null; + + BankAccount(this, owner, initial) { + this.owner = owner; + this.balance = initial; + this.transactions = {}; + this.transactions.push("Initial deposit: " + str(initial)); + } + + deposit(this, amount) { + this.balance += amount; + this.transactions.push("Deposit: " + str(amount)); + } + + withdraw(this, amount) { + if (amount > this.balance) { + print("Insufficient funds!"); + return 0; + } + this.balance -= amount; + this.transactions.push("Withdrawal: " + str(amount)); + return 1; + } + + getStatement(this) { + print("=== Account Statement ==="); + print("Owner:", this.owner); + print("Transactions:"); + for (i = 0; i < len(this.transactions); i++) { + print(" ", this.transactions[i]); + } + print("Current Balance:", this.balance); + } +} + +account = new BankAccount("John Doe", 1000); +account.deposit(500); +account.deposit(250); +account.withdraw(300); +account.withdraw(100); +account.getStatement(); + +print(""); +print("--- Object Equality ---"); +class Token { + type = ""; + value = ""; + + Token(this, type, value) { + this.type = type; + this.value = value; + } + + equals(this, other) { + return this.type == other.type && this.value == other.value; + } +} + +t1 = new Token("NUMBER", "42"); +t2 = new Token("NUMBER", "42"); +t3 = new Token("STRING", "hello"); + +print("t1 equals t2:", t1.equals(t2)); +print("t1 equals t3:", t1.equals(t3)); + +print(""); +print("--- Factory Pattern ---"); +class ShapeFactory { + ShapeFactory(this) { + } + + createRectangle(this, w, h) { + return new Rectangle(w, h); + } + + createSquare(this, size) { + return new Rectangle(size, size); + } +} + +factory = new ShapeFactory(); +rect = factory.createRectangle(4, 6); +square = factory.createSquare(5); + +print("Rectangle area:", rect.area()); +print("Square area:", square.area()); diff --git a/examples/operators.nano b/examples/operators.nano new file mode 100644 index 0000000..3736713 --- /dev/null +++ b/examples/operators.nano @@ -0,0 +1,136 @@ +print("=== Operators Demo ==="); +print(""); + +print("--- Arithmetic Operators ---"); +a = 20; +b = 6; +print("a =", a, ", b =", b); +print("a + b =", a + b); +print("a - b =", a - b); +print("a * b =", a * b); +print("a / b =", a / b); +print("a % b =", a % b); + +print(""); +print("--- Comparison Operators ---"); +x = 10; +y = 20; +z = 10; +print("x =", x, ", y =", y, ", z =", z); +print("x == z:", x == z); +print("x != y:", x != y); +print("x < y:", x < y); +print("y > x:", y > x); +print("x <= z:", x <= z); +print("y >= x:", y >= x); + +print(""); +print("--- Logical Operators ---"); +t = 1; +f = 0; +print("true (1) && true (1):", t && t); +print("true (1) && false (0):", t && f); +print("false (0) || true (1):", f || t); +print("false (0) || false (0):", f || f); + +print(""); +print("--- || for Default Values ---"); +value = null; +result = value || "default"; +print("null || \"default\" =", result); + +value = 0; +result = value || 42; +print("0 || 42 =", result); + +value = "exists"; +result = value || "default"; +print("\"exists\" || \"default\" =", result); + +value = 100; +result = value || 0; +print("100 || 0 =", result); + +print(""); +print("--- String Concatenation ---"); +str1 = "Hello"; +str2 = "World"; +print("str1 + \" \" + str2 =", str1 + " " + str2); + +greeting = "Hi"; +greeting += " there"; +print("After += :", greeting); + +print(""); +print("--- Assignment Operators ---"); +n = 10; +print("n =", n); + +n += 5; +print("n += 5 ->", n); + +n -= 3; +print("n -= 3 ->", n); + +n *= 2; +print("n *= 2 ->", n); + +n /= 4; +print("n /= 4 ->", n); + +print(""); +print("--- Increment/Decrement ---"); +counter = 0; +print("counter =", counter); + +counter++; +print("counter++ ->", counter); + +counter++; +print("counter++ ->", counter); + +counter--; +print("counter-- ->", counter); + +++counter; +print("++counter ->", counter); + +--counter; +print("--counter ->", counter); + +print(""); +print("--- Operator Precedence ---"); +result = 2 + 3 * 4; +print("2 + 3 * 4 =", result); + +result = (2 + 3) * 4; +print("(2 + 3) * 4 =", result); + +result = 10 - 4 - 2; +print("10 - 4 - 2 =", result); + +result = 20 / 4 / 2; +print("20 / 4 / 2 =", result); + +print(""); +print("--- Combined Expressions ---"); +a = 5; +b = 3; +c = 2; +result = a * b + c; +print("5 * 3 + 2 =", result); + +result = a + b * c; +print("5 + 3 * 2 =", result); + +result = (a + b) * c; +print("(5 + 3) * 2 =", result); + +print(""); +print("--- Comparison Chains ---"); +val = 15; +inRange = val > 10 && val < 20; +print("15 > 10 && 15 < 20:", inRange); + +outRange = val < 10 || val > 20; +print("15 < 10 || 15 > 20:", outRange); diff --git a/examples/pointers.nano b/examples/pointers.nano new file mode 100644 index 0000000..b4c1bd6 --- /dev/null +++ b/examples/pointers.nano @@ -0,0 +1,94 @@ +print("=== Pointers Demo ==="); +print(""); + +print("--- Basic Pointer Operations ---"); +x = 42; +print("x =", x); + +ptr = &x; +print("ptr = &x (pointer to x)"); +print("*ptr (dereferenced) =", *ptr); + +print(""); +print("--- Simulating Pass by Reference ---"); +class RefDemo { + RefDemo(this) { + } + + swap(this, a, b) { + temp = a[0]; + a[0] = b[0]; + b[0] = temp; + } + + doubleValue(this, ref) { + ref[0] = ref[0] * 2; + } +} + +demo = new RefDemo(); + +val1 = {10}; +val2 = {20}; +print("Before swap: val1 =", val1[0], ", val2 =", val2[0]); +demo.swap(val1, val2); +print("After swap: val1 =", val1[0], ", val2 =", val2[0]); + +num = {50}; +print("Before double:", num[0]); +demo.doubleValue(num); +print("After double:", num[0]); + +print(""); +print("--- Array as Reference Container ---"); +data = {1, 2, 3, 4, 5}; +print("Original array:"); +for (i = 0; i < len(data); i++) { + print(" data[" + str(i) + "] =", data[i]); +} + +class ArrayModifier { + ArrayModifier(this) { + } + + multiplyAll(this, arr, factor) { + for (j = 0; j < len(arr); j++) { + arr[j] = arr[j] * factor; + } + } +} + +modifier = new ArrayModifier(); +modifier.multiplyAll(data, 10); + +print("After multiplying by 10:"); +for (i = 0; i < len(data); i++) { + print(" data[" + str(i) + "] =", data[i]); +} + +print(""); +print("--- Reference Wrapper Pattern ---"); +class Ref { + value = null; + + Ref(this, v) { + this.value = v; + } + + get(this) { + return this.value; + } + + set(this, v) { + this.value = v; + } +} + +refInt = new Ref(42); +print("Ref value:", refInt.get()); + +refInt.set(100); +print("After set(100):", refInt.get()); + +print(""); +print("All pointer tests completed"); diff --git a/examples/strings.nano b/examples/strings.nano new file mode 100644 index 0000000..e0a620e --- /dev/null +++ b/examples/strings.nano @@ -0,0 +1,88 @@ +print("=== Strings Demo ==="); +print(""); + +print("--- String Literals ---"); +single = 'Single quotes'; +double = "Double quotes"; +print(single); +print(double); + +print(""); +print("--- String Concatenation ---"); +first = "Hello"; +second = "World"; +combined = first + " " + second; +print("Concatenated:", combined); + +greeting = "Hi"; +greeting += " there!"; +print("Using +=:", greeting); + +print(""); +print("--- String Length ---"); +text = "Hello, World!"; +print("Text:", text); +print("Length:", text.length); + +print(""); +print("--- Substring (substr) ---"); +myStr = "Hello, World!"; +print("Original:", myStr); +print("substr(0, 5):", myStr.substr(0, 5)); +print("substr(7, 5):", myStr.substr(7, 5)); + +print(""); +print("--- String Split ---"); +csv = "apple,banana,cherry"; +parts = csv.split(","); +print("CSV:", csv); +print("Split result:"); +for (i = 0; i < len(parts); i++) { + print(" ", parts[i]); +} + +print(""); +print("--- String Count ---"); +text = "abracadabra"; +print("Text:", text); +print("Count of 'a':", text.count("a")); +print("Count of 'br':", text.count("br")); + +print(""); +print("--- String indexOf ---"); +testStr = "Hello, World!"; +print("String:", testStr); +print("indexOf('World'):", testStr.indexOf("World")); +print("indexOf('o'):", testStr.indexOf("o")); + +print(""); +print("--- Case Conversion ---"); +mixed = "Hello World"; +print("Original:", mixed); +print("toUpper():", mixed.toUpper()); +print("toLower():", mixed.toLower()); + +print(""); +print("--- String Trim ---"); +padded = " spaces "; +print("Before trim:", padded); +print("After trim:", padded.trim()); + +print(""); +print("--- String Replace ---"); +original = "Hello World"; +replaced = original.replace("World", "Nano"); +print("Replace 'World' with 'Nano':", replaced); + +print(""); +print("--- Type Conversion ---"); +num = 42; +numStr = str(num); +print("Number to string:", numStr); + +strNum = "123"; +parsed = int(strNum); +print("String to number:", parsed); + +print(""); +print("All string tests completed"); diff --git a/examples/sudoku.nano b/examples/sudoku.nano new file mode 100644 index 0000000..85dc34c --- /dev/null +++ b/examples/sudoku.nano @@ -0,0 +1,102 @@ +class Sudoku { + grid = null; + size = 9; + boxSize = 3; + + Sudoku(this, puzzle) { + this.grid = puzzle; + } + + isValid(this, row, col, num) { + for (i = 0; i < 9; i++) { + if (this.grid[row * 9 + i] == num) { + return 0; + } + } + for (i = 0; i < 9; i++) { + if (this.grid[i * 9 + col] == num) { + return 0; + } + } + boxRow = (row / 3) * 3; + boxCol = (col / 3) * 3; + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + idx = (boxRow + i) * 9 + (boxCol + j); + if (this.grid[idx] == num) { + return 0; + } + } + } + return 1; + } + + solve(this) { + for (row = 0; row < 9; row++) { + for (col = 0; col < 9; col++) { + idx = row * 9 + col; + if (this.grid[idx] == 0) { + for (num = 1; num <= 9; num++) { + if (this.isValid(row, col, num)) { + this.grid[idx] = num; + if (this.solve()) { + return 1; + } + this.grid[idx] = 0; + } + } + return 0; + } + } + } + return 1; + } + + printGrid(this) { + for (row = 0; row < 9; row++) { + line = ""; + for (col = 0; col < 9; col++) { + line += str(this.grid[row * 9 + col]); + if (col < 8) { + line += " "; + } + if (col == 2 || col == 5) { + line += "| "; + } + } + print(line); + if (row == 2 || row == 5) { + print("------+-------+------"); + } + } + } +} + +puzzle = { + 5, 3, 0, 0, 7, 0, 0, 0, 0, + 6, 0, 0, 1, 9, 5, 0, 0, 0, + 0, 9, 8, 0, 0, 0, 0, 6, 0, + 8, 0, 0, 0, 6, 0, 0, 0, 3, + 4, 0, 0, 8, 0, 3, 0, 0, 1, + 7, 0, 0, 0, 2, 0, 0, 0, 6, + 0, 6, 0, 0, 0, 0, 2, 8, 0, + 0, 0, 0, 4, 1, 9, 0, 0, 5, + 0, 0, 0, 0, 8, 0, 0, 7, 9 +}; + +print("Sudoku Puzzle:"); +print("=============="); +sudoku = new Sudoku(puzzle); +sudoku.printGrid(); + +print(""); +print("Solving..."); +print(""); + +if (sudoku.solve()) { + print("Solution:"); + print("========="); + sudoku.printGrid(); +} else { + print("No solution exists"); +} diff --git a/examples/types.nano b/examples/types.nano new file mode 100644 index 0000000..a7212e3 --- /dev/null +++ b/examples/types.nano @@ -0,0 +1,118 @@ +print("=== Type System Demo ==="); +print(""); + +print("--- Dynamic Typing ---"); +x = 42; +print("x =", x, "type:", typeof(x)); + +x = "hello"; +print("x =", x, "type:", typeof(x)); + +x = {1, 2, 3}; +print("x = array, type:", typeof(x)); + +x = null; +print("x =", x, "type:", typeof(x)); + +print(""); +print("--- Null Defaults ---"); +undeclared = null; +print("null variable:", undeclared); + +intDefault = int(null); +print("int(null) =", intDefault); + +strDefault = str(null); +print("str(null) = \"" + strDefault + "\""); + +boolDefault = bool(null); +print("bool(null) =", boolDefault); + +print(""); +print("--- Type Constructors ---"); +fromStr = int("123"); +print("int(\"123\") =", fromStr, "type:", typeof(fromStr)); + +fromInt = str(456); +print("str(456) =", fromInt, "type:", typeof(fromInt)); + +fromFloat = int(3.14); +print("int(3.14) =", fromFloat); + +print(""); +print("--- Type Coercion ---"); +a = "The answer is: "; +b = 42; +result = a + str(b); +print(result); + +c = "10"; +d = int(c) + 5; +print("\"10\" + 5 =", d); + +print(""); +print("--- Boolean Context ---"); +print("Boolean evaluation (0 = false, nonzero = true):"); + +values = {0, 1, -1, 42, "", "hello"}; +names = {"0", "1", "-1", "42", "\"\"", "\"hello\""}; + +for (i = 0; i < len(values); i++) { + if (values[i]) { + print(names[i], "-> truthy"); + } else { + print(names[i], "-> falsy"); + } +} + +print(""); +print("--- Integer as Boolean ---"); +flag = 1; +if (flag) { + print("flag (1) is truthy"); +} + +flag = 0; +if (flag) { + print("This won't print"); +} else { + print("flag (0) is falsy"); +} + +print(""); +print("--- Type Checking with typeof ---"); +checkTypes = {42, "text", null}; +typeNames = {"integer", "string", "null"}; +for (i = 0; i < len(checkTypes); i++) { + print(typeNames[i], "->", typeof(checkTypes[i])); +} + +class TestClass { + value = 0; + TestClass(this) { + } +} +obj = new TestClass(); +print("object -> type:", typeof(obj)); + +print(""); +print("--- Automatic Initialization ---"); +class DefaultValues { + intVal = 0; + strVal = ""; + boolVal = 0; + nullVal = null; + + DefaultValues(this) { + } + + showDefaults(this) { + print("intVal:", this.intVal); + print("strVal: \"" + this.strVal + "\""); + print("boolVal:", this.boolVal); + print("nullVal:", this.nullVal); + } +} + +defaults = new DefaultValues(); +defaults.showDefaults(); diff --git a/nano.py b/nano.py new file mode 100755 index 0000000..ad50db6 --- /dev/null +++ b/nano.py @@ -0,0 +1,504 @@ +#!/usr/bin/env python3 +import re, copy + +class Pointer: + def __init__(s, v=None): s.val = v + +class NanoClass: + def __init__(s, n, p, m, c, d): s.name, s.props, s.methods, s.constructor, s.destructor = n, p, m, c, d + +class NanoObject: + def __init__(s, c, p): s._class, s._props = c, p + +class Runtime: + def __init__(s): + s.classes, s.globals, s.locals_stack = {}, {}, [{}] + s.return_value, s.returning = None, False + + @property + def locals(s): return s.locals_stack[-1] + def push_scope(s): s.locals_stack.append({}) + def pop_scope(s): len(s.locals_stack) > 1 and s.locals_stack.pop() + + def get_var(s, n): + if n in s.locals: return s.locals[n] + return s.globals.get(n) + + def set_var(s, n, v): + for sc in reversed(s.locals_stack): + if n in sc: sc[n] = v; return + if n in s.globals: s.globals[n] = v + else: s.locals[n] = v + + def set_local(s, n, v): + s.locals[n] = v + + def tokenize(s, c): + c = re.sub(r'//[^\n]*', '', re.sub(r'/\*.*?\*/', '', c, flags=re.DOTALL)) + toks, pats = [], [ + (r'\"[^\"]*\"', 'S'), (r"\'[^\']*\'", 'S'), (r'\d+\.?\d*', 'N'), + (r'class\b', 'CL'), (r'new\b', 'NW'), (r'if\b', 'IF'), (r'else\b', 'EL'), + (r'while\b', 'WH'), (r'for\b', 'FR'), (r'return\b', 'RT'), + (r'null\b', 'NU'), (r'true\b', 'TR'), (r'false\b', 'FA'), + (r'\*\*', 'DS'), (r'\|\|', 'OR'), (r'&&', 'AN'), (r'==', 'EQ'), (r'!=', 'NE'), + (r'<=', 'LE'), (r'>=', 'GE'), (r'\+=', 'PE'), (r'-=', 'ME'), (r'\*=', 'UE'), + (r'/=', 'DE'), (r'\+\+', 'IC'), (r'--', 'DC'), + (r'[a-zA-Z_][a-zA-Z0-9_]*', 'I'), (r'[{}()\[\];,.<>+\-*/%=!&|:~]', 'Y'), (r'\s+', None)] + i = 0 + while i < len(c): + for p, t in pats: + m = re.match(p, c[i:]) + if m: + t and toks.append((t, m.group())) + i += len(m.group()); break + else: i += 1 + return toks + + def parse_block(s, t, st): + if st >= len(t) or t[st] != ('Y', '{'): return [], st + d, i, b = 1, st + 1, [] + while i < len(t) and d > 0: + if t[i] == ('Y', '{'): d += 1 + elif t[i] == ('Y', '}'): d -= 1 + d > 0 and b.append(t[i]); i += 1 + return b, i + + def parse_expr(s, t, st, es=None): + es = es or [';', ',', ')', '}', ']'] + e, d, i = [], 0, st + while i < len(t): + x = t[i] + if d == 0 and x[0] == 'Y' and x[1] in es: break + if x[0] == 'Y' and x[1] in '([{': d += 1 + elif x[0] == 'Y' and x[1] in ')]}': d -= 1 + e.append(x); i += 1 + return e, i + + def to_num(s, v): + if v is None: return 0 + if isinstance(v, (int, float)): return v + try: return int(v) + except: + try: return float(v) + except: return 0 + + def is_truthy(s, v): + if v is None: return False + if isinstance(v, (int, float)): return v != 0 + if isinstance(v, (str, list)): return len(v) > 0 + return True + + def eval_expr(s, t): + if not t: return None + if len(t) == 1: + x = t[0] + if x[0] == 'N': return float(x[1]) if '.' in x[1] else int(x[1]) + if x[0] == 'S': return x[1][1:-1] + if x[0] == 'NU': return None + if x[0] == 'TR': return 1 + if x[0] == 'FA': return 0 + if x[0] == 'I': return s.get_var(x[1]) + if t[0] == ('NW', 'new'): return s.eval_new(t[1:]) + if t[0] == ('Y', '*') and len(t) > 1: + v = s.eval_expr(t[1:]) + return v.val if isinstance(v, Pointer) else v + if t[0] == ('Y', '&') and len(t) > 1 and t[1][0] == 'I': + return Pointer(s.get_var(t[1][1])) + if t[0] == ('Y', '{'): return s.eval_array(t) + for i, x in enumerate(t): + if x == ('OR', '||'): + l = s.eval_expr(t[:i]) + return l if s.is_truthy(l) else s.eval_expr(t[i+1:]) + for i, x in enumerate(t): + if x == ('AN', '&&'): + l = s.eval_expr(t[:i]) + return 0 if not s.is_truthy(l) else (1 if s.is_truthy(s.eval_expr(t[i+1:])) else 0) + for op in [('EQ', '=='), ('NE', '!=')]: + d = 0 + for i, x in enumerate(t): + if x[0] == 'Y' and x[1] in '([{': d += 1 + elif x[0] == 'Y' and x[1] in ')]}': d -= 1 + if d == 0 and x == op: + l, r = s.eval_expr(t[:i]), s.eval_expr(t[i+1:]) + return 1 if (l == r if op[1] == '==' else l != r) else 0 + for op in [('LE', '<='), ('GE', '>='), ('Y', '<'), ('Y', '>')]: + d = 0 + for i, x in enumerate(t): + if x[0] == 'Y' and x[1] in '([{': d += 1 + elif x[0] == 'Y' and x[1] in ')]}': d -= 1 + if d == 0 and x == op: + l, r = s.to_num(s.eval_expr(t[:i])), s.to_num(s.eval_expr(t[i+1:])) + if op[1] == '<': return 1 if l < r else 0 + if op[1] == '>': return 1 if l > r else 0 + if op[1] == '<=': return 1 if l <= r else 0 + if op[1] == '>=': return 1 if l >= r else 0 + d = 0 + for i in range(len(t) - 1, -1, -1): + x = t[i] + if x[0] == 'Y' and x[1] in ')]}': d += 1 + elif x[0] == 'Y' and x[1] in '([{': d -= 1 + if d == 0: + if x == ('Y', '+') and i > 0: + l, r = s.eval_expr(t[:i]), s.eval_expr(t[i+1:]) + if isinstance(l, str) or isinstance(r, str): + return str(l if l is not None else '') + str(r if r is not None else '') + return s.to_num(l) + s.to_num(r) + if x == ('Y', '-') and i > 0: + return s.to_num(s.eval_expr(t[:i])) - s.to_num(s.eval_expr(t[i+1:])) + d = 0 + for i in range(len(t) - 1, -1, -1): + x = t[i] + if x[0] == 'Y' and x[1] in ')]}': d += 1 + elif x[0] == 'Y' and x[1] in '([{': d -= 1 + if d == 0: + if x == ('Y', '*') and i > 0: + return s.to_num(s.eval_expr(t[:i])) * s.to_num(s.eval_expr(t[i+1:])) + if x == ('Y', '/') and i > 0: + r = s.to_num(s.eval_expr(t[i+1:])) + return s.to_num(s.eval_expr(t[:i])) / r if r else 0 + if x == ('Y', '%') and i > 0: + r = s.to_num(s.eval_expr(t[i+1:])) + return s.to_num(s.eval_expr(t[:i])) % r if r else 0 + if t[0] == ('Y', '('): + d, i = 1, 1 + while i < len(t) and d > 0: + if t[i] == ('Y', '('): d += 1 + elif t[i] == ('Y', ')'): d -= 1 + i += 1 + return s.eval_expr(t[1:i-1]) + if len(t) >= 2 and t[0][0] == 'I': + if t[1] == ('Y', '('): return s.eval_call(t) + if t[1] == ('Y', '.'): return s.eval_member(t) + if t[1] == ('Y', '['): return s.eval_index(t) + return None + + def eval_array(s, t): + if not t or t[0] != ('Y', '{'): return [] + items, i = [], 1 + while i < len(t) and t[i] != ('Y', '}'): + e, i = s.parse_expr(t, i, [',', '}']) + e and items.append(s.eval_expr(e)) + i < len(t) and t[i] == ('Y', ',') and (i := i + 1) + return items + + def eval_new(s, t): + if not t or t[0][0] != 'I': return None + c = s.classes.get(t[0][1]) + if not c: return None + o = NanoObject(c, copy.deepcopy(c.props)) + a = s.parse_args(t, 1) if len(t) > 1 and t[1] == ('Y', '(') else [] + c.constructor and s.call_method(o, c.constructor, a) + return o + + def parse_args(s, t, st): + if st >= len(t) or t[st] != ('Y', '('): return [] + a, i, d = [], st + 1, 1 + while i < len(t) and d > 0: + if t[i] == ('Y', ')'): + d -= 1 + if d == 0: break + i += 1; continue + if t[i] == ('Y', '('): d += 1 + e, i = s.parse_expr(t, i, [',', ')']) + e and a.append(s.eval_expr(e)) + i < len(t) and t[i] == ('Y', ',') and (i := i + 1) + return a + + def eval_call(s, t): + n, a = t[0][1], s.parse_args(t, 1) + if n == 'print': print(*a); return + if n == 'len': return len(a[0]) if a and a[0] else 0 + if n == 'str': return str(a[0]) if a and a[0] is not None else '' + if n == 'int': + if not a or a[0] is None: return 0 + try: return int(float(a[0])) + except: return 0 + if n == 'bool': return 1 if a and s.is_truthy(a[0]) else 0 + if n == 'typeof': + if not a: return 'null' + v = a[0] + if v is None: return 'null' + if isinstance(v, int): return 'int' + if isinstance(v, float): return 'float' + if isinstance(v, str): return 'str' + if isinstance(v, list): return 'array' + if isinstance(v, NanoObject): return v._class.name + return 'unknown' + f = s.get_var(n) + if f and isinstance(f, tuple) and len(f) == 2: return s.call_func(f[0], f[1], a) + return None + + def call_func(s, ps, bd, ar): + s.push_scope() + rp, va, vk = [], None, None + for p in ps: + if p.startswith('**'): vk = p[2:] + elif p.startswith('*'): va = p[1:] + else: rp.append(p) + for i, p in enumerate(rp): + if '=' in p: + pn, df = p.split('=', 1) + s.set_local(pn.strip(), ar[i] if i < len(ar) else s.eval_expr(s.tokenize(df.strip()))) + else: s.set_local(p.strip(), ar[i] if i < len(ar) else None) + va and s.set_local(va, list(ar[len(rp):])) + vk and s.set_local(vk, {}) + s.returning, s.return_value = False, None + s.execute_block(bd) + r = s.return_value + s.returning, s.return_value = False, None + s.pop_scope() + return r + + def eval_member(s, t): + o, i = s.get_var(t[0][1]), 2 + while i < len(t): + if t[i] == ('Y', '['): + e, j = s.parse_expr(t, i + 1, [']']) + idx = s.eval_expr(e) + if isinstance(o, (list, str)): + idx = int(idx) if idx is not None else 0 + o = o[idx] if 0 <= idx < len(o) else None + elif isinstance(o, dict): + o = o.get(idx) + i = j + 1 + continue + if t[i][0] != 'I': break + m = t[i][1]; i += 1 + if i < len(t) and t[i] == ('Y', '('): + a = s.parse_args(t, i) + o = s.call_obj_method(o, m, a) + d = 1; i += 1 + while i < len(t) and d > 0: + if t[i] == ('Y', '('): d += 1 + elif t[i] == ('Y', ')'): d -= 1 + i += 1 + else: + if isinstance(o, NanoObject): o = o._props.get(m) + elif isinstance(o, dict): o = o.get(m) + elif isinstance(o, str) and m == 'length': o = len(o) + elif isinstance(o, list) and m == 'length': o = len(o) + if i < len(t) and t[i] == ('Y', '.'): i += 1 + elif i < len(t) and t[i] == ('Y', '['): pass + else: break + return o + + def call_obj_method(s, o, m, a): + if isinstance(o, str): + if m == 'substr': return o[int(a[0]) if a else 0:(int(a[0]) if a else 0)+(int(a[1]) if len(a)>1 else len(o))] + if m == 'split': return o.split(a[0] if a else ' ') + if m == 'count': return o.count(a[0] if a else '') + if m == 'indexOf': return o.find(a[0] if a else '') + if m == 'toUpper': return o.upper() + if m == 'toLower': return o.lower() + if m == 'trim': return o.strip() + if m == 'replace': return o.replace(a[0] if a else '', a[1] if len(a)>1 else '') + if isinstance(o, list): + if m == 'push': o.append(a[0] if a else None); return len(o) + if m == 'pop': return o.pop() if o else None + if m == 'join': return (str(a[0]) if a else '').join(str(x) for x in o) + if m == 'indexOf': + try: return o.index(a[0] if a else None) + except: return -1 + if m == 'slice': return o[int(a[0]) if a else 0:int(a[1]) if len(a)>1 else len(o)] + if isinstance(o, NanoObject) and m in o._class.methods: + return s.call_method(o, o._class.methods[m], a) + return None + + def call_method(s, o, md, ar): + ps, bd = md + s.push_scope() + rp, va, vk = [], None, None + for p in ps: + if p.startswith('**'): vk = p[2:] + elif p.startswith('*'): va = p[1:] + else: rp.append(p) + if rp and rp[0].strip() == 'this': s.set_local('this', o); rp = rp[1:] + for i, p in enumerate(rp): + if '=' in p: + pn, df = p.split('=', 1) + s.set_local(pn.strip(), ar[i] if i < len(ar) else s.eval_expr(s.tokenize(df.strip()))) + else: s.set_local(p.strip(), ar[i] if i < len(ar) else None) + va and s.set_local(va, list(ar[len(rp):])) + vk and s.set_local(vk, {}) + s.returning, s.return_value = False, None + s.execute_block(bd) + r = s.return_value + s.returning, s.return_value = False, None + s.pop_scope() + return r + + def eval_index(s, t): + o = s.get_var(t[0][1]) + i = 1 + while i < len(t) and t[i] == ('Y', '['): + e, j = s.parse_expr(t, i + 1, [']']) + idx = s.eval_expr(e) + if isinstance(o, (list, str)): + idx = int(idx) if idx is not None else 0 + o = o[idx] if 0 <= idx < len(o) else None + elif isinstance(o, dict): + o = o.get(idx) + else: + return None + i = j + 1 + return o + + def execute(s, c): s.execute_tokens(s.tokenize(c)) + + def execute_tokens(s, t): + i = 0 + while i < len(t): + if s.returning: return s.return_value + if t[i] == ('CL', 'class'): i = s.parse_class(t, i) + elif t[i] == ('IF', 'if'): i = s.execute_if(t, i) + elif t[i] == ('WH', 'while'): i = s.execute_while(t, i) + elif t[i] == ('FR', 'for'): i = s.execute_for(t, i) + elif t[i] == ('RT', 'return'): + e, i = s.parse_expr(t, i + 1, [';']) + s.return_value, s.returning = s.eval_expr(e), True + return s.return_value + else: + st, i = s.parse_expr(t, i, [';']) + st and s.execute_stmt(st) + i < len(t) and t[i] == ('Y', ';') and (i := i + 1) + return None + + def execute_block(s, t): return s.execute_tokens(t) + + def parse_class(s, t, st): + n = t[st + 1][1] + bd, i = s.parse_block(t, st + 2) + ps, ms, ct, dt, j = {}, {}, None, None, 0 + while j < len(bd): + if bd[j] == ('Y', '~') and j + 1 < len(bd): + j += 1 + if bd[j][0] == 'I' and bd[j][1] == n: + j += 1 + pm, j = s.parse_params(bd, j) + mb, j = s.parse_block(bd, j) + dt = (pm, mb) + elif bd[j][0] == 'I': + pn = bd[j][1]; j += 1 + if j < len(bd) and bd[j] == ('Y', '('): + pm, j = s.parse_params(bd, j) + mb, j = s.parse_block(bd, j) + if pn == n: ct = (pm, mb) + else: ms[pn] = (pm, mb) + elif j < len(bd) and bd[j] == ('Y', '='): + j += 1 + e, j = s.parse_expr(bd, j, [';']) + ps[pn] = s.eval_expr(e) + j < len(bd) and bd[j] == ('Y', ';') and (j := j + 1) + else: + ps[pn] = None + j < len(bd) and bd[j] == ('Y', ';') and (j := j + 1) + else: j += 1 + s.classes[n] = NanoClass(n, ps, ms, ct, dt) + return i + + def parse_params(s, t, st): + if st >= len(t) or t[st] != ('Y', '('): return [], st + ps, i, c, d = [], st + 1, '', 0 + while i < len(t): + x = t[i] + if x == ('Y', '('): d += 1; c += x[1] + elif x == ('Y', ')'): + if d == 0: c.strip() and ps.append(c.strip()); i += 1; break + d -= 1; c += x[1] + elif x == ('Y', ',') and d == 0: c.strip() and ps.append(c.strip()); c = '' + else: c += x[1] + i += 1 + return ps, i + + def execute_if(s, t, st): + i = st + 1 + if i >= len(t) or t[i] != ('Y', '('): return i + cd, i = s.parse_expr(t, i + 1, [')']); i += 1 + bd, i = s.parse_block(t, i) + eb = [] + if i < len(t) and t[i] == ('EL', 'else'): + i += 1 + if i < len(t) and t[i] == ('IF', 'if'): + return s.execute_if(t, i) if not s.is_truthy(s.eval_expr(cd)) else i + eb, i = s.parse_block(t, i) + if s.is_truthy(s.eval_expr(cd)): s.execute_block(bd) + elif eb: s.execute_block(eb) + return i + + def execute_while(s, t, st): + i = st + 1 + if i >= len(t) or t[i] != ('Y', '('): return i + cd, ce = s.parse_expr(t, i + 1, [')']) + bd, i = s.parse_block(t, ce + 1) + while s.is_truthy(s.eval_expr(cd)): + s.execute_block(bd) + if s.returning: break + return i + + def execute_for(s, t, st): + i = st + 1 + if i >= len(t) or t[i] != ('Y', '('): return i + it, i = s.parse_expr(t, i + 1, [';']) + it and s.execute_stmt(it); i += 1 + cd, i = s.parse_expr(t, i, [';']); i += 1 + up, i = s.parse_expr(t, i, [')']); i += 1 + bd, i = s.parse_block(t, i) + while s.is_truthy(s.eval_expr(cd)): + s.execute_block(bd) + if s.returning: break + up and s.execute_stmt(up) + return i + + def execute_stmt(s, t): + if not t: return + for i, x in enumerate(t): + if x == ('PE', '+='): + tg, vl = t[:i], s.eval_expr(t[i+1:]) + cr = s.eval_expr(tg) + nv = str(cr if cr is not None else '') + str(vl if vl is not None else '') if isinstance(cr, str) or isinstance(vl, str) else s.to_num(cr) + s.to_num(vl) + s.assign(tg, nv); return + if x == ('ME', '-='): s.assign(t[:i], s.to_num(s.eval_expr(t[:i])) - s.to_num(s.eval_expr(t[i+1:]))); return + if x == ('UE', '*='): s.assign(t[:i], s.to_num(s.eval_expr(t[:i])) * s.to_num(s.eval_expr(t[i+1:]))); return + if x == ('DE', '/='): + r = s.to_num(s.eval_expr(t[i+1:])) + s.assign(t[:i], s.to_num(s.eval_expr(t[:i])) / r if r else 0); return + if x == ('Y', '='): s.assign(t[:i], s.eval_expr(t[i+1:])); return + for i, x in enumerate(t): + if x == ('IC', '++'): tg = t[:i] if i > 0 else t[i+1:]; s.assign(tg, s.to_num(s.eval_expr(tg)) + 1); return + if x == ('DC', '--'): tg = t[:i] if i > 0 else t[i+1:]; s.assign(tg, s.to_num(s.eval_expr(tg)) - 1); return + s.eval_expr(t) + + def assign(s, tg, v): + if not tg: return + if len(tg) == 1 and tg[0][0] == 'I': s.set_var(tg[0][1], v); return + if len(tg) >= 3 and tg[1] == ('Y', '.'): + o, i = s.get_var(tg[0][1]), 2 + while i < len(tg) - 2: + m = tg[i][1] + if isinstance(o, NanoObject): o = o._props.get(m) + elif isinstance(o, dict): o = o.get(m) + i += 2 + m = tg[i][1] + if isinstance(o, NanoObject): o._props[m] = v + elif isinstance(o, dict): o[m] = v + return + if len(tg) >= 3 and tg[1] == ('Y', '['): + o = s.get_var(tg[0][1]) + e, _ = s.parse_expr(tg, 2, [']']) + idx = s.eval_expr(e) + if isinstance(o, list): + idx = int(idx) if idx is not None else 0 + while len(o) <= idx: o.append(None) + o[idx] = v + elif isinstance(o, dict): o[idx] = v + return + if tg[0] == ('Y', '*'): + p = s.eval_expr(tg[1:]) + isinstance(p, Pointer) and setattr(p, 'val', v) + +def run_file(fn): + with open(fn) as f: Runtime().execute(f.read()) + +if __name__ == '__main__': + import sys + len(sys.argv) > 1 and run_file(sys.argv[1]) or print("Usage: python nano.py ") diff --git a/test.nano b/test.nano new file mode 100644 index 0000000..095e1dd --- /dev/null +++ b/test.nano @@ -0,0 +1,139 @@ +class Person { + name = "Unknown"; + age = 0; + _temp = null; + + Person(this, name, age = 18) { + this.name = name; + this.age = age; + } + + ~Person(this) { + print("Person destroyed:", this.name); + } + + greet(this) { + print("Hello, I am", this.name, "and I am", this.age, "years old"); + } + + setAge(this, newAge) { + this.age = newAge; + } +} + +person = new Person("Alice", 25); +person.greet(); + +person2 = new Person("Bob"); +person2.greet(); + +person.setAge(30); +print("Alice new age:", person.age); + +x = 10; +y = 20; +print("Sum:", x + y); +print("Product:", x * y); + +str1 = "Hello"; +str2 = " World"; +result = str1 + str2; +print("Concatenation:", result); + +str1 += "!"; +print("After +=:", str1); + +count = 0; +while (count < 3) { + print("Count:", count); + count++; +} + +for (i = 0; i < 3; i++) { + print("For loop i:", i); +} + +val = null || 5; +print("Default value:", val); + +val2 = 10 || 5; +print("Existing value:", val2); + +if (1) { + print("Truthy: 1 is true"); +} + +if (0) { + print("This should not print"); +} else { + print("Falsy: 0 is false"); +} + +arr = {1, 2, 3, 4, 5}; +print("Array element 0:", arr[0]); +print("Array element 2:", arr[2]); + +arr[1] = 10; +print("Modified array element 1:", arr[1]); + +testStr = "hello world"; +print("Substr:", testStr.substr(0, 5)); +print("Count 'o':", testStr.count("o")); + +parts = testStr.split(" "); +print("Split result count:", len(parts)); + +numStr = "42"; +num = int(numStr); +print("Converted to int:", num); + +converted = str(123); +print("Converted to str:", converted); + +class Counter { + value = 0; + + Counter(this, start = 0) { + this.value = start; + } + + increment(this) { + this.value++; + } + + decrement(this) { + this.value--; + } + + getValue(this) { + return this.value; + } +} + +counter = new Counter(5); +print("Counter initial:", counter.getValue()); +counter.increment(); +counter.increment(); +print("Counter after 2 increments:", counter.getValue()); +counter.decrement(); +print("Counter after decrement:", counter.getValue()); + +a = 5; +b = 5; +if (a == b) { + print("a equals b"); +} + +if (a != 10) { + print("a is not 10"); +} + +if (a < 10 && b < 10) { + print("Both a and b are less than 10"); +} + +if (a > 10 || b < 10) { + print("Either a > 10 or b < 10"); +} + +print("All tests completed successfully");