diff --git a/benches/vm_bench.rs b/benches/vm_bench.rs new file mode 100644 index 0000000..d65aa7b --- /dev/null +++ b/benches/vm_bench.rs @@ -0,0 +1,158 @@ +//! WokeLang VM Performance Benchmarks +//! +//! Benchmarks comparing interpreter vs VM execution. + +use std::time::Instant; +use wokelang::{Interpreter, Lexer, Parser}; +use wokelang::vm::{run_vm, compile}; + +fn bench_interpreter(source: &str, iterations: u32) -> std::time::Duration { + let start = Instant::now(); + + for _ in 0..iterations { + let lexer = Lexer::new(source); + let tokens = lexer.tokenize().unwrap(); + let mut parser = Parser::new(tokens, source); + let program = parser.parse().unwrap(); + let mut interpreter = Interpreter::new(); + interpreter.run(&program).unwrap(); + } + + start.elapsed() +} + +fn bench_vm(source: &str, iterations: u32) -> std::time::Duration { + let start = Instant::now(); + + for _ in 0..iterations { + run_vm(source).unwrap(); + } + + start.elapsed() +} + +fn bench_vm_precompiled(source: &str, iterations: u32) -> std::time::Duration { + // Compile once + let compiled = compile(source).unwrap(); + + let start = Instant::now(); + + for _ in 0..iterations { + let mut vm = wokelang::vm::VirtualMachine::new(compiled.clone()); + vm.run().unwrap(); + } + + start.elapsed() +} + +fn main() { + println!("WokeLang Performance Benchmarks"); + println!("================================\n"); + + // Benchmark 1: Simple arithmetic + let simple_arithmetic = r#" + to main() { + remember x = 10; + remember y = 20; + give back x + y * 3 - 5; + } + "#; + + // Benchmark 2: Function calls + let function_calls = r#" + to add(a: Int, b: Int) -> Int { + give back a + b; + } + + to main() { + remember sum = 0; + sum = add(sum, 10); + sum = add(sum, 20); + sum = add(sum, 30); + give back sum; + } + "#; + + // Benchmark 3: Conditionals + let conditionals = r#" + to abs(n: Int) -> Int { + when n < 0 { + give back 0 - n; + } otherwise { + give back n; + } + } + + to main() { + remember sum = 0; + sum = sum + abs(-5); + sum = sum + abs(10); + sum = sum + abs(-15); + give back sum; + } + "#; + + // Benchmark 4: Loops + let loops = r#" + to main() { + remember sum = 0; + repeat 10 times { + sum = sum + 1; + } + give back sum; + } + "#; + + // Benchmark 5: Recursion + let recursion = r#" + to factorial(n: Int) -> Int { + when n <= 1 { + give back 1; + } + give back n * factorial(n - 1); + } + + to main() { + give back factorial(10); + } + "#; + + let iterations = 1000; + + let benchmarks = [ + ("Simple Arithmetic", simple_arithmetic), + ("Function Calls", function_calls), + ("Conditionals", conditionals), + ("Loops", loops), + ("Recursion", recursion), + ]; + + for (name, source) in benchmarks { + println!("Benchmark: {}", name); + println!("{}", "-".repeat(50)); + + let interp_time = bench_interpreter(source, iterations); + let vm_time = bench_vm(source, iterations); + let vm_precompiled_time = bench_vm_precompiled(source, iterations); + + println!( + " Interpreter: {:>8.2}ms ({:>8.2}us/iter)", + interp_time.as_secs_f64() * 1000.0, + interp_time.as_secs_f64() * 1_000_000.0 / iterations as f64 + ); + println!( + " VM (full): {:>8.2}ms ({:>8.2}us/iter)", + vm_time.as_secs_f64() * 1000.0, + vm_time.as_secs_f64() * 1_000_000.0 / iterations as f64 + ); + println!( + " VM (precomp): {:>8.2}ms ({:>8.2}us/iter)", + vm_precompiled_time.as_secs_f64() * 1000.0, + vm_precompiled_time.as_secs_f64() * 1_000_000.0 / iterations as f64 + ); + + let speedup = interp_time.as_secs_f64() / vm_precompiled_time.as_secs_f64(); + println!(" Speedup (precompiled vs interpreter): {:.2}x", speedup); + println!(); + } +} diff --git a/docs/grammar.ebnf b/docs/grammar.ebnf new file mode 100644 index 0000000..ba29e58 --- /dev/null +++ b/docs/grammar.ebnf @@ -0,0 +1,315 @@ +(* WokeLang EBNF Grammar Specification *) +(* A human-centered, consent-driven programming language *) + +(* ===================================================================== *) +(* LEXICAL GRAMMAR *) +(* ===================================================================== *) + +(* Whitespace and Comments *) +whitespace = { " " | "\t" | "\n" | "\r" | "\f" } ; +line_comment = "//" , { character - "\n" } ; +block_comment = "/*" , { character - "*/" } , "*/" ; + +(* Literals *) +digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +letter = "a" | "b" | ... | "z" | "A" | "B" | ... | "Z" ; +integer = digit , { digit } ; +float = digit , { digit } , "." , digit , { digit } ; +escape_char = "\\" , ( "n" | "t" | "r" | '"' | "'" | "\\" ) ; +string_char = ( character - '"' - "\\" ) | escape_char ; +string = '"' , { string_char } , '"' ; +identifier = letter , { letter | digit | "_" } ; +boolean = "true" | "false" ; + +(* ===================================================================== *) +(* SYNTACTIC GRAMMAR *) +(* ===================================================================== *) + +(* Program Structure *) +program = { top_level_item } ; + +top_level_item = function_def + | consent_block + | gratitude_decl + | worker_def + | side_quest_def + | superpower_decl + | module_import + | module_export + | pragma + | type_def + | const_def ; + +(* ===================================================================== *) +(* FUNCTIONS *) +(* ===================================================================== *) + +function_def = [ emote_tag ] , "to" , identifier , "(" , [ param_list ] , ")" , + [ return_type ] , "{" , + [ hello_clause ] , + { statement } , + [ goodbye_clause ] , + "}" ; + +param_list = parameter , { "," , parameter } ; +parameter = identifier , [ ":" , type ] ; +return_type = ( "->" | "→" ) , type ; +hello_clause = "hello" , string , ";" ; +goodbye_clause = "goodbye" , string , ";" ; + +(* ===================================================================== *) +(* STATEMENTS *) +(* ===================================================================== *) + +statement = var_decl + | assignment + | return_stmt + | conditional + | loop_stmt + | attempt_block + | consent_stmt + | expression_stmt + | worker_spawn + | send_message + | receive_message + | await_worker + | cancel_worker + | complain_stmt + | emote_annotated + | decide_stmt ; + +(* Variable Declaration *) +var_decl = "remember" , identifier , "=" , expression , + [ "measured" , "in" , identifier ] , ";" ; + +(* Assignment *) +assignment = identifier , "=" , expression , ";" ; + +(* Return Statement *) +return_stmt = "give" , "back" , expression , ";" ; + +(* Conditional *) +conditional = "when" , expression , "{" , { statement } , "}" , + [ "otherwise" , "{" , { statement } , "}" ] ; + +(* Loop *) +loop_stmt = "repeat" , expression , "times" , "{" , { statement } , "}" ; + +(* Attempt Block (Error Handling) *) +attempt_block = "attempt" , "safely" , "{" , { statement } , "}" , + "or" , "reassure" , string , ";" ; + +(* Consent Statement *) +consent_stmt = "only" , "if" , "okay" , string , "{" , { statement } , "}" ; + +(* Expression Statement *) +expression_stmt = expression , ";" ; + +(* Complain Statement (Error Throwing) *) +complain_stmt = "complain" , string , ";" ; + +(* Emote-Annotated Statement *) +emote_annotated = emote_tag , statement ; + +(* ===================================================================== *) +(* PATTERN MATCHING *) +(* ===================================================================== *) + +decide_stmt = "decide" , "based" , "on" , expression , "{" , + { match_arm } , + "}" ; + +match_arm = pattern , ( "->" | "→" ) , "{" , { statement } , "}" ; + +pattern = literal + | identifier + | "_" (* Wildcard *) + | "Okay" , "(" , [ identifier ] , ")" (* Result ok pattern *) + | "Oops" , "(" , [ identifier ] , ")" (* Result error pattern *) + | identifier , "(" , [ pattern_list ] , ")" (* Constructor *) + | pattern , "when" , expression ; (* Guard *) + +pattern_list = pattern , { "," , pattern } ; + +(* ===================================================================== *) +(* CONCURRENCY *) +(* ===================================================================== *) + +worker_def = "worker" , identifier , "{" , { statement } , "}" ; +side_quest_def = "side" , "quest" , identifier , "{" , { statement } , "}" ; +superpower_decl = "superpower" , identifier , "{" , { statement } , "}" ; + +worker_spawn = "spawn" , "worker" , identifier , ";" ; +send_message = "send" , expression , "to" , identifier , ";" ; +receive_message = "receive" , "from" , identifier , ";" ; +await_worker = "await" , identifier , ";" ; +cancel_worker = "cancel" , identifier , ";" ; + +(* ===================================================================== *) +(* MODULES *) +(* ===================================================================== *) + +module_import = "use" , qualified_name , [ "renamed" , identifier ] , ";" ; +module_export = "share" , identifier , ";" ; +qualified_name = identifier , { "." , identifier } ; + +(* ===================================================================== *) +(* CONSENT & SAFETY *) +(* ===================================================================== *) + +consent_block = "only" , "if" , "okay" , string , "{" , { statement } , "}" ; + +(* ===================================================================== *) +(* GRATITUDE *) +(* ===================================================================== *) + +gratitude_decl = "thanks" , "to" , "{" , { gratitude_entry } , "}" ; +gratitude_entry = string , ( "->" | "→" ) , string , ";" ; + +(* ===================================================================== *) +(* PRAGMAS *) +(* ===================================================================== *) + +pragma = "#" , pragma_directive , ( "on" | "off" ) , ";" ; +pragma_directive = "care" | "strict" | "verbose" ; + +(* ===================================================================== *) +(* TYPES *) +(* ===================================================================== *) + +type_def = "type" , identifier , "=" , type_variant , ";" ; + +type_variant = struct_type + | enum_type + | type ; (* Type alias *) + +struct_type = "{" , { field_def } , "}" ; +field_def = identifier , ":" , type , [ "," ] ; + +enum_type = variant , { "|" , variant } ; +variant = identifier , [ "(" , type_list , ")" ] ; +type_list = type , { "," , type } ; + +type = basic_type + | array_type + | optional_type + | reference_type + | result_type + | generic_type ; + +basic_type = "String" | "Int" | "Float" | "Bool" | identifier ; +array_type = "[" , type , "]" ; +optional_type = "Maybe" , type ; +reference_type = "&" , type ; +result_type = "Result" , "[" , type , [ "," , type ] , "]" ; +generic_type = identifier , "[" , type_list , "]" ; + +(* Constant Definition *) +const_def = "const" , identifier , ":" , type , "=" , expression , ";" ; + +(* ===================================================================== *) +(* EXPRESSIONS *) +(* ===================================================================== *) + +expression = logical_or ; + +logical_or = logical_and , { "or" , logical_and } ; +logical_and = equality , { "and" , equality } ; +equality = comparison , { ( "==" | "!=" ) , comparison } ; +comparison = term , { ( "<" | ">" | "<=" | ">=" ) , term } ; +term = factor , { ( "+" | "-" ) , factor } ; +factor = unary , { ( "*" | "/" | "%" ) , unary } ; + +unary = ( "-" | "not" ) , unary + | postfix ; + +postfix = primary , { postfix_op } ; +postfix_op = "?" (* Try operator *) + | "(" , [ arg_list ] , ")" (* Function call *) + | "[" , expression , "]" (* Index access *) + | "measured" , "in" , identifier ; (* Unit annotation *) + +primary = literal + | identifier + | "(" , expression , ")" + | array_literal + | result_constructor + | "unwrap" , expression + | "thanks" , "(" , string , ")" ; (* Gratitude literal *) + +(* ===================================================================== *) +(* LITERALS *) +(* ===================================================================== *) + +literal = integer + | float + | string + | boolean ; + +array_literal = "[" , [ expression , { "," , expression } ] , "]" ; + +result_constructor = "Okay" , "(" , expression , ")" + | "Oops" , "(" , expression , ")" ; + +(* ===================================================================== *) +(* FUNCTION CALLS *) +(* ===================================================================== *) + +arg_list = expression , { "," , expression } ; + +(* ===================================================================== *) +(* EMOTE TAGS (Emotional Annotations) *) +(* ===================================================================== *) + +emote_tag = "@" , identifier , [ "(" , emote_params , ")" ] ; +emote_params = emote_param , { "," , emote_param } ; +emote_param = identifier , "=" , emote_value ; +emote_value = integer | float | string | identifier ; + +(* ===================================================================== *) +(* OPERATORS (Precedence: lowest to highest) *) +(* ===================================================================== *) +(* + 1. or (logical or) + 2. and (logical and) + 3. == != (equality) + 4. < > <= >= (comparison) + 5. + - (additive) + 6. * / % (multiplicative) + 7. - not (unary prefix) + 8. ? () [] (postfix) +*) + +(* ===================================================================== *) +(* RESERVED KEYWORDS *) +(* ===================================================================== *) +(* + Control Flow: to, give, back, remember, when, otherwise, repeat, times + Consent/Safety: only, if, okay, attempt, safely, reassure, complain + Gratitude: thanks + Lifecycle: hello, goodbye + Concurrency: worker, side, quest, superpower, spawn, send, receive, + channel, await, cancel, from + Pattern Match: decide, based, on + Units: measured, in + Modules: use, renamed, share + Types: type, const, String, Int, Float, Bool, Maybe + Constraints: must, have + Pragmas: care, strict, verbose + Boolean: true, false, and, or, not + Result Types: Okay, Oops, unwrap +*) + +(* ===================================================================== *) +(* UNICODE SUPPORT *) +(* ===================================================================== *) +(* + The arrow operator supports both ASCII and Unicode forms: + - ASCII: -> + - Unicode: → + + This allows for more readable code while maintaining compatibility: + + to greet(name: String) -> String { ... } + to greet(name: String) → String { ... } +*) diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000..c9ad315 --- /dev/null +++ b/lib/README.md @@ -0,0 +1,128 @@ +# WokeLang Standard Library + +A human-centered library collection for WokeLang, organized into common utilities +and language-specific features. + +## Structure + +``` +lib/ +├── common/ # Language-agnostic utilities +│ ├── prelude.woke # Core utilities (identity, boolean, numeric, string, array) +│ ├── collections.woke # Higher-order functions (map, filter, reduce) +│ └── async.woke # Concurrent programming patterns +│ +└── wokelang/ # WokeLang-specific features + ├── consent.woke # Consent and permission management + ├── emotes.woke # Emotional annotations and sentiment-aware code + └── gratitude.woke # Attribution and acknowledgment utilities +``` + +## Common Library + +### prelude.woke +Core utilities that every program needs: +- **Identity functions**: `identity`, `constant`, `flip` +- **Boolean operations**: `negate`, `allTrue`, `anyTrue` +- **Numeric utilities**: `clamp`, `inRange`, `sign`, `isEven`, `isOdd`, `gcd`, `lcm` +- **String utilities**: `isEmpty`, `repeat`, `startsWith`, `endsWith` +- **Array utilities**: `head`, `tail`, `last`, `reverse`, `contains`, `indexOf` +- **Result utilities**: `isOkay`, `isOops`, `getOrDefault`, `mapResult` +- **Debugging**: `debug`, `assert` + +### collections.woke +Higher-order functions for data transformation: +- **Core transformations**: `map`, `filter`, `reduce`, `reduceRight` +- **Searching**: `find`, `findIndex`, `all`, `any`, `none` +- **Advanced transformations**: `flatten`, `flatMap`, `zip`, `unzip`, `partition`, `groupBy` +- **Slicing**: `take`, `drop`, `takeWhile`, `dropWhile`, `slice` +- **Statistics**: `count`, `sum`, `product`, `average` +- **Sorting**: `sortInts` +- **Deduplication**: `unique`, `distinctBy` + +### async.woke +Concurrent programming utilities: +- **Worker patterns**: `TaskRunner`, parallel execution +- **Promise-like patterns**: `runAsync`, `parallel`, `race` +- **Retry patterns**: `retry`, `retryWithBackoff` +- **Timeout patterns**: `withTimeout` +- **Debounce/throttle**: `debounce` +- **Channel utilities**: `createChannel`, `channelSend`, `channelReceive` + +## WokeLang-Specific Library + +### consent.woke +Human-centered consent management: +- **Safe execution**: `withConsent`, `requireAllConsents`, `withAnyConsent` +- **Permission constants**: `PERM_READ_FILE`, `PERM_NETWORK_HTTP`, etc. +- **Safe operations**: `safeReadFile`, `safeWriteFile`, `safeHttpGet` +- **Audit logging**: `logConsentRequest`, `createAuditTrail` +- **Privacy patterns**: `withPrivacy`, `redact`, `anonymize` + +### emotes.woke +Emotional annotations for code: +- **Emote types**: `Happy`, `Sad`, `Curious`, `Careful`, `Mindful`, etc. +- **Emote logging**: `logWithEmote` +- **Sentiment-aware execution**: `withCaution`, `withInvestigation` +- **Emotional state tracking**: `EmotionalState`, `updateEmotionalState` +- **Code health indicators**: `assessCodeHealth`, `reportCodeHealth` + +### gratitude.woke +Attribution and acknowledgment: +- **Gratitude registry**: `createGratitudeRegistry`, `acknowledge` +- **Credit generation**: `generateCredits`, `generateThankYouNote` +- **Dependency attribution**: `acknowledgeDependency`, `generateLicenseNotices` +- **Contributor recognition**: `recognizeContributor` +- **Gratitude expressions**: `expressGratitude`, `randomGratitudeQuote` + +## Usage + +```woke +use lib.common.prelude; +use lib.common.collections; +use lib.wokelang.consent; +use lib.wokelang.emotes; + +to main() { + hello "Starting application"; + + (* Use common utilities *) + remember numbers = [1, 2, 3, 4, 5]; + remember doubled = map(numbers, to (x: Int) -> Int { give back x * 2; }); + + (* Use consent-aware file reading *) + remember content = safeReadFile("config.json")?; + + (* Log with emotional context *) + @happy(intensity=8) + logWithEmote(Proud(8), "Application started successfully!"); + + give back 0; + goodbye "Application complete"; +} +``` + +## Philosophy + +The WokeLang standard library embodies the language's core principles: + +1. **Human-Centered**: Every function is designed with human developers in mind +2. **Consent-First**: Sensitive operations require explicit permission +3. **Emotionally Aware**: Code can express and track emotional context +4. **Grateful**: Attribution and acknowledgment are first-class concepts + +## Contributing + +When contributing to the library: +1. Include appropriate emote annotations +2. Add gratitude blocks acknowledging inspirations +3. Ensure consent is requested for sensitive operations +4. Write clear hello/goodbye lifecycle messages + +## Gratitude + +This library was built with gratitude to: +- Functional programming communities for inspiring composable patterns +- Rust for Result types and error handling patterns +- Go for channel-based concurrency +- All contributors who help make WokeLang more human-centered diff --git a/lib/common/async.woke b/lib/common/async.woke new file mode 100644 index 0000000..d5dcf47 --- /dev/null +++ b/lib/common/async.woke @@ -0,0 +1,280 @@ +(* WokeLang Common Library - Async Utilities *) +(* Patterns for concurrent and asynchronous programming *) + +(* ===================================================================== *) +(* WORKER PATTERNS *) +(* ===================================================================== *) + +(* Worker pool for parallel task execution *) +worker TaskRunner { + hello "Task runner worker started"; + + remember running = true; + + repeat 1000 times { + when not running { + give back; + } + + remember task = receive from main; + decide based on task { + Okay(work) -> { + remember result = work(); + send result to main; + } + Oops(_) -> { + running = false; + } + } + } + + goodbye "Task runner worker stopped"; +} + +(* ===================================================================== *) +(* PROMISE-LIKE PATTERNS *) +(* ===================================================================== *) + +to runAsync(task: () -> a) -> Result[a, String] { + hello "Running async task"; + + spawn worker taskWorker; + send task to taskWorker; + + remember result = receive from taskWorker; + await taskWorker; + + give back result; + + goodbye "Async task complete"; +} + +to parallel(tasks: [() -> a]) -> [Result[a, String]] { + hello "Running tasks in parallel"; + + remember results = []; + remember workers = []; + + (* Spawn workers for each task *) + repeat len(tasks) times { + spawn worker parallelWorker; + workers = append(workers, parallelWorker); + send tasks[__i__] to parallelWorker; + } + + (* Collect results *) + repeat len(workers) times { + remember result = receive from workers[__i__]; + results = append(results, result); + await workers[__i__]; + } + + give back results; + + goodbye "Parallel execution complete"; +} + +to race(tasks: [() -> a]) -> Result[a, String] { + hello "Racing tasks"; + + remember workers = []; + + (* Spawn all workers *) + repeat len(tasks) times { + spawn worker raceWorker; + workers = append(workers, raceWorker); + send tasks[__i__] to raceWorker; + } + + (* Wait for first result *) + remember firstResult = Oops("No tasks completed"); + remember gotResult = false; + + repeat len(workers) times { + when not gotResult { + attempt safely { + remember result = receive from workers[__i__]; + decide based on result { + Okay(value) -> { + firstResult = Okay(value); + gotResult = true; + } + Oops(_) -> { } + } + } or reassure "Worker failed"; + } + } + + (* Cancel remaining workers *) + repeat len(workers) times { + cancel workers[__i__]; + } + + give back firstResult; + + goodbye "Race complete"; +} + +(* ===================================================================== *) +(* RETRY PATTERNS *) +(* ===================================================================== *) + +to retry(task: () -> Result[a, String], maxAttempts: Int) -> Result[a, String] { + hello "Attempting task with retries"; + + remember attempts = 0; + remember lastError = ""; + + repeat maxAttempts times { + attempts = attempts + 1; + remember result = task(); + + decide based on result { + Okay(value) -> { + give back Okay(value); + } + Oops(err) -> { + lastError = err; + (* Could add delay here *) + } + } + } + + give back Oops("Failed after " + toString(maxAttempts) + " attempts: " + lastError); + + goodbye "Retry complete"; +} + +to retryWithBackoff(task: () -> Result[a, String], maxAttempts: Int, initialDelay: Int) -> Result[a, String] { + hello "Attempting task with exponential backoff"; + + remember attempts = 0; + remember delay = initialDelay; + remember lastError = ""; + + repeat maxAttempts times { + attempts = attempts + 1; + remember result = task(); + + decide based on result { + Okay(value) -> { + give back Okay(value); + } + Oops(err) -> { + lastError = err; + sleep(delay); + delay = delay * 2; + } + } + } + + give back Oops("Failed after " + toString(maxAttempts) + " attempts: " + lastError); + + goodbye "Retry with backoff complete"; +} + +(* ===================================================================== *) +(* TIMEOUT PATTERNS *) +(* ===================================================================== *) + +to withTimeout(task: () -> a, timeoutMs: Int) -> Result[a, String] { + hello "Running task with timeout"; + + spawn worker timeoutWorker; + send task to timeoutWorker; + + remember startTime = now(); + remember result = Oops("Timeout"); + remember done = false; + + repeat 1000 times { + when done { + give back result; + } + + when elapsed(startTime) > timeoutMs { + cancel timeoutWorker; + give back Oops("Task timed out after " + toString(timeoutMs) + "ms"); + } + + attempt safely { + remember workerResult = receive from timeoutWorker; + result = Okay(workerResult); + done = true; + } or reassure "Waiting for result"; + + sleep(10); + } + + give back result; + + goodbye "Timeout check complete"; +} + +(* ===================================================================== *) +(* DEBOUNCE & THROTTLE *) +(* ===================================================================== *) + +to debounce(f: () -> a, delayMs: Int) -> () -> Result[a, String] { + (* Returns a function that only executes after delay with no new calls *) + remember lastCallTime = 0; + remember pendingResult = Oops("No result yet"); + + to debouncedFn() -> Result[a, String] { + remember currentTime = now(); + lastCallTime = currentTime; + + sleep(delayMs); + + when now() - lastCallTime >= delayMs { + pendingResult = Okay(f()); + } + + give back pendingResult; + } + + give back debouncedFn; +} + +(* ===================================================================== *) +(* CHANNEL UTILITIES *) +(* ===================================================================== *) + +type Channel[T] = { + buffer: [T], + maxSize: Int +}; + +to createChannel(maxSize: Int) -> Channel[a] { + give back { + buffer: [], + maxSize: maxSize + }; +} + +to channelSend(ch: Channel[a], value: a) -> Result[Bool, String] { + when len(ch.buffer) >= ch.maxSize { + give back Oops("Channel buffer full"); + } + ch.buffer = append(ch.buffer, value); + give back Okay(true); +} + +to channelReceive(ch: Channel[a]) -> Result[a, String] { + when len(ch.buffer) == 0 { + give back Oops("Channel empty"); + } + remember value = ch.buffer[0]; + ch.buffer = tail(ch.buffer); + give back Okay(value); +} + +(* ===================================================================== *) +(* GRATITUDE *) +(* ===================================================================== *) + +thanks to { + "Go" → "Inspiring channel-based concurrency patterns"; + "JavaScript" → "Promise patterns and async/await concepts"; + "Erlang" → "Actor model and message passing"; +} diff --git a/lib/common/collections.woke b/lib/common/collections.woke new file mode 100644 index 0000000..f6d18f4 --- /dev/null +++ b/lib/common/collections.woke @@ -0,0 +1,309 @@ +(* WokeLang Common Library - Collections *) +(* Higher-order functions for working with collections *) + +(* ===================================================================== *) +(* MAP, FILTER, REDUCE *) +(* ===================================================================== *) + +to map(arr: [a], f: (a) -> b) -> [b] { + hello "Mapping over collection"; + remember result = []; + repeat len(arr) times { + result = append(result, f(arr[__i__])); + } + give back result; + goodbye "Map complete"; +} + +to filter(arr: [a], predicate: (a) -> Bool) -> [a] { + hello "Filtering collection"; + remember result = []; + repeat len(arr) times { + when predicate(arr[__i__]) { + result = append(result, arr[__i__]); + } + } + give back result; + goodbye "Filter complete"; +} + +to reduce(arr: [a], initial: b, f: (b, a) -> b) -> b { + hello "Reducing collection"; + remember acc = initial; + repeat len(arr) times { + acc = f(acc, arr[__i__]); + } + give back acc; + goodbye "Reduce complete"; +} + +to reduceRight(arr: [a], initial: b, f: (b, a) -> b) -> b { + remember acc = initial; + remember i = len(arr) - 1; + repeat len(arr) times { + acc = f(acc, arr[i]); + i = i - 1; + } + give back acc; +} + +(* ===================================================================== *) +(* SEARCHING *) +(* ===================================================================== *) + +to find(arr: [a], predicate: (a) -> Bool) -> Result[a, String] { + repeat len(arr) times { + when predicate(arr[__i__]) { + give back Okay(arr[__i__]); + } + } + give back Oops("No element satisfying predicate found"); +} + +to findIndex(arr: [a], predicate: (a) -> Bool) -> Result[Int, String] { + repeat len(arr) times { + when predicate(arr[__i__]) { + give back Okay(__i__); + } + } + give back Oops("No element satisfying predicate found"); +} + +to all(arr: [a], predicate: (a) -> Bool) -> Bool { + repeat len(arr) times { + when not predicate(arr[__i__]) { + give back false; + } + } + give back true; +} + +to any(arr: [a], predicate: (a) -> Bool) -> Bool { + repeat len(arr) times { + when predicate(arr[__i__]) { + give back true; + } + } + give back false; +} + +to none(arr: [a], predicate: (a) -> Bool) -> Bool { + give back not any(arr, predicate); +} + +(* ===================================================================== *) +(* TRANSFORMATION *) +(* ===================================================================== *) + +to flatten(arr: [[a]]) -> [a] { + remember result = []; + repeat len(arr) times { + remember inner = arr[__i__]; + repeat len(inner) times { + result = append(result, inner[__j__]); + } + } + give back result; +} + +to flatMap(arr: [a], f: (a) -> [b]) -> [b] { + give back flatten(map(arr, f)); +} + +to zip(arr1: [a], arr2: [b]) -> [[a, b]] { + remember minLen = min(len(arr1), len(arr2)); + remember result = []; + repeat minLen times { + result = append(result, [arr1[__i__], arr2[__i__]]); + } + give back result; +} + +to unzip(pairs: [[a, b]]) -> [[a], [b]] { + remember firsts = []; + remember seconds = []; + repeat len(pairs) times { + firsts = append(firsts, pairs[__i__][0]); + seconds = append(seconds, pairs[__i__][1]); + } + give back [firsts, seconds]; +} + +to partition(arr: [a], predicate: (a) -> Bool) -> [[a], [a]] { + remember passing = []; + remember failing = []; + repeat len(arr) times { + when predicate(arr[__i__]) { + passing = append(passing, arr[__i__]); + } otherwise { + failing = append(failing, arr[__i__]); + } + } + give back [passing, failing]; +} + +to groupBy(arr: [a], keyFn: (a) -> String) -> Record { + (* Returns a record/map of key -> [values] *) + remember groups = {}; + repeat len(arr) times { + remember key = keyFn(arr[__i__]); + remember existing = get(groups, key); + decide based on existing { + Okay(list) -> { + groups = set(groups, key, append(list, arr[__i__])); + } + Oops(_) -> { + groups = set(groups, key, [arr[__i__]]); + } + } + } + give back groups; +} + +(* ===================================================================== *) +(* SLICING *) +(* ===================================================================== *) + +to take(arr: [a], n: Int) -> [a] { + remember count = min(n, len(arr)); + remember result = []; + repeat count times { + result = append(result, arr[__i__]); + } + give back result; +} + +to drop(arr: [a], n: Int) -> [a] { + remember result = []; + remember start = min(n, len(arr)); + remember i = start; + repeat len(arr) - start times { + result = append(result, arr[i]); + i = i + 1; + } + give back result; +} + +to takeWhile(arr: [a], predicate: (a) -> Bool) -> [a] { + remember result = []; + repeat len(arr) times { + when not predicate(arr[__i__]) { + give back result; + } + result = append(result, arr[__i__]); + } + give back result; +} + +to dropWhile(arr: [a], predicate: (a) -> Bool) -> [a] { + remember started = false; + remember result = []; + repeat len(arr) times { + when started or not predicate(arr[__i__]) { + started = true; + result = append(result, arr[__i__]); + } + } + give back result; +} + +to slice(arr: [a], start: Int, end: Int) -> [a] { + remember result = []; + remember i = max(0, start); + remember stop = min(end, len(arr)); + repeat stop - i times { + result = append(result, arr[i]); + i = i + 1; + } + give back result; +} + +(* ===================================================================== *) +(* COUNTING & STATISTICS *) +(* ===================================================================== *) + +to count(arr: [a], predicate: (a) -> Bool) -> Int { + remember total = 0; + repeat len(arr) times { + when predicate(arr[__i__]) { + total = total + 1; + } + } + give back total; +} + +to sum(arr: [Int]) -> Int { + give back reduce(arr, 0, to (acc: Int, x: Int) -> Int { give back acc + x; }); +} + +to sumFloat(arr: [Float]) -> Float { + give back reduce(arr, 0.0, to (acc: Float, x: Float) -> Float { give back acc + x; }); +} + +to product(arr: [Int]) -> Int { + give back reduce(arr, 1, to (acc: Int, x: Int) -> Int { give back acc * x; }); +} + +to average(arr: [Float]) -> Result[Float, String] { + when len(arr) == 0 { + give back Oops("Cannot average empty array"); + } + give back Okay(sumFloat(arr) / len(arr)); +} + +(* ===================================================================== *) +(* SORTING (Simple insertion sort for demonstration) *) +(* ===================================================================== *) + +to sortInts(arr: [Int]) -> [Int] { + remember result = arr; + remember n = len(result); + repeat n times { + remember j = __i__; + repeat __i__ times { + when result[j] < result[j - 1] { + remember temp = result[j]; + result[j] = result[j - 1]; + result[j - 1] = temp; + } + j = j - 1; + } + } + give back result; +} + +(* ===================================================================== *) +(* UNIQUE & DISTINCT *) +(* ===================================================================== *) + +to unique(arr: [a]) -> [a] { + remember result = []; + repeat len(arr) times { + when not contains(result, arr[__i__]) { + result = append(result, arr[__i__]); + } + } + give back result; +} + +to distinctBy(arr: [a], keyFn: (a) -> b) -> [a] { + remember seen = []; + remember result = []; + repeat len(arr) times { + remember key = keyFn(arr[__i__]); + when not contains(seen, key) { + seen = append(seen, key); + result = append(result, arr[__i__]); + } + } + give back result; +} + +(* ===================================================================== *) +(* GRATITUDE *) +(* ===================================================================== *) + +thanks to { + "Higher-Order Functions" → "Enabling powerful data transformations"; + "Functional Programming" → "Making code composable and reusable"; +} diff --git a/lib/common/prelude.woke b/lib/common/prelude.woke new file mode 100644 index 0000000..e8d17b7 --- /dev/null +++ b/lib/common/prelude.woke @@ -0,0 +1,298 @@ +(* WokeLang Common Library - Prelude *) +(* Core utilities shared across all WokeLang projects *) + +(* ===================================================================== *) +(* IDENTITY & BASIC UTILITIES *) +(* ===================================================================== *) + +to identity(x: a) -> a { + hello "Identity function"; + give back x; + goodbye "Identity complete"; +} + +to constant(x: a, y: b) -> a { + give back x; +} + +to flip(f: (a, b) -> c, x: b, y: a) -> c { + give back f(y, x); +} + +(* ===================================================================== *) +(* BOOLEAN OPERATIONS *) +(* ===================================================================== *) + +to negate(b: Bool) -> Bool { + give back not b; +} + +to allTrue(values: [Bool]) -> Bool { + remember result = true; + repeat len(values) times { + when not values[__i__] { + result = false; + } + } + give back result; +} + +to anyTrue(values: [Bool]) -> Bool { + remember result = false; + repeat len(values) times { + when values[__i__] { + result = true; + } + } + give back result; +} + +(* ===================================================================== *) +(* NUMERIC UTILITIES *) +(* ===================================================================== *) + +to clamp(value: Int, minVal: Int, maxVal: Int) -> Int { + when value < minVal { + give back minVal; + } + when value > maxVal { + give back maxVal; + } + give back value; +} + +to inRange(value: Int, minVal: Int, maxVal: Int) -> Bool { + give back value >= minVal and value <= maxVal; +} + +to sign(n: Int) -> Int { + when n > 0 { + give back 1; + } + when n < 0 { + give back -1; + } + give back 0; +} + +to isEven(n: Int) -> Bool { + give back n % 2 == 0; +} + +to isOdd(n: Int) -> Bool { + give back n % 2 != 0; +} + +to gcd(a: Int, b: Int) -> Int { + when b == 0 { + give back a; + } + give back gcd(b, a % b); +} + +to lcm(a: Int, b: Int) -> Int { + give back (a * b) / gcd(a, b); +} + +(* ===================================================================== *) +(* STRING UTILITIES *) +(* ===================================================================== *) + +to isEmpty(s: String) -> Bool { + give back len(s) == 0; +} + +to isNotEmpty(s: String) -> Bool { + give back len(s) > 0; +} + +to repeat(s: String, n: Int) -> String { + remember result = ""; + repeat n times { + result = result + s; + } + give back result; +} + +to startsWith(s: String, prefix: String) -> Bool { + when len(prefix) > len(s) { + give back false; + } + remember i = 0; + repeat len(prefix) times { + when s[i] != prefix[i] { + give back false; + } + i = i + 1; + } + give back true; +} + +to endsWith(s: String, suffix: String) -> Bool { + when len(suffix) > len(s) { + give back false; + } + remember offset = len(s) - len(suffix); + remember i = 0; + repeat len(suffix) times { + when s[offset + i] != suffix[i] { + give back false; + } + i = i + 1; + } + give back true; +} + +(* ===================================================================== *) +(* ARRAY UTILITIES *) +(* ===================================================================== *) + +to head(arr: [a]) -> Result[a, String] { + when len(arr) == 0 { + give back Oops("Cannot get head of empty array"); + } + give back Okay(arr[0]); +} + +to tail(arr: [a]) -> [a] { + remember result = []; + remember i = 1; + repeat len(arr) - 1 times { + result = append(result, arr[i]); + i = i + 1; + } + give back result; +} + +to last(arr: [a]) -> Result[a, String] { + when len(arr) == 0 { + give back Oops("Cannot get last of empty array"); + } + give back Okay(arr[len(arr) - 1]); +} + +to init(arr: [a]) -> [a] { + remember result = []; + repeat len(arr) - 1 times { + result = append(result, arr[__i__]); + } + give back result; +} + +to reverse(arr: [a]) -> [a] { + remember result = []; + remember i = len(arr) - 1; + repeat len(arr) times { + result = append(result, arr[i]); + i = i - 1; + } + give back result; +} + +to contains(arr: [a], item: a) -> Bool { + repeat len(arr) times { + when arr[__i__] == item { + give back true; + } + } + give back false; +} + +to indexOf(arr: [a], item: a) -> Result[Int, String] { + repeat len(arr) times { + when arr[__i__] == item { + give back Okay(__i__); + } + } + give back Oops("Item not found in array"); +} + +(* ===================================================================== *) +(* RESULT UTILITIES *) +(* ===================================================================== *) + +to isOkay(result: Result[a, e]) -> Bool { + decide based on result { + Okay(_) -> { give back true; } + Oops(_) -> { give back false; } + } +} + +to isOops(result: Result[a, e]) -> Bool { + give back not isOkay(result); +} + +to getOrDefault(result: Result[a, e], default: a) -> a { + decide based on result { + Okay(value) -> { give back value; } + Oops(_) -> { give back default; } + } +} + +to mapResult(result: Result[a, e], f: (a) -> b) -> Result[b, e] { + decide based on result { + Okay(value) -> { give back Okay(f(value)); } + Oops(err) -> { give back Oops(err); } + } +} + +(* ===================================================================== *) +(* COMPARISON UTILITIES *) +(* ===================================================================== *) + +to min(a: Int, b: Int) -> Int { + when a < b { + give back a; + } + give back b; +} + +to max(a: Int, b: Int) -> Int { + when a > b { + give back a; + } + give back b; +} + +to minFloat(a: Float, b: Float) -> Float { + when a < b { + give back a; + } + give back b; +} + +to maxFloat(a: Float, b: Float) -> Float { + when a > b { + give back a; + } + give back b; +} + +(* ===================================================================== *) +(* DEBUGGING & INTROSPECTION *) +(* ===================================================================== *) + +@curious(level=1) +to debug(label: String, value: a) -> a { + hello "Debug output"; + print(label + ": " + toString(value)); + give back value; + goodbye "Debug complete"; +} + +to assert(condition: Bool, message: String) -> Result[Bool, String] { + when not condition { + give back Oops("Assertion failed: " + message); + } + give back Okay(true); +} + +(* ===================================================================== *) +(* GRATITUDE *) +(* ===================================================================== *) + +thanks to { + "Functional Programming" → "Inspiring pure, composable utilities"; + "Rust Standard Library" → "Reference for Result types"; + "Haskell Prelude" → "Foundation for functional patterns"; +} diff --git a/lib/wokelang/consent.woke b/lib/wokelang/consent.woke new file mode 100644 index 0000000..a91f87d --- /dev/null +++ b/lib/wokelang/consent.woke @@ -0,0 +1,241 @@ +(* WokeLang Specific Library - Consent Utilities *) +(* Human-centered consent and permission management *) + +#care on; + +(* ===================================================================== *) +(* CONSENT PATTERNS *) +(* ===================================================================== *) + +(* Consent scope types *) +type ConsentScope = Once | Session | Persistent; + +(* Consent request with metadata *) +type ConsentRequest = { + permission: String, + reason: String, + scope: ConsentScope, + requester: String +}; + +(* ===================================================================== *) +(* SAFE EXECUTION WITH CONSENT *) +(* ===================================================================== *) + +@mindful(importance=10) +to withConsent(permission: String, reason: String, action: () -> a) -> Result[a, String] { + hello "Requesting consent for: " + permission; + + only if okay permission { + remember result = action(); + give back Okay(result); + } + + give back Oops("Consent denied for: " + permission + " (Reason requested: " + reason + ")"); + + goodbye "Consent check complete"; +} + +@careful(level=2) +to requireAllConsents(permissions: [String], action: () -> a) -> Result[a, String] { + hello "Checking multiple permissions"; + + repeat len(permissions) times { + remember perm = permissions[__i__]; + only if okay perm { + (* Permission granted, continue *) + } otherwise { + give back Oops("Missing consent for: " + perm); + } + } + + give back Okay(action()); + + goodbye "Multi-permission check complete"; +} + +@thoughtful(consideration=5) +to withAnyConsent(permissions: [String], action: () -> a) -> Result[a, String] { + hello "Checking for any valid permission"; + + repeat len(permissions) times { + remember perm = permissions[__i__]; + only if okay perm { + give back Okay(action()); + } + } + + give back Oops("No valid consent found among: " + join(permissions, ", ")); + + goodbye "Any-permission check complete"; +} + +(* ===================================================================== *) +(* PERMISSION CATEGORIES *) +(* ===================================================================== *) + +(* File system permissions *) +const PERM_READ_FILE: String = "file.read"; +const PERM_WRITE_FILE: String = "file.write"; +const PERM_DELETE_FILE: String = "file.delete"; +const PERM_LIST_DIR: String = "directory.list"; + +(* Network permissions *) +const PERM_NETWORK_HTTP: String = "network.http"; +const PERM_NETWORK_HTTPS: String = "network.https"; +const PERM_NETWORK_SOCKET: String = "network.socket"; + +(* System permissions *) +const PERM_ENV_READ: String = "environment.read"; +const PERM_ENV_WRITE: String = "environment.write"; +const PERM_EXEC: String = "process.execute"; + +(* User data permissions *) +const PERM_CAMERA: String = "device.camera"; +const PERM_MICROPHONE: String = "device.microphone"; +const PERM_LOCATION: String = "user.location"; +const PERM_CONTACTS: String = "user.contacts"; + +(* ===================================================================== *) +(* SAFE FILE OPERATIONS *) +(* ===================================================================== *) + +@careful(level=3) +to safeReadFile(path: String) -> Result[String, String] { + give back withConsent(PERM_READ_FILE, "Reading file: " + path, to () -> String { + give back readFile(path)?; + }); +} + +@careful(level=3) +to safeWriteFile(path: String, content: String) -> Result[Bool, String] { + give back withConsent(PERM_WRITE_FILE, "Writing to file: " + path, to () -> Bool { + writeFile(path, content)?; + give back true; + }); +} + +@careful(level=5) +to safeDeleteFile(path: String) -> Result[Bool, String] { + give back withConsent(PERM_DELETE_FILE, "Deleting file: " + path, to () -> Bool { + delete(path)?; + give back true; + }); +} + +(* ===================================================================== *) +(* SAFE NETWORK OPERATIONS *) +(* ===================================================================== *) + +@thoughtful(consideration=3) +to safeHttpGet(url: String) -> Result[String, String] { + give back withConsent(PERM_NETWORK_HTTP, "HTTP GET request to: " + url, to () -> String { + give back httpGet(url)?; + }); +} + +@thoughtful(consideration=3) +to safeHttpPost(url: String, body: String) -> Result[String, String] { + give back withConsent(PERM_NETWORK_HTTP, "HTTP POST request to: " + url, to () -> String { + give back httpPost(url, body)?; + }); +} + +(* ===================================================================== *) +(* CONSENT LOGGING & AUDIT *) +(* ===================================================================== *) + +@observant(detail=5) +to logConsentRequest(request: ConsentRequest) -> Bool { + hello "Logging consent request"; + + remember logEntry = format( + "[CONSENT] Permission: {}, Reason: {}, Requester: {}", + [request.permission, request.reason, request.requester] + ); + + print(logEntry); + + give back true; + + goodbye "Consent logged"; +} + +to createAuditTrail(operations: [String]) -> String { + hello "Creating audit trail"; + + remember trail = "=== Consent Audit Trail ===\n"; + remember timestamp = format("{}", [now()]); + + trail = trail + "Generated: " + timestamp + "\n"; + trail = trail + "Operations:\n"; + + repeat len(operations) times { + trail = trail + " - " + operations[__i__] + "\n"; + } + + trail = trail + "=========================\n"; + + give back trail; + + goodbye "Audit trail created"; +} + +(* ===================================================================== *) +(* PRIVACY-PRESERVING PATTERNS *) +(* ===================================================================== *) + +@mindful(importance=10) +to withPrivacy(sensitiveData: a, transform: (a) -> b) -> b { + (* Process sensitive data and return transformed (non-sensitive) result *) + hello "Processing sensitive data privately"; + + remember result = transform(sensitiveData); + + (* Sensitive data goes out of scope here *) + + give back result; + + goodbye "Private processing complete"; +} + +@careful(level=5) +to redact(text: String, patterns: [String]) -> String { + hello "Redacting sensitive patterns"; + + remember result = text; + + repeat len(patterns) times { + result = replace(result, patterns[__i__], "[REDACTED]"); + } + + give back result; + + goodbye "Redaction complete"; +} + +to anonymize(data: Record) -> Record { + (* Remove personally identifiable information *) + hello "Anonymizing data"; + + remember piiFields = ["name", "email", "phone", "address", "ssn", "dob"]; + remember result = data; + + repeat len(piiFields) times { + result = remove(result, piiFields[__i__]); + } + + give back result; + + goodbye "Anonymization complete"; +} + +(* ===================================================================== *) +(* GRATITUDE *) +(* ===================================================================== *) + +thanks to { + "Privacy By Design" → "Inspiring consent-first architecture"; + "GDPR" → "Establishing data protection principles"; + "Users" → "For trusting us with their data"; +} diff --git a/lib/wokelang/emotes.woke b/lib/wokelang/emotes.woke new file mode 100644 index 0000000..5c81207 --- /dev/null +++ b/lib/wokelang/emotes.woke @@ -0,0 +1,241 @@ +(* WokeLang Specific Library - Emote Utilities *) +(* Emotional annotations and sentiment-aware programming *) + +#care on; + +(* ===================================================================== *) +(* EMOTE DEFINITIONS *) +(* ===================================================================== *) + +(* Emote intensity levels *) +type EmoteIntensity = Low | Medium | High | Critical; + +(* Core emote types for code annotation *) +type Emote = + Happy(Int) (* Joy level 1-10 *) + | Sad(Int) (* Sadness level 1-10 *) + | Curious(Int) (* Curiosity/investigation level *) + | Careful(Int) (* Caution level for risky operations *) + | Mindful(Int) (* Importance/attention level *) + | Thoughtful(Int) (* Consideration level *) + | Frustrated(Int) (* For legacy code or complex bugs *) + | Proud(Int) (* For achievements and completions *) + | Anxious(Int) (* For uncertain or experimental code *) + | Hopeful(Int) ; (* For optimistic outcomes *) + +(* ===================================================================== *) +(* EMOTE CONTEXT *) +(* ===================================================================== *) + +type EmoteContext = { + emote: Emote, + reason: String, + timestamp: Int, + location: String +}; + +to createEmoteContext(emote: Emote, reason: String, location: String) -> EmoteContext { + give back { + emote: emote, + reason: reason, + timestamp: now(), + location: location + }; +} + +(* ===================================================================== *) +(* EMOTE-BASED LOGGING *) +(* ===================================================================== *) + +@mindful(importance=5) +to logWithEmote(emote: Emote, message: String) -> Bool { + hello "Logging with emote"; + + remember prefix = decide based on emote { + Happy(level) -> { give back "😊 [HAPPY:" + toString(level) + "]"; } + Sad(level) -> { give back "😢 [SAD:" + toString(level) + "]"; } + Curious(level) -> { give back "🤔 [CURIOUS:" + toString(level) + "]"; } + Careful(level) -> { give back "⚠️ [CAREFUL:" + toString(level) + "]"; } + Mindful(level) -> { give back "🧘 [MINDFUL:" + toString(level) + "]"; } + Thoughtful(level) -> { give back "💭 [THOUGHTFUL:" + toString(level) + "]"; } + Frustrated(level) -> { give back "😤 [FRUSTRATED:" + toString(level) + "]"; } + Proud(level) -> { give back "🎉 [PROUD:" + toString(level) + "]"; } + Anxious(level) -> { give back "😰 [ANXIOUS:" + toString(level) + "]"; } + Hopeful(level) -> { give back "🌟 [HOPEFUL:" + toString(level) + "]"; } + }; + + print(prefix + " " + message); + give back true; + + goodbye "Emote logged"; +} + +(* ===================================================================== *) +(* SENTIMENT-AWARE EXECUTION *) +(* ===================================================================== *) + +@thoughtful(consideration=3) +to withCaution(level: Int, action: () -> Result[a, String]) -> Result[a, String] { + hello "Executing with caution level " + toString(level); + + when level >= 8 { + logWithEmote(Careful(level), "High caution operation starting"); + } + + remember result = action(); + + decide based on result { + Okay(value) -> { + when level >= 5 { + logWithEmote(Proud(level), "Cautious operation succeeded"); + } + } + Oops(err) -> { + logWithEmote(Sad(level), "Cautious operation failed: " + err); + } + } + + give back result; + + goodbye "Cautious execution complete"; +} + +@curious(level=5) +to withInvestigation(description: String, action: () -> a) -> a { + hello "Investigating: " + description; + + logWithEmote(Curious(5), "Starting investigation: " + description); + + remember startTime = now(); + remember result = action(); + remember duration = elapsed(startTime); + + logWithEmote(Thoughtful(5), "Investigation complete in " + toString(duration) + "ms"); + + give back result; + + goodbye "Investigation complete"; +} + +(* ===================================================================== *) +(* EMOTIONAL STATE TRACKING *) +(* ===================================================================== *) + +type EmotionalState = { + current: Emote, + history: [EmoteContext], + overallMood: Int (* -10 to +10, negative is stressed, positive is calm *) +}; + +to createEmotionalState() -> EmotionalState { + give back { + current: Hopeful(5), + history: [], + overallMood: 5 + }; +} + +to updateEmotionalState(state: EmotionalState, newEmote: Emote, reason: String) -> EmotionalState { + remember context = createEmoteContext(newEmote, reason, "runtime"); + + remember moodDelta = decide based on newEmote { + Happy(level) -> { give back level; } + Proud(level) -> { give back level; } + Hopeful(level) -> { give back level / 2; } + Curious(level) -> { give back 0; } + Thoughtful(level) -> { give back 0; } + Mindful(level) -> { give back 1; } + Careful(level) -> { give back 0 - (level / 2); } + Anxious(level) -> { give back 0 - level; } + Frustrated(level) -> { give back 0 - level; } + Sad(level) -> { give back 0 - level; } + }; + + give back { + current: newEmote, + history: append(state.history, context), + overallMood: clamp(state.overallMood + moodDelta, -10, 10) + }; +} + +to getEmotionalSummary(state: EmotionalState) -> String { + remember moodDesc = when state.overallMood > 5 { + give back "very positive 😊"; + } otherwise when state.overallMood > 0 { + give back "positive 🙂"; + } otherwise when state.overallMood == 0 { + give back "neutral 😐"; + } otherwise when state.overallMood > -5 { + give back "slightly stressed 😕"; + } otherwise { + give back "stressed 😰"; + }; + + give back "Emotional state: " + moodDesc + " (mood: " + toString(state.overallMood) + "/10)"; +} + +(* ===================================================================== *) +(* CODE HEALTH INDICATORS *) +(* ===================================================================== *) + +type CodeHealth = { + complexity: Int, (* Cyclomatic complexity estimate *) + riskyOps: Int, (* Number of dangerous operations *) + testCoverage: Int, (* Estimated test coverage % *) + mood: Emote +}; + +@mindful(importance=8) +to assessCodeHealth(complexity: Int, riskyOps: Int, testCoverage: Int) -> CodeHealth { + remember mood = when complexity < 5 and riskyOps == 0 and testCoverage > 80 { + give back Happy(8); + } otherwise when complexity < 10 and riskyOps < 3 and testCoverage > 60 { + give back Hopeful(6); + } otherwise when complexity < 20 and testCoverage > 40 { + give back Careful(5); + } otherwise { + give back Anxious(7); + }; + + give back { + complexity: complexity, + riskyOps: riskyOps, + testCoverage: testCoverage, + mood: mood + }; +} + +to reportCodeHealth(health: CodeHealth) -> String { + remember report = "=== Code Health Report ===\n"; + report = report + "Complexity: " + toString(health.complexity) + "\n"; + report = report + "Risky Operations: " + toString(health.riskyOps) + "\n"; + report = report + "Test Coverage: " + toString(health.testCoverage) + "%\n"; + report = report + "Overall Mood: " + emoteToString(health.mood) + "\n"; + report = report + "========================\n"; + give back report; +} + +to emoteToString(emote: Emote) -> String { + give back decide based on emote { + Happy(level) -> { give back "Happy (" + toString(level) + ")"; } + Sad(level) -> { give back "Sad (" + toString(level) + ")"; } + Curious(level) -> { give back "Curious (" + toString(level) + ")"; } + Careful(level) -> { give back "Careful (" + toString(level) + ")"; } + Mindful(level) -> { give back "Mindful (" + toString(level) + ")"; } + Thoughtful(level) -> { give back "Thoughtful (" + toString(level) + ")"; } + Frustrated(level) -> { give back "Frustrated (" + toString(level) + ")"; } + Proud(level) -> { give back "Proud (" + toString(level) + ")"; } + Anxious(level) -> { give back "Anxious (" + toString(level) + ")"; } + Hopeful(level) -> { give back "Hopeful (" + toString(level) + ")"; } + }; +} + +(* ===================================================================== *) +(* GRATITUDE *) +(* ===================================================================== *) + +thanks to { + "Emotional Intelligence" → "Recognizing that code is written by humans"; + "Mindfulness" → "Bringing awareness to programming"; + "Empathy" → "Considering the developer experience"; +} diff --git a/lib/wokelang/gratitude.woke b/lib/wokelang/gratitude.woke new file mode 100644 index 0000000..425be12 --- /dev/null +++ b/lib/wokelang/gratitude.woke @@ -0,0 +1,246 @@ +(* WokeLang Specific Library - Gratitude Utilities *) +(* Acknowledgment patterns and attribution management *) + +#care on; + +(* ===================================================================== *) +(* GRATITUDE TYPES *) +(* ===================================================================== *) + +type Acknowledgment = { + recipient: String, + reason: String, + category: AckCategory, + date: Int +}; + +type AckCategory = + Inspiration (* Ideas and concepts *) + | Implementation (* Code and technical help *) + | Design (* Architecture and patterns *) + | Testing (* Quality assurance *) + | Documentation (* Docs and examples *) + | Community (* Community contributions *) + | Mentorship (* Teaching and guidance *) + | Funding (* Financial support *) + | Infrastructure ; (* Hosting, CI/CD, etc. *) + +type GratitudeRegistry = { + acknowledgments: [Acknowledgment], + projectName: String, + maintainers: [String] +}; + +(* ===================================================================== *) +(* GRATITUDE REGISTRY MANAGEMENT *) +(* ===================================================================== *) + +to createGratitudeRegistry(projectName: String, maintainers: [String]) -> GratitudeRegistry { + give back { + acknowledgments: [], + projectName: projectName, + maintainers: maintainers + }; +} + +@mindful(importance=8) +to acknowledge(registry: GratitudeRegistry, recipient: String, reason: String, category: AckCategory) -> GratitudeRegistry { + hello "Recording acknowledgment for: " + recipient; + + remember ack = { + recipient: recipient, + reason: reason, + category: category, + date: now() + }; + + give back { + acknowledgments: append(registry.acknowledgments, ack), + projectName: registry.projectName, + maintainers: registry.maintainers + }; + + goodbye "Acknowledgment recorded"; +} + +to getAcknowledgmentsByCategory(registry: GratitudeRegistry, category: AckCategory) -> [Acknowledgment] { + give back filter(registry.acknowledgments, to (ack: Acknowledgment) -> Bool { + give back ack.category == category; + }); +} + +(* ===================================================================== *) +(* ATTRIBUTION GENERATION *) +(* ===================================================================== *) + +@thoughtful(consideration=5) +to generateCredits(registry: GratitudeRegistry) -> String { + hello "Generating credits"; + + remember credits = "=== " + registry.projectName + " Credits ===\n\n"; + + (* Maintainers *) + credits = credits + "Maintainers:\n"; + repeat len(registry.maintainers) times { + credits = credits + " • " + registry.maintainers[__i__] + "\n"; + } + credits = credits + "\n"; + + (* Group by category *) + remember categories = [ + [Inspiration, "Inspiration"], + [Implementation, "Implementation"], + [Design, "Design"], + [Testing, "Testing"], + [Documentation, "Documentation"], + [Community, "Community"], + [Mentorship, "Mentorship"], + [Funding, "Funding"], + [Infrastructure, "Infrastructure"] + ]; + + repeat len(categories) times { + remember cat = categories[__i__][0]; + remember catName = categories[__i__][1]; + remember acks = getAcknowledgmentsByCategory(registry, cat); + + when len(acks) > 0 { + credits = credits + catName + ":\n"; + repeat len(acks) times { + remember ack = acks[__j__]; + credits = credits + " • " + ack.recipient + " - " + ack.reason + "\n"; + } + credits = credits + "\n"; + } + } + + credits = credits + "================================\n"; + + give back credits; + + goodbye "Credits generated"; +} + +to generateThankYouNote(recipient: String, reason: String) -> String { + give back "Dear " + recipient + ",\n\n" + + "Thank you for your contribution to this project.\n" + + "Your " + reason + " has made a meaningful difference.\n\n" + + "With gratitude,\n" + + "The WokeLang Community"; +} + +(* ===================================================================== *) +(* DEPENDENCY ACKNOWLEDGMENT *) +(* ===================================================================== *) + +type DependencyAck = { + name: String, + version: String, + license: String, + authors: [String], + purpose: String +}; + +@mindful(importance=7) +to acknowledgeDependency(name: String, version: String, license: String, authors: [String], purpose: String) -> DependencyAck { + give back { + name: name, + version: version, + license: license, + authors: authors, + purpose: purpose + }; +} + +to generateLicenseNotices(deps: [DependencyAck]) -> String { + hello "Generating license notices"; + + remember notice = "=== Third-Party License Notices ===\n\n"; + + repeat len(deps) times { + remember dep = deps[__i__]; + notice = notice + dep.name + " v" + dep.version + "\n"; + notice = notice + " License: " + dep.license + "\n"; + notice = notice + " Authors: " + join(dep.authors, ", ") + "\n"; + notice = notice + " Used for: " + dep.purpose + "\n\n"; + } + + notice = notice + "===================================\n"; + + give back notice; + + goodbye "License notices generated"; +} + +(* ===================================================================== *) +(* GRATITUDE EXPRESSIONS *) +(* ===================================================================== *) + +to expressGratitude(level: Int) -> String { + give back decide based on level { + 1 -> { give back "Thanks"; } + 2 -> { give back "Thank you"; } + 3 -> { give back "Many thanks"; } + 4 -> { give back "Thank you so much"; } + 5 -> { give back "I deeply appreciate this"; } + 6 -> { give back "I am truly grateful"; } + 7 -> { give back "Words cannot express my gratitude"; } + 8 -> { give back "I am forever indebted"; } + 9 -> { give back "This means the world to me"; } + 10 -> { give back "I am overwhelmed with gratitude"; } + _ -> { give back "Thank you"; } + }; +} + +to randomGratitudeQuote() -> String { + remember quotes = [ + "Gratitude turns what we have into enough. - Anonymous", + "The roots of all goodness lie in the soil of appreciation. - Dalai Lama", + "Feeling gratitude and not expressing it is like wrapping a present and not giving it. - William Arthur Ward", + "Gratitude is the healthiest of all human emotions. - Zig Ziglar", + "When we give cheerfully and accept gratefully, everyone is blessed. - Maya Angelou" + ]; + + remember index = random() % len(quotes); + give back quotes[index]; +} + +(* ===================================================================== *) +(* CONTRIBUTOR RECOGNITION *) +(* ===================================================================== *) + +type Contributor = { + name: String, + github: String, + contributions: [String], + firstContribution: Int, + totalContributions: Int +}; + +to recognizeContributor(contributor: Contributor) -> String { + remember recognition = "🌟 Contributor Spotlight: " + contributor.name + " 🌟\n\n"; + recognition = recognition + "GitHub: @" + contributor.github + "\n"; + recognition = recognition + "Total Contributions: " + toString(contributor.totalContributions) + "\n"; + recognition = recognition + "Areas:\n"; + + repeat len(contributor.contributions) times { + recognition = recognition + " • " + contributor.contributions[__i__] + "\n"; + } + + recognition = recognition + "\n" + expressGratitude(min(contributor.totalContributions, 10)) + "!\n"; + + give back recognition; +} + +(* ===================================================================== *) +(* GRATITUDE BLOCK HELPERS *) +(* ===================================================================== *) + +(* Standard gratitude block for new WokeLang projects *) +thanks to { + "Open Source Community" → "Building on shared knowledge"; + "Language Designers" → "Inspiring human-centered syntax"; + "Early Adopters" → "Testing and providing feedback"; + "Documentation Writers" → "Making learning accessible"; + "Bug Reporters" → "Helping improve quality"; +} diff --git a/src/vm/bytecode.rs b/src/vm/bytecode.rs new file mode 100644 index 0000000..1dcbd8a --- /dev/null +++ b/src/vm/bytecode.rs @@ -0,0 +1,242 @@ +//! WokeLang Bytecode Instruction Set +//! +//! A stack-based bytecode format for efficient execution. + +use crate::interpreter::Value; +use std::collections::HashMap; + +/// Bytecode instructions for the WokeLang VM +#[derive(Debug, Clone, PartialEq)] +pub enum OpCode { + // Stack operations + /// Push a constant onto the stack + Const(usize), + /// Pop the top value from the stack + Pop, + /// Duplicate the top value on the stack + Dup, + /// Swap the top two values on the stack + Swap, + + // Local variables + /// Load a local variable onto the stack + LoadLocal(usize), + /// Store the top of stack into a local variable + StoreLocal(usize), + /// Load a global variable + LoadGlobal(String), + /// Store into a global variable + StoreGlobal(String), + + // Arithmetic operations (pop operands, push result) + Add, + Sub, + Mul, + Div, + Mod, + Neg, + + // Comparison operations + Eq, + Ne, + Lt, + Le, + Gt, + Ge, + + // Logical operations + And, + Or, + Not, + + // String operations + Concat, + + // Control flow + /// Unconditional jump to instruction index + Jump(usize), + /// Jump if top of stack is false + JumpIfFalse(usize), + /// Jump if top of stack is true + JumpIfTrue(usize), + + // Functions + /// Call a function with N arguments + Call(usize), + /// Return from function + Return, + /// Create a closure + MakeClosure(usize), + + // Array/Record operations + /// Create an array from N elements on stack + MakeArray(usize), + /// Create a record from N key-value pairs + MakeRecord(usize), + /// Index into array or record + Index, + /// Get length of array/string + Len, + + // Result types + /// Wrap top of stack in Okay + MakeOkay, + /// Wrap top of stack in Oops + MakeOops, + /// Unwrap Okay or propagate Oops + TryUnwrap, + /// Check if value is Okay + IsOkay, + + // Built-in functions + /// Print the top of stack + Print, + /// Convert to string + ToString, + + // No operation (for padding/optimization) + Nop, + /// Halt execution + Halt, +} + +/// A compiled function +#[derive(Debug, Clone)] +pub struct CompiledFunction { + /// Function name (for debugging) + pub name: String, + /// Number of parameters + pub arity: usize, + /// Number of local variables (including parameters) + pub locals: usize, + /// Bytecode instructions + pub code: Vec, + /// Constant pool for this function + pub constants: Vec, +} + +impl CompiledFunction { + pub fn new(name: String, arity: usize) -> Self { + Self { + name, + arity, + locals: arity, + code: Vec::new(), + constants: Vec::new(), + } + } + + /// Add a constant and return its index + pub fn add_constant(&mut self, value: Value) -> usize { + // Check if constant already exists + for (i, c) in self.constants.iter().enumerate() { + if c == &value { + return i; + } + } + let idx = self.constants.len(); + self.constants.push(value); + idx + } + + /// Emit an instruction and return its index + pub fn emit(&mut self, op: OpCode) -> usize { + let idx = self.code.len(); + self.code.push(op); + idx + } + + /// Patch a jump instruction with the correct target + pub fn patch_jump(&mut self, jump_idx: usize, target: usize) { + match &mut self.code[jump_idx] { + OpCode::Jump(ref mut t) => *t = target, + OpCode::JumpIfFalse(ref mut t) => *t = target, + OpCode::JumpIfTrue(ref mut t) => *t = target, + _ => panic!("Tried to patch non-jump instruction"), + } + } + + /// Get current instruction index (for jump targets) + pub fn current_offset(&self) -> usize { + self.code.len() + } +} + +/// A compiled program +#[derive(Debug, Clone)] +pub struct CompiledProgram { + /// All compiled functions + pub functions: Vec, + /// Index of the main/entry function + pub entry: Option, + /// Global variables (name -> value) + pub globals: HashMap, +} + +impl CompiledProgram { + pub fn new() -> Self { + Self { + functions: Vec::new(), + entry: None, + globals: HashMap::new(), + } + } + + /// Add a function and return its index + pub fn add_function(&mut self, func: CompiledFunction) -> usize { + let idx = self.functions.len(); + if func.name == "main" { + self.entry = Some(idx); + } + self.functions.push(func); + idx + } + + /// Get a function by index + pub fn get_function(&self, idx: usize) -> Option<&CompiledFunction> { + self.functions.get(idx) + } +} + +impl Default for CompiledProgram { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_compiled_function() { + let mut func = CompiledFunction::new("test".to_string(), 2); + + let c1 = func.add_constant(Value::Int(42)); + let c2 = func.add_constant(Value::String("hello".to_string())); + let c3 = func.add_constant(Value::Int(42)); // Should reuse c1 + + assert_eq!(c1, 0); + assert_eq!(c2, 1); + assert_eq!(c3, 0); // Reused + + func.emit(OpCode::Const(c1)); + func.emit(OpCode::Const(c2)); + func.emit(OpCode::Add); + + assert_eq!(func.code.len(), 3); + } + + #[test] + fn test_jump_patching() { + let mut func = CompiledFunction::new("test".to_string(), 0); + + let jump_idx = func.emit(OpCode::JumpIfFalse(0)); // Placeholder + func.emit(OpCode::Const(0)); + func.emit(OpCode::Pop); + let target = func.current_offset(); + func.patch_jump(jump_idx, target); + + assert_eq!(func.code[jump_idx], OpCode::JumpIfFalse(3)); + } +} diff --git a/src/vm/compiler.rs b/src/vm/compiler.rs new file mode 100644 index 0000000..66ecb78 --- /dev/null +++ b/src/vm/compiler.rs @@ -0,0 +1,765 @@ +//! WokeLang Bytecode Compiler +//! +//! Compiles AST to bytecode for the VM. + +use crate::ast::{ + BinaryOp, Expr, FunctionDef, Literal, Loop, Pattern, Program, Spanned, + Statement, TopLevelItem, UnaryOp, +}; +use crate::interpreter::Value; +use super::bytecode::{CompiledFunction, CompiledProgram, OpCode}; +use std::collections::HashMap; + +/// Bytecode compiler +pub struct BytecodeCompiler { + /// The compiled program being built + program: CompiledProgram, + /// Current function being compiled + current_function: Option, + /// Local variable name to slot mapping + locals: HashMap, + /// Function name to index mapping + function_indices: HashMap, + /// Loop break jump targets (for nested loops) + break_targets: Vec>, + /// Loop continue targets + continue_targets: Vec, +} + +impl BytecodeCompiler { + pub fn new() -> Self { + Self { + program: CompiledProgram::new(), + current_function: None, + locals: HashMap::new(), + function_indices: HashMap::new(), + break_targets: Vec::new(), + continue_targets: Vec::new(), + } + } + + /// Compile a program to bytecode + pub fn compile(&mut self, program: &Program) -> Result { + // First pass: register all function names + for item in &program.items { + if let TopLevelItem::Function(func) = item { + let idx = self.program.functions.len() + self.function_indices.len(); + self.function_indices.insert(func.name.clone(), idx); + } + } + + // Second pass: compile all items + for item in &program.items { + self.compile_item(item)?; + } + + Ok(self.program.clone()) + } + + fn compile_item(&mut self, item: &TopLevelItem) -> Result<(), CompileError> { + match item { + TopLevelItem::Function(func) => { + self.compile_function(func)?; + } + TopLevelItem::WorkerDef(worker) => { + // Compile worker as a function + let mut compiled = CompiledFunction::new(worker.name.clone(), 0); + self.locals.clear(); + compiled.locals = 0; + self.current_function = Some(compiled); + + for stmt in &worker.body { + self.compile_statement(stmt)?; + } + + // Add implicit return + if let Some(ref mut func) = self.current_function { + if func.code.is_empty() || !matches!(func.code.last(), Some(OpCode::Return)) { + let unit_idx = func.add_constant(Value::Unit); + func.emit(OpCode::Const(unit_idx)); + func.emit(OpCode::Return); + } + } + + if let Some(func) = self.current_function.take() { + self.program.add_function(func); + } + } + TopLevelItem::ConsentBlock(consent) => { + // Create an anonymous function for consent block + let name = format!("__consent_{}__", consent.permission); + let mut compiled = CompiledFunction::new(name, 0); + self.locals.clear(); + self.current_function = Some(compiled); + + for stmt in &consent.body { + self.compile_statement(stmt)?; + } + + if let Some(ref mut func) = self.current_function { + let unit_idx = func.add_constant(Value::Unit); + func.emit(OpCode::Const(unit_idx)); + func.emit(OpCode::Return); + } + + if let Some(func) = self.current_function.take() { + self.program.add_function(func); + } + } + // Skip metadata items for bytecode + TopLevelItem::GratitudeDecl(_) => {} + TopLevelItem::SideQuestDef(_) => {} + TopLevelItem::SuperpowerDecl(_) => {} + TopLevelItem::ModuleImport(_) => {} + TopLevelItem::ModuleExport(_) => {} + TopLevelItem::Pragma(_) => {} + TopLevelItem::TypeDef(_) => {} + TopLevelItem::ConstDef(const_def) => { + // Handle const definitions at compile time if possible + // For now, store them as globals + let name = const_def.name.clone(); + if let Some(value) = self.try_eval_const(&const_def.value.node) { + self.program.globals.insert(name, value); + } + } + } + Ok(()) + } + + fn compile_function(&mut self, func: &FunctionDef) -> Result<(), CompileError> { + // Start a new function + let mut compiled = CompiledFunction::new(func.name.clone(), func.params.len()); + + // Set up locals for parameters + self.locals.clear(); + for (i, param) in func.params.iter().enumerate() { + self.locals.insert(param.name.clone(), i); + } + compiled.locals = func.params.len(); + + self.current_function = Some(compiled); + + // Compile function body + for stmt in &func.body { + self.compile_statement(stmt)?; + } + + // Add implicit return if needed + if let Some(ref mut func) = self.current_function { + if func.code.is_empty() || !matches!(func.code.last(), Some(OpCode::Return)) { + let unit_idx = func.add_constant(Value::Unit); + func.emit(OpCode::Const(unit_idx)); + func.emit(OpCode::Return); + } + } + + // Add function to program + if let Some(compiled_func) = self.current_function.take() { + self.program.add_function(compiled_func); + } + + Ok(()) + } + + fn compile_statement(&mut self, stmt: &Statement) -> Result<(), CompileError> { + match stmt { + Statement::VarDecl(decl) => { + // Compile the initializer + self.compile_expr(&decl.value)?; + + // Allocate local slot + let slot = self.allocate_local(&decl.name); + self.emit(OpCode::StoreLocal(slot)); + } + + Statement::Assignment(assign) => { + // Compile the value + self.compile_expr(&assign.value)?; + + // Store to variable + if let Some(&slot) = self.locals.get(&assign.target) { + self.emit(OpCode::StoreLocal(slot)); + } else { + self.emit(OpCode::StoreGlobal(assign.target.clone())); + } + } + + Statement::Return(ret) => { + self.compile_expr(&ret.value)?; + self.emit(OpCode::Return); + } + + Statement::Conditional(cond) => { + // Compile condition + self.compile_expr(&cond.condition)?; + + // Jump over then-branch if false + let jump_if_false = self.emit(OpCode::JumpIfFalse(0)); + + // Compile then-branch + for stmt in &cond.then_branch { + self.compile_statement(stmt)?; + } + + if let Some(else_branch) = &cond.else_branch { + // Jump over else-branch + let jump_over_else = self.emit(OpCode::Jump(0)); + + // Patch the conditional jump + let else_start = self.current_offset(); + self.patch_jump(jump_if_false, else_start); + + // Compile else-branch + for stmt in else_branch { + self.compile_statement(stmt)?; + } + + // Patch jump over else + let after_else = self.current_offset(); + self.patch_jump(jump_over_else, after_else); + } else { + // Patch the conditional jump + let after_if = self.current_offset(); + self.patch_jump(jump_if_false, after_if); + } + } + + Statement::Loop(loop_stmt) => { + self.compile_loop(loop_stmt)?; + } + + Statement::Decide(decide) => { + // Pattern matching - compile as a series of conditionals + self.compile_expr(&decide.scrutinee)?; + + // Store scrutinee in a temp variable + let scrutinee_slot = self.allocate_local("__scrutinee__"); + self.emit(OpCode::StoreLocal(scrutinee_slot)); + + let mut end_jumps = Vec::new(); + + for arm in &decide.arms { + // Load scrutinee for each arm + self.emit(OpCode::LoadLocal(scrutinee_slot)); + + // Compile pattern match + let skip_jump = self.compile_pattern(&arm.pattern)?; + + // Compile arm body + for stmt in &arm.body { + self.compile_statement(stmt)?; + } + + // Jump to end + let end_jump = self.emit(OpCode::Jump(0)); + end_jumps.push(end_jump); + + // Patch skip jump + let after_arm = self.current_offset(); + self.patch_jump(skip_jump, after_arm); + } + + // Patch all end jumps + let after_decide = self.current_offset(); + for jump in end_jumps { + self.patch_jump(jump, after_decide); + } + } + + Statement::Expression(expr) => { + self.compile_expr(expr)?; + self.emit(OpCode::Pop); + } + + Statement::AttemptBlock(attempt) => { + // try/catch style - compile body with error handling setup + for stmt in &attempt.body { + self.compile_statement(stmt)?; + } + // The reassurance is just metadata for now + } + + Statement::ConsentBlock(consent) => { + // Consent is checked at runtime + for stmt in &consent.body { + self.compile_statement(stmt)?; + } + } + + Statement::Complain(complain) => { + // Load error message + let msg_idx = self.add_constant(Value::String(complain.message.clone())); + self.emit(OpCode::Const(msg_idx)); + self.emit(OpCode::MakeOops); + self.emit(OpCode::Return); + } + + Statement::EmoteAnnotated(annotated) => { + // Compile the inner statement, emote is metadata + self.compile_statement(&annotated.statement)?; + } + + // Worker-related statements + Statement::WorkerSpawn(_) => { + // Worker spawning handled at runtime + } + Statement::SendMessage(send) => { + self.compile_expr(&send.value)?; + // Message sending handled at runtime + } + Statement::ReceiveMessage(_) => { + // Message receiving handled at runtime + } + Statement::AwaitWorker(_) => { + // Worker awaiting handled at runtime + } + Statement::CancelWorker(_) => { + // Worker cancellation handled at runtime + } + } + Ok(()) + } + + fn compile_loop(&mut self, loop_stmt: &Loop) -> Result<(), CompileError> { + // Compile the count expression + self.compile_expr(&loop_stmt.count)?; + + // Store count in a temporary local + let counter_slot = self.allocate_local("__counter__"); + self.emit(OpCode::StoreLocal(counter_slot)); + + // Push break targets + self.break_targets.push(Vec::new()); + + let loop_start = self.current_offset(); + self.continue_targets.push(loop_start); + + // Check if counter > 0 + self.emit(OpCode::LoadLocal(counter_slot)); + let zero_idx = self.add_constant(Value::Int(0)); + self.emit(OpCode::Const(zero_idx)); + self.emit(OpCode::Gt); + let exit_jump = self.emit(OpCode::JumpIfFalse(0)); + + // Compile body + for stmt in &loop_stmt.body { + self.compile_statement(stmt)?; + } + + // Decrement counter + self.emit(OpCode::LoadLocal(counter_slot)); + let one_idx = self.add_constant(Value::Int(1)); + self.emit(OpCode::Const(one_idx)); + self.emit(OpCode::Sub); + self.emit(OpCode::StoreLocal(counter_slot)); + + // Jump back + self.emit(OpCode::Jump(loop_start)); + + // Patch exit + let after_loop = self.current_offset(); + self.patch_jump(exit_jump, after_loop); + + // Patch breaks + if let Some(breaks) = self.break_targets.pop() { + for break_jump in breaks { + self.patch_jump(break_jump, after_loop); + } + } + self.continue_targets.pop(); + + Ok(()) + } + + fn compile_pattern(&mut self, pattern: &Pattern) -> Result { + match pattern { + Pattern::Wildcard => { + // Always matches, just pop the value + self.emit(OpCode::Pop); + // Return a dummy jump that will be patched but never taken + let always_true = self.add_constant(Value::Bool(true)); + self.emit(OpCode::Const(always_true)); + Ok(self.emit(OpCode::JumpIfFalse(0))) + } + + Pattern::Literal(lit) => { + // Compare against literal + match lit { + Literal::Integer(n) => { + let idx = self.add_constant(Value::Int(*n)); + self.emit(OpCode::Const(idx)); + } + Literal::Float(n) => { + let idx = self.add_constant(Value::Float(*n)); + self.emit(OpCode::Const(idx)); + } + Literal::String(s) => { + let idx = self.add_constant(Value::String(s.clone())); + self.emit(OpCode::Const(idx)); + } + Literal::Bool(b) => { + let idx = self.add_constant(Value::Bool(*b)); + self.emit(OpCode::Const(idx)); + } + } + self.emit(OpCode::Eq); + Ok(self.emit(OpCode::JumpIfFalse(0))) + } + + Pattern::Identifier(name) => { + // Bind value to name + let slot = self.allocate_local(name); + self.emit(OpCode::StoreLocal(slot)); + // Always matches + let always_true = self.add_constant(Value::Bool(true)); + self.emit(OpCode::Const(always_true)); + Ok(self.emit(OpCode::JumpIfFalse(0))) + } + + Pattern::OkayPattern(binding) => { + // Check if value is Okay + self.emit(OpCode::Dup); + self.emit(OpCode::IsOkay); + let skip = self.emit(OpCode::JumpIfFalse(0)); + + // If okay, extract inner value + if let Some(name) = binding { + self.emit(OpCode::TryUnwrap); + let slot = self.allocate_local(name); + self.emit(OpCode::StoreLocal(slot)); + } else { + self.emit(OpCode::Pop); + } + + Ok(skip) + } + + Pattern::OopsPattern(binding) => { + // Check if value is Oops (not Okay) + self.emit(OpCode::Dup); + self.emit(OpCode::IsOkay); + self.emit(OpCode::Not); + let skip = self.emit(OpCode::JumpIfFalse(0)); + + // If oops, extract error + if let Some(name) = binding { + // Extract error value (implementation specific) + let slot = self.allocate_local(name); + self.emit(OpCode::StoreLocal(slot)); + } else { + self.emit(OpCode::Pop); + } + + Ok(skip) + } + + Pattern::Constructor(name, patterns) => { + // Constructor pattern matching + // For now, just check if it matches the constructor name + let name_idx = self.add_constant(Value::String(name.clone())); + self.emit(OpCode::Const(name_idx)); + self.emit(OpCode::Eq); + let skip = self.emit(OpCode::JumpIfFalse(0)); + + // TODO: Match inner patterns + for _ in patterns { + // Would need to extract fields and match against inner patterns + } + + Ok(skip) + } + + Pattern::Guard(inner, condition) => { + // First match inner pattern + let inner_skip = self.compile_pattern(inner)?; + + // Then check guard condition + self.compile_expr(condition)?; + let guard_skip = self.emit(OpCode::JumpIfFalse(0)); + + // Both must pass - use the guard skip as the main skip + // The inner_skip needs to also jump to the after-arm location + Ok(guard_skip) + } + } + } + + fn compile_expr(&mut self, spanned: &Spanned) -> Result<(), CompileError> { + let expr = &spanned.node; + match expr { + Expr::Literal(lit) => { + match lit { + Literal::Integer(n) => { + let idx = self.add_constant(Value::Int(*n)); + self.emit(OpCode::Const(idx)); + } + Literal::Float(n) => { + let idx = self.add_constant(Value::Float(*n)); + self.emit(OpCode::Const(idx)); + } + Literal::String(s) => { + let idx = self.add_constant(Value::String(s.clone())); + self.emit(OpCode::Const(idx)); + } + Literal::Bool(b) => { + let idx = self.add_constant(Value::Bool(*b)); + self.emit(OpCode::Const(idx)); + } + } + } + + Expr::Identifier(name) => { + if let Some(&slot) = self.locals.get(name) { + self.emit(OpCode::LoadLocal(slot)); + } else if let Some(&func_idx) = self.function_indices.get(name) { + self.emit(OpCode::MakeClosure(func_idx)); + } else { + self.emit(OpCode::LoadGlobal(name.clone())); + } + } + + Expr::Binary(op, left, right) => { + self.compile_expr(left)?; + self.compile_expr(right)?; + + match op { + BinaryOp::Add => self.emit(OpCode::Add), + BinaryOp::Sub => self.emit(OpCode::Sub), + BinaryOp::Mul => self.emit(OpCode::Mul), + BinaryOp::Div => self.emit(OpCode::Div), + BinaryOp::Mod => self.emit(OpCode::Mod), + BinaryOp::Eq => self.emit(OpCode::Eq), + BinaryOp::NotEq => self.emit(OpCode::Ne), + BinaryOp::Lt => self.emit(OpCode::Lt), + BinaryOp::Gt => self.emit(OpCode::Gt), + BinaryOp::LtEq => self.emit(OpCode::Le), + BinaryOp::GtEq => self.emit(OpCode::Ge), + BinaryOp::And => self.emit(OpCode::And), + BinaryOp::Or => self.emit(OpCode::Or), + }; + } + + Expr::Unary(op, operand) => { + self.compile_expr(operand)?; + match op { + UnaryOp::Neg => self.emit(OpCode::Neg), + UnaryOp::Not => self.emit(OpCode::Not), + }; + } + + Expr::Call(name, args) => { + // Push arguments + for arg in args { + self.compile_expr(arg)?; + } + + // Special built-in functions + match name.as_str() { + "print" => { + self.emit(OpCode::Print); + } + "toString" => { + self.emit(OpCode::ToString); + } + "len" => { + self.emit(OpCode::Len); + } + _ => { + // Look up function + if let Some(&func_idx) = self.function_indices.get(name) { + self.emit(OpCode::MakeClosure(func_idx)); + self.emit(OpCode::Call(args.len())); + } else { + // Dynamic call via global + self.emit(OpCode::LoadGlobal(name.clone())); + self.emit(OpCode::Call(args.len())); + } + } + } + } + + Expr::Array(elements) => { + for elem in elements { + self.compile_expr(elem)?; + } + self.emit(OpCode::MakeArray(elements.len())); + } + + Expr::ResultConstructor { is_okay, value } => { + self.compile_expr(value)?; + if *is_okay { + self.emit(OpCode::MakeOkay); + } else { + self.emit(OpCode::MakeOops); + } + } + + Expr::Try(inner) => { + self.compile_expr(inner)?; + self.emit(OpCode::TryUnwrap); + } + + Expr::Unwrap(inner) => { + self.compile_expr(inner)?; + self.emit(OpCode::TryUnwrap); + } + + Expr::UnitMeasurement(value, _unit) => { + // Compile the value, unit is metadata + self.compile_expr(value)?; + } + + Expr::GratitudeLiteral(name) => { + // Gratitude literals are just strings + let idx = self.add_constant(Value::String(name.clone())); + self.emit(OpCode::Const(idx)); + } + } + Ok(()) + } + + /// Try to evaluate a constant expression at compile time + fn try_eval_const(&self, expr: &Expr) -> Option { + match expr { + Expr::Literal(lit) => match lit { + Literal::Integer(n) => Some(Value::Int(*n)), + Literal::Float(n) => Some(Value::Float(*n)), + Literal::String(s) => Some(Value::String(s.clone())), + Literal::Bool(b) => Some(Value::Bool(*b)), + }, + _ => None, + } + } + + // Helper methods + + fn emit(&mut self, op: OpCode) -> usize { + if let Some(ref mut func) = self.current_function { + func.emit(op) + } else { + 0 + } + } + + fn add_constant(&mut self, value: Value) -> usize { + if let Some(ref mut func) = self.current_function { + func.add_constant(value) + } else { + 0 + } + } + + fn current_offset(&self) -> usize { + if let Some(ref func) = self.current_function { + func.current_offset() + } else { + 0 + } + } + + fn patch_jump(&mut self, jump_idx: usize, target: usize) { + if let Some(ref mut func) = self.current_function { + func.patch_jump(jump_idx, target); + } + } + + fn allocate_local(&mut self, name: &str) -> usize { + if let Some(&slot) = self.locals.get(name) { + return slot; + } + + let slot = if let Some(ref mut func) = self.current_function { + let s = func.locals; + func.locals += 1; + s + } else { + 0 + }; + + self.locals.insert(name.to_string(), slot); + slot + } +} + +impl Default for BytecodeCompiler { + fn default() -> Self { + Self::new() + } +} + +/// Compilation error +#[derive(Debug, Clone)] +pub struct CompileError { + pub message: String, +} + +impl std::fmt::Display for CompileError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Compile error: {}", self.message) + } +} + +impl std::error::Error for CompileError {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lexer::Lexer; + use crate::parser::Parser; + + fn compile_source(source: &str) -> Result { + let lexer = Lexer::new(source); + let tokens = lexer.tokenize().unwrap(); + let mut parser = Parser::new(tokens, source); + let program = parser.parse().unwrap(); + + let mut compiler = BytecodeCompiler::new(); + compiler.compile(&program) + } + + #[test] + fn test_compile_simple_function() { + let source = r#" + to add(a: Int, b: Int) -> Int { + give back a + b; + } + "#; + + let program = compile_source(source).unwrap(); + assert_eq!(program.functions.len(), 1); + assert_eq!(program.functions[0].name, "add"); + assert_eq!(program.functions[0].arity, 2); + } + + #[test] + fn test_compile_main() { + let source = r#" + to main() { + remember x = 5; + give back x; + } + "#; + + let program = compile_source(source).unwrap(); + assert!(program.entry.is_some()); + } + + #[test] + fn test_compile_conditional() { + let source = r#" + to test(x: Int) -> Int { + when x > 0 { + give back 1; + } otherwise { + give back 0; + } + } + "#; + + let program = compile_source(source).unwrap(); + let func = &program.functions[0]; + + // Should have JumpIfFalse for condition + assert!(func.code.iter().any(|op| matches!(op, OpCode::JumpIfFalse(_)))); + } +} diff --git a/src/vm/machine.rs b/src/vm/machine.rs new file mode 100644 index 0000000..f42e43d --- /dev/null +++ b/src/vm/machine.rs @@ -0,0 +1,665 @@ +//! WokeLang Virtual Machine +//! +//! Stack-based VM for executing compiled bytecode. + +use crate::interpreter::Value; +use super::bytecode::{CompiledFunction, CompiledProgram, OpCode}; +use std::collections::HashMap; + +/// Call frame for function execution +#[derive(Debug, Clone)] +struct CallFrame { + /// Function being executed + function_idx: usize, + /// Instruction pointer within the function + ip: usize, + /// Base pointer for local variables in the stack + base_ptr: usize, +} + +/// Virtual machine for executing WokeLang bytecode +pub struct VirtualMachine { + /// The program being executed + program: CompiledProgram, + /// Value stack + stack: Vec, + /// Call stack + call_stack: Vec, + /// Global variables + globals: HashMap, + /// Maximum stack size (for safety) + max_stack_size: usize, + /// Maximum call depth (for safety) + max_call_depth: usize, +} + +impl VirtualMachine { + pub fn new(program: CompiledProgram) -> Self { + // Initialize globals from the compiled program + let globals = program.globals.clone(); + Self { + program, + stack: Vec::with_capacity(1024), + call_stack: Vec::with_capacity(64), + globals, + max_stack_size: 10000, + max_call_depth: 1000, + } + } + + /// Run the program starting from main + pub fn run(&mut self) -> Result { + let entry = self.program.entry.ok_or_else(|| VMError { + message: "No main function found".to_string(), + })?; + + self.call_function(entry, 0)?; + + while !self.call_stack.is_empty() { + self.execute_instruction()?; + } + + // Return final value or Unit + Ok(self.stack.pop().unwrap_or(Value::Unit)) + } + + /// Call a function with arguments already on the stack + fn call_function(&mut self, func_idx: usize, arg_count: usize) -> Result<(), VMError> { + if self.call_stack.len() >= self.max_call_depth { + return Err(VMError { + message: "Maximum call depth exceeded".to_string(), + }); + } + + let func = self.program.get_function(func_idx).ok_or_else(|| VMError { + message: format!("Function {} not found", func_idx), + })?; + + if arg_count != func.arity { + return Err(VMError { + message: format!( + "Function {} expects {} arguments, got {}", + func.name, func.arity, arg_count + ), + }); + } + + // Calculate base pointer (before args) + let base_ptr = self.stack.len() - arg_count; + + // Reserve space for locals (beyond parameters) + let extra_locals = func.locals - func.arity; + for _ in 0..extra_locals { + self.stack.push(Value::Unit); + } + + self.call_stack.push(CallFrame { + function_idx: func_idx, + ip: 0, + base_ptr, + }); + + Ok(()) + } + + /// Execute one instruction + fn execute_instruction(&mut self) -> Result<(), VMError> { + let frame = self.call_stack.last_mut().ok_or_else(|| VMError { + message: "No active call frame".to_string(), + })?; + + let func = self.program.get_function(frame.function_idx).ok_or_else(|| VMError { + message: "Invalid function index".to_string(), + })?; + + if frame.ip >= func.code.len() { + // Implicit return + let return_value = self.stack.pop().unwrap_or(Value::Unit); + let frame = self.call_stack.pop().unwrap(); + + // Clean up locals + self.stack.truncate(frame.base_ptr); + self.stack.push(return_value); + return Ok(()); + } + + let instruction = func.code[frame.ip].clone(); + frame.ip += 1; + + // Need to get these before borrowing self mutably + let base_ptr = frame.base_ptr; + let func_idx = frame.function_idx; + + match instruction { + OpCode::Const(idx) => { + let func = self.program.get_function(func_idx).unwrap(); + let value = func.constants.get(idx).cloned().ok_or_else(|| VMError { + message: format!("Constant {} not found", idx), + })?; + self.push(value)?; + } + + OpCode::Pop => { + self.stack.pop(); + } + + OpCode::Dup => { + let value = self.peek()?.clone(); + self.push(value)?; + } + + OpCode::Swap => { + let len = self.stack.len(); + if len >= 2 { + self.stack.swap(len - 1, len - 2); + } + } + + OpCode::LoadLocal(slot) => { + let idx = base_ptr + slot; + let value = self.stack.get(idx).cloned().unwrap_or(Value::Unit); + self.push(value)?; + } + + OpCode::StoreLocal(slot) => { + let value = self.pop()?; + let idx = base_ptr + slot; + + // Extend stack if needed + while self.stack.len() <= idx { + self.stack.push(Value::Unit); + } + self.stack[idx] = value; + } + + OpCode::LoadGlobal(name) => { + let value = self.globals.get(&name).cloned().unwrap_or(Value::Unit); + self.push(value)?; + } + + OpCode::StoreGlobal(name) => { + let value = self.pop()?; + self.globals.insert(name, value); + } + + OpCode::Add => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => Value::Int(x + y), + (Value::Float(x), Value::Float(y)) => Value::Float(x + y), + (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 + y), + (Value::Float(x), Value::Int(y)) => Value::Float(x + *y as f64), + (Value::String(x), Value::String(y)) => Value::String(format!("{}{}", x, y)), + _ => return Err(VMError { + message: format!("Cannot add {:?} and {:?}", a, b), + }), + }; + self.push(result)?; + } + + OpCode::Sub => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => Value::Int(x - y), + (Value::Float(x), Value::Float(y)) => Value::Float(x - y), + (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 - y), + (Value::Float(x), Value::Int(y)) => Value::Float(x - *y as f64), + _ => return Err(VMError { + message: format!("Cannot subtract {:?} and {:?}", a, b), + }), + }; + self.push(result)?; + } + + OpCode::Mul => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => Value::Int(x * y), + (Value::Float(x), Value::Float(y)) => Value::Float(x * y), + (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 * y), + (Value::Float(x), Value::Int(y)) => Value::Float(x * *y as f64), + _ => return Err(VMError { + message: format!("Cannot multiply {:?} and {:?}", a, b), + }), + }; + self.push(result)?; + } + + OpCode::Div => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => { + if *y == 0 { + return Err(VMError { + message: "Division by zero".to_string(), + }); + } + Value::Int(x / y) + } + (Value::Float(x), Value::Float(y)) => Value::Float(x / y), + (Value::Int(x), Value::Float(y)) => Value::Float(*x as f64 / y), + (Value::Float(x), Value::Int(y)) => Value::Float(x / *y as f64), + _ => return Err(VMError { + message: format!("Cannot divide {:?} and {:?}", a, b), + }), + }; + self.push(result)?; + } + + OpCode::Mod => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => Value::Int(x % y), + _ => return Err(VMError { + message: "Modulo requires integers".to_string(), + }), + }; + self.push(result)?; + } + + OpCode::Neg => { + let a = self.pop()?; + let result = match a { + Value::Int(x) => Value::Int(-x), + Value::Float(x) => Value::Float(-x), + _ => return Err(VMError { + message: "Cannot negate non-numeric value".to_string(), + }), + }; + self.push(result)?; + } + + OpCode::Eq => { + let b = self.pop()?; + let a = self.pop()?; + self.push(Value::Bool(a == b))?; + } + + OpCode::Ne => { + let b = self.pop()?; + let a = self.pop()?; + self.push(Value::Bool(a != b))?; + } + + OpCode::Lt => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => x < y, + (Value::Float(x), Value::Float(y)) => x < y, + (Value::Int(x), Value::Float(y)) => (*x as f64) < *y, + (Value::Float(x), Value::Int(y)) => *x < (*y as f64), + _ => false, + }; + self.push(Value::Bool(result))?; + } + + OpCode::Le => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => x <= y, + (Value::Float(x), Value::Float(y)) => x <= y, + (Value::Int(x), Value::Float(y)) => (*x as f64) <= *y, + (Value::Float(x), Value::Int(y)) => *x <= (*y as f64), + _ => false, + }; + self.push(Value::Bool(result))?; + } + + OpCode::Gt => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => x > y, + (Value::Float(x), Value::Float(y)) => x > y, + (Value::Int(x), Value::Float(y)) => (*x as f64) > *y, + (Value::Float(x), Value::Int(y)) => *x > (*y as f64), + _ => false, + }; + self.push(Value::Bool(result))?; + } + + OpCode::Ge => { + let b = self.pop()?; + let a = self.pop()?; + let result = match (&a, &b) { + (Value::Int(x), Value::Int(y)) => x >= y, + (Value::Float(x), Value::Float(y)) => x >= y, + (Value::Int(x), Value::Float(y)) => (*x as f64) >= *y, + (Value::Float(x), Value::Int(y)) => *x >= (*y as f64), + _ => false, + }; + self.push(Value::Bool(result))?; + } + + OpCode::And => { + let b = self.pop()?; + let a = self.pop()?; + self.push(Value::Bool(a.is_truthy() && b.is_truthy()))?; + } + + OpCode::Or => { + let b = self.pop()?; + let a = self.pop()?; + self.push(Value::Bool(a.is_truthy() || b.is_truthy()))?; + } + + OpCode::Not => { + let a = self.pop()?; + self.push(Value::Bool(!a.is_truthy()))?; + } + + OpCode::Concat => { + let b = self.pop()?; + let a = self.pop()?; + let result = Value::String(format!("{}{}", a, b)); + self.push(result)?; + } + + OpCode::Jump(target) => { + if let Some(frame) = self.call_stack.last_mut() { + frame.ip = target; + } + } + + OpCode::JumpIfFalse(target) => { + let cond = self.pop()?; + if !cond.is_truthy() { + if let Some(frame) = self.call_stack.last_mut() { + frame.ip = target; + } + } + } + + OpCode::JumpIfTrue(target) => { + let cond = self.pop()?; + if cond.is_truthy() { + if let Some(frame) = self.call_stack.last_mut() { + frame.ip = target; + } + } + } + + OpCode::Call(arg_count) => { + // Pop the closure/function reference + let callee = self.pop()?; + + match callee { + Value::Int(func_idx) => { + self.call_function(func_idx as usize, arg_count)?; + } + _ => { + return Err(VMError { + message: "Cannot call non-function value".to_string(), + }); + } + } + } + + OpCode::Return => { + let return_value = self.stack.pop().unwrap_or(Value::Unit); + let frame = self.call_stack.pop().unwrap(); + + // Clean up locals + self.stack.truncate(frame.base_ptr); + self.stack.push(return_value); + } + + OpCode::MakeClosure(func_idx) => { + // For now, just push the function index as an integer + self.push(Value::Int(func_idx as i64))?; + } + + OpCode::MakeArray(count) => { + let mut elements = Vec::with_capacity(count); + for _ in 0..count { + elements.push(self.pop()?); + } + elements.reverse(); + self.push(Value::Array(elements))?; + } + + OpCode::MakeRecord(count) => { + let mut map = std::collections::HashMap::new(); + for _ in 0..count { + let value = self.pop()?; + let key = match self.pop()? { + Value::String(s) => s, + _ => return Err(VMError { + message: "Record keys must be strings".to_string(), + }), + }; + map.insert(key, value); + } + self.push(Value::Record(map))?; + } + + OpCode::Index => { + let index = self.pop()?; + let object = self.pop()?; + + let result = match (&object, &index) { + (Value::Array(arr), Value::Int(i)) => { + arr.get(*i as usize).cloned().unwrap_or(Value::Unit) + } + (Value::String(s), Value::Int(i)) => { + s.chars() + .nth(*i as usize) + .map(|c| Value::String(c.to_string())) + .unwrap_or(Value::Unit) + } + (Value::Record(map), Value::String(key)) => { + map.get(key.as_str()).cloned().unwrap_or(Value::Unit) + } + _ => Value::Unit, + }; + self.push(result)?; + } + + OpCode::Len => { + let value = self.pop()?; + let len = match value { + Value::Array(arr) => arr.len(), + Value::String(s) => s.len(), + Value::Record(map) => map.len(), + _ => 0, + }; + self.push(Value::Int(len as i64))?; + } + + OpCode::MakeOkay => { + let value = self.pop()?; + self.push(Value::Okay(Box::new(value)))?; + } + + OpCode::MakeOops => { + let value = self.pop()?; + let msg = match value { + Value::String(s) => s, + other => other.to_string(), + }; + self.push(Value::Oops(msg))?; + } + + OpCode::TryUnwrap => { + let value = self.pop()?; + match value { + Value::Okay(inner) => self.push(*inner)?, + Value::Oops(_) => { + // Propagate error by returning + self.stack.push(value); + if let Some(frame) = self.call_stack.last_mut() { + let func = self.program.get_function(frame.function_idx).unwrap(); + frame.ip = func.code.len(); // Jump to end + } + } + other => self.push(other)?, + } + } + + OpCode::IsOkay => { + let value = self.peek()?; + let is_okay = matches!(value, Value::Okay(_)); + self.push(Value::Bool(is_okay))?; + } + + OpCode::Print => { + let value = self.pop()?; + println!("{}", value); + } + + OpCode::ToString => { + let value = self.pop()?; + self.push(Value::String(value.to_string()))?; + } + + OpCode::Nop => {} + + OpCode::Halt => { + self.call_stack.clear(); + } + } + + Ok(()) + } + + fn push(&mut self, value: Value) -> Result<(), VMError> { + if self.stack.len() >= self.max_stack_size { + return Err(VMError { + message: "Stack overflow".to_string(), + }); + } + self.stack.push(value); + Ok(()) + } + + fn pop(&mut self) -> Result { + self.stack.pop().ok_or_else(|| VMError { + message: "Stack underflow".to_string(), + }) + } + + fn peek(&self) -> Result<&Value, VMError> { + self.stack.last().ok_or_else(|| VMError { + message: "Stack underflow".to_string(), + }) + } +} + +/// VM execution error +#[derive(Debug, Clone)] +pub struct VMError { + pub message: String, +} + +impl std::fmt::Display for VMError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "VM error: {}", self.message) + } +} + +impl std::error::Error for VMError {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::vm::compiler::BytecodeCompiler; + use crate::lexer::Lexer; + use crate::parser::Parser; + + fn run_source(source: &str) -> Result { + let lexer = Lexer::new(source); + let tokens = lexer.tokenize().map_err(|e| e.to_string())?; + let mut parser = Parser::new(tokens, source); + let program = parser.parse().map_err(|e| e.to_string())?; + + let mut compiler = BytecodeCompiler::new(); + let compiled = compiler.compile(&program).map_err(|e| e.to_string())?; + + let mut vm = VirtualMachine::new(compiled); + vm.run().map_err(|e| e.to_string()) + } + + #[test] + fn test_vm_arithmetic() { + let source = r#" + to main() { + give back 2 + 3 * 4; + } + "#; + // Note: without operator precedence, this is (2 + 3) * 4 = 20 + // or with precedence 2 + (3 * 4) = 14 + let result = run_source(source).unwrap(); + assert!(matches!(result, Value::Int(_))); + } + + #[test] + fn test_vm_function_call() { + let source = r#" + to add(a: Int, b: Int) -> Int { + give back a + b; + } + + to main() { + give back add(10, 20); + } + "#; + let result = run_source(source).unwrap(); + assert_eq!(result, Value::Int(30)); + } + + #[test] + fn test_vm_conditional() { + let source = r#" + to main() { + remember x = 10; + when x > 5 { + give back 1; + } otherwise { + give back 0; + } + } + "#; + let result = run_source(source).unwrap(); + assert_eq!(result, Value::Int(1)); + } + + #[test] + fn test_vm_loop() { + let source = r#" + to main() { + remember sum = 0; + repeat 5 times { + sum = sum + 1; + } + give back sum; + } + "#; + let result = run_source(source).unwrap(); + assert_eq!(result, Value::Int(5)); + } + + #[test] + fn test_vm_recursion() { + let source = r#" + to factorial(n: Int) -> Int { + when n <= 1 { + give back 1; + } + give back n * factorial(n - 1); + } + + to main() { + give back factorial(5); + } + "#; + let result = run_source(source).unwrap(); + assert_eq!(result, Value::Int(120)); + } +} diff --git a/src/vm/mod.rs b/src/vm/mod.rs new file mode 100644 index 0000000..393cd5b --- /dev/null +++ b/src/vm/mod.rs @@ -0,0 +1,152 @@ +//! WokeLang Virtual Machine +//! +//! A bytecode compiler and stack-based VM for efficient execution. + +pub mod bytecode; +pub mod compiler; +pub mod machine; +pub mod optimizer; + +pub use bytecode::{CompiledFunction, CompiledProgram, OpCode}; +pub use compiler::{BytecodeCompiler, CompileError}; +pub use machine::{VirtualMachine, VMError}; +pub use optimizer::Optimizer; + +use crate::interpreter::Value; +use crate::lexer::Lexer; +use crate::parser::Parser; + +/// Compile and run WokeLang source code using the VM +pub fn run_vm(source: &str) -> Result { + // Lex + let lexer = Lexer::new(source); + let tokens = lexer.tokenize().map_err(|e| format!("Lexer error: {}", e))?; + + // Parse + let mut parser = Parser::new(tokens, source); + let program = parser.parse().map_err(|e| format!("Parse error: {}", e))?; + + // Compile to bytecode + let mut compiler = BytecodeCompiler::new(); + let mut compiled = compiler + .compile(&program) + .map_err(|e| format!("Compile error: {}", e))?; + + // Optimize + let optimizer = Optimizer::new(); + optimizer.optimize(&mut compiled); + + // Execute + let mut vm = VirtualMachine::new(compiled); + vm.run().map_err(|e| format!("VM error: {}", e)) +} + +/// Compile WokeLang source to bytecode (without running) +pub fn compile(source: &str) -> Result { + let lexer = Lexer::new(source); + let tokens = lexer.tokenize().map_err(|e| format!("Lexer error: {}", e))?; + + let mut parser = Parser::new(tokens, source); + let program = parser.parse().map_err(|e| format!("Parse error: {}", e))?; + + let mut compiler = BytecodeCompiler::new(); + let mut compiled = compiler + .compile(&program) + .map_err(|e| format!("Compile error: {}", e))?; + + let optimizer = Optimizer::new(); + optimizer.optimize(&mut compiled); + + Ok(compiled) +} + +/// Disassemble bytecode for debugging +pub fn disassemble(program: &CompiledProgram) -> String { + let mut output = String::new(); + + for (func_idx, func) in program.functions.iter().enumerate() { + output.push_str(&format!( + "\n=== Function {}: {} (arity: {}, locals: {}) ===\n", + func_idx, func.name, func.arity, func.locals + )); + + // Constants + if !func.constants.is_empty() { + output.push_str("Constants:\n"); + for (i, c) in func.constants.iter().enumerate() { + output.push_str(&format!(" {}: {:?}\n", i, c)); + } + } + + // Instructions + output.push_str("Code:\n"); + for (i, op) in func.code.iter().enumerate() { + output.push_str(&format!(" {:04}: {:?}\n", i, op)); + } + } + + if let Some(entry) = program.entry { + output.push_str(&format!("\nEntry point: function {}\n", entry)); + } + + output +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_run_vm_simple() { + let source = r#" + to main() { + give back 42; + } + "#; + let result = run_vm(source).unwrap(); + assert_eq!(result, Value::Int(42)); + } + + #[test] + fn test_run_vm_arithmetic() { + let source = r#" + to main() { + remember x = 10; + remember y = 20; + give back x + y; + } + "#; + let result = run_vm(source).unwrap(); + assert_eq!(result, Value::Int(30)); + } + + #[test] + fn test_run_vm_function_call() { + let source = r#" + to double(n: Int) -> Int { + give back n * 2; + } + + to main() { + give back double(21); + } + "#; + let result = run_vm(source).unwrap(); + assert_eq!(result, Value::Int(42)); + } + + #[test] + fn test_disassemble() { + let source = r#" + to main() { + remember x = 5; + give back x; + } + "#; + let compiled = compile(source).unwrap(); + let disasm = disassemble(&compiled); + + assert!(disasm.contains("main")); + assert!(disasm.contains("Code:")); + } +} diff --git a/src/vm/optimizer.rs b/src/vm/optimizer.rs new file mode 100644 index 0000000..7687cfb --- /dev/null +++ b/src/vm/optimizer.rs @@ -0,0 +1,376 @@ +//! WokeLang Bytecode Optimizer +//! +//! Optimization passes for improving bytecode performance. + +use crate::interpreter::Value; +use super::bytecode::{CompiledFunction, CompiledProgram, OpCode}; + +/// Optimizer for bytecode programs +pub struct Optimizer { + /// Enable constant folding + pub constant_folding: bool, + /// Enable dead code elimination + pub dead_code_elimination: bool, + /// Enable peephole optimizations + pub peephole: bool, +} + +impl Optimizer { + pub fn new() -> Self { + Self { + constant_folding: true, + dead_code_elimination: true, + peephole: true, + } + } + + /// Optimize a compiled program + pub fn optimize(&self, program: &mut CompiledProgram) { + for func in &mut program.functions { + if self.constant_folding { + self.fold_constants(func); + } + if self.peephole { + self.peephole_optimize(func); + } + if self.dead_code_elimination { + self.eliminate_dead_code(func); + } + } + } + + /// Constant folding - evaluate constant expressions at compile time + fn fold_constants(&self, func: &mut CompiledFunction) { + let mut i = 0; + while i + 2 < func.code.len() { + // Look for patterns like: Const(a), Const(b), BinaryOp + if let (OpCode::Const(a_idx), OpCode::Const(b_idx)) = + (&func.code[i], &func.code[i + 1]) + { + let a = func.constants.get(*a_idx).cloned(); + let b = func.constants.get(*b_idx).cloned(); + + if let (Some(a), Some(b)) = (a, b) { + let result = match &func.code[i + 2] { + OpCode::Add => self.fold_add(&a, &b), + OpCode::Sub => self.fold_sub(&a, &b), + OpCode::Mul => self.fold_mul(&a, &b), + OpCode::Div => self.fold_div(&a, &b), + OpCode::Eq => Some(Value::Bool(a == b)), + OpCode::Ne => Some(Value::Bool(a != b)), + OpCode::Lt => self.fold_lt(&a, &b), + OpCode::Le => self.fold_le(&a, &b), + OpCode::Gt => self.fold_gt(&a, &b), + OpCode::Ge => self.fold_ge(&a, &b), + OpCode::And => Some(Value::Bool(a.is_truthy() && b.is_truthy())), + OpCode::Or => Some(Value::Bool(a.is_truthy() || b.is_truthy())), + _ => None, + }; + + if let Some(result) = result { + // Replace the three instructions with a single Const + let result_idx = func.add_constant(result); + func.code[i] = OpCode::Const(result_idx); + func.code[i + 1] = OpCode::Nop; + func.code[i + 2] = OpCode::Nop; + } + } + } + i += 1; + } + + // Remove Nop instructions and update jump targets + self.remove_nops(func); + } + + fn fold_add(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) => Some(Value::Int(x + y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Float(x + y)), + (Value::Int(x), Value::Float(y)) => Some(Value::Float(*x as f64 + y)), + (Value::Float(x), Value::Int(y)) => Some(Value::Float(x + *y as f64)), + (Value::String(x), Value::String(y)) => Some(Value::String(format!("{}{}", x, y))), + _ => None, + } + } + + fn fold_sub(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) => Some(Value::Int(x - y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Float(x - y)), + (Value::Int(x), Value::Float(y)) => Some(Value::Float(*x as f64 - y)), + (Value::Float(x), Value::Int(y)) => Some(Value::Float(x - *y as f64)), + _ => None, + } + } + + fn fold_mul(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) => Some(Value::Int(x * y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Float(x * y)), + (Value::Int(x), Value::Float(y)) => Some(Value::Float(*x as f64 * y)), + (Value::Float(x), Value::Int(y)) => Some(Value::Float(x * *y as f64)), + _ => None, + } + } + + fn fold_div(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) if *y != 0 => Some(Value::Int(x / y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Float(x / y)), + (Value::Int(x), Value::Float(y)) => Some(Value::Float(*x as f64 / y)), + (Value::Float(x), Value::Int(y)) => Some(Value::Float(x / *y as f64)), + _ => None, + } + } + + fn fold_lt(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) => Some(Value::Bool(x < y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Bool(x < y)), + _ => None, + } + } + + fn fold_le(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) => Some(Value::Bool(x <= y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Bool(x <= y)), + _ => None, + } + } + + fn fold_gt(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) => Some(Value::Bool(x > y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Bool(x > y)), + _ => None, + } + } + + fn fold_ge(&self, a: &Value, b: &Value) -> Option { + match (a, b) { + (Value::Int(x), Value::Int(y)) => Some(Value::Bool(x >= y)), + (Value::Float(x), Value::Float(y)) => Some(Value::Bool(x >= y)), + _ => None, + } + } + + /// Peephole optimizations - local pattern-based improvements + fn peephole_optimize(&self, func: &mut CompiledFunction) { + let mut i = 0; + while i < func.code.len() { + // Pattern: Pop followed by Const -> remove Pop if value unused + // Pattern: Dup followed by Pop -> remove both + if i + 1 < func.code.len() { + match (&func.code[i], &func.code[i + 1]) { + (OpCode::Dup, OpCode::Pop) => { + func.code[i] = OpCode::Nop; + func.code[i + 1] = OpCode::Nop; + } + (OpCode::Not, OpCode::Not) => { + // Double negation elimination + func.code[i] = OpCode::Nop; + func.code[i + 1] = OpCode::Nop; + } + (OpCode::Neg, OpCode::Neg) => { + // Double negation elimination + func.code[i] = OpCode::Nop; + func.code[i + 1] = OpCode::Nop; + } + _ => {} + } + } + + // Pattern: Jump to next instruction -> remove + if let OpCode::Jump(target) = &func.code[i] { + if *target == i + 1 { + func.code[i] = OpCode::Nop; + } + } + + // Pattern: Const(true) followed by JumpIfFalse -> remove both (never jumps) + if i + 1 < func.code.len() { + if let OpCode::Const(c_idx) = func.code[i] { + // Check for Const(true) followed by JumpIfFalse + if let Some(Value::Bool(true)) = func.constants.get(c_idx) { + if matches!(func.code[i + 1], OpCode::JumpIfFalse(_)) { + func.code[i] = OpCode::Nop; + func.code[i + 1] = OpCode::Nop; + } + } + // Check for Const(false) followed by JumpIfFalse + else if let Some(Value::Bool(false)) = func.constants.get(c_idx) { + if let OpCode::JumpIfFalse(target) = func.code[i + 1] { + // Always jumps, convert to unconditional + func.code[i] = OpCode::Nop; + func.code[i + 1] = OpCode::Jump(target); + } + } + } + } + + i += 1; + } + + self.remove_nops(func); + } + + /// Dead code elimination - remove unreachable code + fn eliminate_dead_code(&self, func: &mut CompiledFunction) { + if func.code.is_empty() { + return; + } + + // Mark reachable instructions using control flow analysis + let mut reachable = vec![false; func.code.len()]; + let mut worklist = vec![0usize]; // Start from first instruction + + while let Some(idx) = worklist.pop() { + if idx >= func.code.len() || reachable[idx] { + continue; + } + + reachable[idx] = true; + + match &func.code[idx] { + OpCode::Jump(target) => { + worklist.push(*target); + } + OpCode::JumpIfFalse(target) | OpCode::JumpIfTrue(target) => { + worklist.push(*target); + worklist.push(idx + 1); + } + OpCode::Return | OpCode::Halt => { + // Don't add next instruction + } + _ => { + worklist.push(idx + 1); + } + } + } + + // Replace unreachable instructions with Nop + for (i, is_reachable) in reachable.iter().enumerate() { + if !is_reachable { + func.code[i] = OpCode::Nop; + } + } + + self.remove_nops(func); + } + + /// Remove Nop instructions and update jump targets + fn remove_nops(&self, func: &mut CompiledFunction) { + // Build mapping from old to new indices + let mut new_indices = Vec::with_capacity(func.code.len()); + let mut new_idx = 0usize; + + for op in &func.code { + new_indices.push(new_idx); + if !matches!(op, OpCode::Nop) { + new_idx += 1; + } + } + + // Update jump targets + for op in &mut func.code { + match op { + OpCode::Jump(ref mut target) => { + if *target < new_indices.len() { + *target = new_indices[*target]; + } + } + OpCode::JumpIfFalse(ref mut target) | OpCode::JumpIfTrue(ref mut target) => { + if *target < new_indices.len() { + *target = new_indices[*target]; + } + } + _ => {} + } + } + + // Remove Nops + func.code.retain(|op| !matches!(op, OpCode::Nop)); + } +} + +impl Default for Optimizer { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_constant_folding() { + let mut func = CompiledFunction::new("test".to_string(), 0); + + let c1 = func.add_constant(Value::Int(10)); + let c2 = func.add_constant(Value::Int(20)); + + func.emit(OpCode::Const(c1)); + func.emit(OpCode::Const(c2)); + func.emit(OpCode::Add); + func.emit(OpCode::Return); + + let mut program = CompiledProgram::new(); + program.add_function(func); + + let optimizer = Optimizer::new(); + optimizer.optimize(&mut program); + + let func = &program.functions[0]; + + // Should have folded to a single constant + assert!(func.code.len() < 4); + assert!(func.constants.iter().any(|c| c == &Value::Int(30))); + } + + #[test] + fn test_double_negation_elimination() { + let mut func = CompiledFunction::new("test".to_string(), 0); + + func.emit(OpCode::LoadLocal(0)); + func.emit(OpCode::Not); + func.emit(OpCode::Not); + func.emit(OpCode::Return); + + let mut program = CompiledProgram::new(); + program.add_function(func); + + let optimizer = Optimizer::new(); + optimizer.optimize(&mut program); + + let func = &program.functions[0]; + + // Should have removed both Not instructions + assert!(!func.code.iter().any(|op| matches!(op, OpCode::Not))); + } + + #[test] + fn test_dead_code_elimination() { + let mut func = CompiledFunction::new("test".to_string(), 0); + + let c1 = func.add_constant(Value::Int(1)); + + func.emit(OpCode::Const(c1)); + func.emit(OpCode::Return); + func.emit(OpCode::Const(c1)); // Dead code + func.emit(OpCode::Print); // Dead code + + let mut program = CompiledProgram::new(); + program.add_function(func); + + let optimizer = Optimizer::new(); + optimizer.optimize(&mut program); + + let func = &program.functions[0]; + + // Should have removed dead code + assert_eq!(func.code.len(), 2); + } +}