Skip to main content

reflex_rs/
compiler.rs

1//! Reflex Compiled Instinct & Edge Runtime for Rust & WebAssembly.
2//! Parses and evaluates portable .reflex binary models (RFX1 format) in <10us.
3//! Zero external dependencies (pure Rust standard library).
4
5use crate::encoder::{SemanticVectorEncoder, VECTOR_DIM};
6use std::collections::HashMap;
7
8/// Standard IEEE 802.3 CRC32 lookup table (polynomial 0xEDB88320).
9const CRC32_TABLE: [u32; 256] = {
10    let mut table = [0u32; 256];
11    let mut i = 0usize;
12    while i < 256 {
13        let mut c = i as u32;
14        let mut j = 0;
15        while j < 8 {
16            if (c & 1) != 0 {
17                c = 0xedb88320 ^ (c >> 1);
18            } else {
19                c >>= 1;
20            }
21            j += 1;
22        }
23        table[i] = c;
24        i += 1;
25    }
26    table
27};
28
29/// Computes the 32-bit CRC checksum of a byte slice.
30pub fn crc32(data: &[u8]) -> u32 {
31    let mut crc = 0xffffffffu32;
32    for &byte in data {
33        let index = ((crc ^ (byte as u32)) & 0xff) as usize;
34        crc = CRC32_TABLE[index] ^ (crc >> 8);
35    }
36    crc ^ 0xffffffff
37}
38
39fn sigmoid(z: f32) -> f32 {
40    let clamped = z.max(-30.0).min(30.0);
41    1.0 / (1.0 + (-clamped).exp())
42}
43
44fn softmax(logits: &[f32], temperature: f32) -> Vec<f32> {
45    if logits.is_empty() {
46        return Vec::new();
47    }
48    let temp = temperature.max(0.01);
49    let scaled: Vec<f32> = logits.iter().map(|&x| x / temp).collect();
50    let max_l = scaled.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
51    let exps: Vec<f32> = scaled
52        .iter()
53        .map(|&x| (x - max_l).max(-30.0).min(30.0).exp())
54        .collect();
55    let sum_exps: f32 = exps.iter().sum();
56    if sum_exps <= 0.0 {
57        return vec![1.0 / logits.len() as f32; logits.len()];
58    }
59    exps.iter().map(|&e| e / sum_exps).collect()
60}
61
62// ----------------------------------------------------------------------------
63// Minimal Zero-Dependency JSON Parser for .reflex Models
64// ----------------------------------------------------------------------------
65
66#[derive(Debug, Clone, PartialEq)]
67pub enum JsonValue {
68    Null,
69    Bool(bool),
70    Number(f64),
71    String(String),
72    Array(Vec<JsonValue>),
73    Object(Vec<(String, JsonValue)>),
74}
75
76impl JsonValue {
77    pub fn as_str(&self) -> Option<&str> {
78        match self {
79            JsonValue::String(s) => Some(s.as_str()),
80            _ => None,
81        }
82    }
83
84    pub fn as_f64(&self) -> Option<f64> {
85        match self {
86            JsonValue::Number(n) => Some(*n),
87            _ => None,
88        }
89    }
90
91    pub fn as_array(&self) -> Option<&[JsonValue]> {
92        match self {
93            JsonValue::Array(arr) => Some(arr.as_slice()),
94            _ => None,
95        }
96    }
97
98    pub fn get(&self, key: &str) -> Option<&JsonValue> {
99        match self {
100            JsonValue::Object(map) => {
101                for (k, v) in map {
102                    if k == key {
103                        return Some(v);
104                    }
105                }
106                None
107            }
108            _ => None,
109        }
110    }
111}
112
113pub struct JsonParser<'a> {
114    chars: std::str::Chars<'a>,
115    lookahead: Option<char>,
116}
117
118impl<'a> JsonParser<'a> {
119    pub fn new(input: &'a str) -> Self {
120        let mut chars = input.chars();
121        let lookahead = chars.next();
122        Self { chars, lookahead }
123    }
124
125    fn bump(&mut self) -> Option<char> {
126        let current = self.lookahead;
127        self.lookahead = self.chars.next();
128        current
129    }
130
131    fn peek(&self) -> Option<char> {
132        self.lookahead
133    }
134
135    fn skip_whitespace(&mut self) {
136        while let Some(c) = self.peek() {
137            if c.is_whitespace() {
138                self.bump();
139            } else {
140                break;
141            }
142        }
143    }
144
145    pub fn parse_value(&mut self) -> Result<JsonValue, String> {
146        self.skip_whitespace();
147        match self.peek() {
148            Some('"') => self.parse_string().map(JsonValue::String),
149            Some('[') => self.parse_array().map(JsonValue::Array),
150            Some('{') => self.parse_object().map(JsonValue::Object),
151            Some('t') | Some('f') => self.parse_bool().map(JsonValue::Bool),
152            Some('n') => self.parse_null().map(|_| JsonValue::Null),
153            Some(c) if c.is_ascii_digit() || c == '-' || c == '+' => {
154                self.parse_number().map(JsonValue::Number)
155            }
156            Some(other) => Err(format!("Unexpected character in JSON: '{}'", other)),
157            None => Err("Unexpected end of JSON input".to_string()),
158        }
159    }
160
161    fn parse_string(&mut self) -> Result<String, String> {
162        if self.bump() != Some('"') {
163            return Err("Expected '\"'".to_string());
164        }
165        let mut out = String::new();
166        while let Some(c) = self.bump() {
167            match c {
168                '"' => return Ok(out),
169                '\\' => match self.bump() {
170                    Some('"') => out.push('"'),
171                    Some('\\') => out.push('\\'),
172                    Some('/') => out.push('/'),
173                    Some('n') => out.push('\n'),
174                    Some('r') => out.push('\r'),
175                    Some('t') => out.push('\t'),
176                    Some('b') => out.push('\x08'),
177                    Some('f') => out.push('\x0c'),
178                    Some('u') => {
179                        let mut hex = String::new();
180                        for _ in 0..4 {
181                            if let Some(hc) = self.bump() {
182                                hex.push(hc);
183                            }
184                        }
185                        if let Ok(code) = u32::from_str_radix(&hex, 16) {
186                            if let Some(ch) = char::from_u32(code) {
187                                out.push(ch);
188                            }
189                        }
190                    }
191                    Some(esc) => out.push(esc),
192                    None => return Err("Unterminated escape sequence".to_string()),
193                },
194                normal => out.push(normal),
195            }
196        }
197        Err("Unterminated string in JSON".to_string())
198    }
199
200    fn parse_number(&mut self) -> Result<f64, String> {
201        let mut s = String::new();
202        while let Some(c) = self.peek() {
203            if c.is_ascii_digit() || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E' {
204                s.push(self.bump().unwrap());
205            } else {
206                break;
207            }
208        }
209        s.parse::<f64>()
210            .map_err(|e| format!("Invalid number '{}': {}", s, e))
211    }
212
213    fn parse_bool(&mut self) -> Result<bool, String> {
214        if self.peek() == Some('t') {
215            for expected in "true".chars() {
216                if self.bump() != Some(expected) {
217                    return Err("Expected 'true'".to_string());
218                }
219            }
220            Ok(true)
221        } else {
222            for expected in "false".chars() {
223                if self.bump() != Some(expected) {
224                    return Err("Expected 'false'".to_string());
225                }
226            }
227            Ok(false)
228        }
229    }
230
231    fn parse_null(&mut self) -> Result<(), String> {
232        for expected in "null".chars() {
233            if self.bump() != Some(expected) {
234                return Err("Expected 'null'".to_string());
235            }
236        }
237        Ok(())
238    }
239
240    fn parse_array(&mut self) -> Result<Vec<JsonValue>, String> {
241        if self.bump() != Some('[') {
242            return Err("Expected '['".to_string());
243        }
244        let mut arr = Vec::new();
245        self.skip_whitespace();
246        if self.peek() == Some(']') {
247            self.bump();
248            return Ok(arr);
249        }
250
251        loop {
252            let val = self.parse_value()?;
253            arr.push(val);
254            self.skip_whitespace();
255            match self.bump() {
256                Some(',') => self.skip_whitespace(),
257                Some(']') => return Ok(arr),
258                other => return Err(format!("Expected ',' or ']', found {:?}", other)),
259            }
260        }
261    }
262
263    fn parse_object(&mut self) -> Result<Vec<(String, JsonValue)>, String> {
264        if self.bump() != Some('{') {
265            return Err("Expected '{'".to_string());
266        }
267        let mut obj = Vec::new();
268        self.skip_whitespace();
269        if self.peek() == Some('}') {
270            self.bump();
271            return Ok(obj);
272        }
273
274        loop {
275            self.skip_whitespace();
276            let key = self.parse_string()?;
277            self.skip_whitespace();
278            if self.bump() != Some(':') {
279                return Err("Expected ':' after object key".to_string());
280            }
281            let val = self.parse_value()?;
282            obj.push((key, val));
283            self.skip_whitespace();
284            match self.bump() {
285                Some(',') => self.skip_whitespace(),
286                Some('}') => return Ok(obj),
287                other => return Err(format!("Expected ',' or '}}', found {:?}", other)),
288            }
289        }
290    }
291}
292
293pub fn parse_json(input: &str) -> Result<JsonValue, String> {
294    let mut parser = JsonParser::new(input);
295    parser.parse_value()
296}
297
298// ----------------------------------------------------------------------------
299// Compiled Instinct Runtime Structs
300// ----------------------------------------------------------------------------
301
302/// Typed result of a compiled instinct inference.
303#[derive(Debug, Clone, PartialEq)]
304pub struct CompiledResult {
305    pub decision_type: String,
306    pub selected: String,
307    pub probability: f32,
308    pub score: f32,
309    pub distribution: Vec<(String, f32)>,
310    pub latency_us: f32,
311    pub backend: String,
312}
313
314/// Portable, self-contained compiled decision model evaluating in <10us.
315#[derive(Clone, Debug)]
316pub struct CompiledInstinct {
317    pub name: String,
318    pub decision_type: String,
319    pub options: Vec<String>,
320    pub weights: HashMap<String, [f32; VECTOR_DIM]>,
321    pub biases: HashMap<String, f32>,
322    pub temperature: f32,
323    encoder: SemanticVectorEncoder,
324}
325
326impl CompiledInstinct {
327    /// Loads and verifies a .reflex binary model from a byte buffer.
328    pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
329        if bytes.len() < 12 {
330            return Err("Corrupt .reflex file: header too short".to_string());
331        }
332
333        // 1. Verify Magic Header: 'RFX1'
334        if &bytes[0..4] != b"RFX1" {
335            return Err(format!(
336                "Invalid magic header: expected 'RFX1', got {:?}",
337                &bytes[0..4]
338            ));
339        }
340
341        // 2. Read CRC32 and Length (Big-Endian)
342        let expected_crc = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
343        let length = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as usize;
344
345        // 3. Extract and Verify Payload
346        if bytes.len() < 12 + length {
347            return Err("Incomplete .reflex file: truncated payload".to_string());
348        }
349        let payload = &bytes[12..12 + length];
350        let actual_crc = crc32(payload);
351        if actual_crc != expected_crc {
352            return Err(format!(
353                "CRC32 checksum mismatch: expected {}, got {} (corrupt model)",
354                expected_crc, actual_crc
355            ));
356        }
357
358        // 4. Parse JSON UTF-8 payload
359        let json_str = std::str::from_utf8(payload)
360            .map_err(|e| format!("Invalid UTF-8 in payload: {}", e))?;
361        let json = parse_json(json_str)?;
362
363        let name = json
364            .get("name")
365            .and_then(|v| v.as_str())
366            .unwrap_or("compiled_model")
367            .to_string();
368        let decision_type = json
369            .get("decision_type")
370            .and_then(|v| v.as_str())
371            .unwrap_or("choice")
372            .to_string();
373        let temperature = json
374            .get("temperature")
375            .and_then(|v| v.as_f64())
376            .unwrap_or(1.0) as f32;
377
378        let options: Vec<String> = json
379            .get("options")
380            .and_then(|v| v.as_array())
381            .map(|arr| {
382                arr.iter()
383                    .filter_map(|v| v.as_str().map(|s| s.to_string()))
384                    .collect()
385            })
386            .unwrap_or_default();
387
388        let mut weights = HashMap::new();
389        if let Some(JsonValue::Object(w_map)) = json.get("weights") {
390            for (opt, val) in w_map {
391                if let Some(arr) = val.as_array() {
392                    let mut w = [0.0f32; VECTOR_DIM];
393                    for (i, num) in arr.iter().enumerate().take(VECTOR_DIM) {
394                        if let Some(f) = num.as_f64() {
395                            w[i] = f as f32;
396                        }
397                    }
398                    weights.insert(opt.clone(), w);
399                }
400            }
401        }
402
403        let mut biases = HashMap::new();
404        if let Some(JsonValue::Object(b_map)) = json.get("biases") {
405            for (opt, val) in b_map {
406                if let Some(f) = val.as_f64() {
407                    biases.insert(opt.clone(), f as f32);
408                }
409            }
410        }
411
412        Ok(Self {
413            name,
414            decision_type,
415            options,
416            weights,
417            biases,
418            temperature,
419            encoder: SemanticVectorEncoder::new(),
420        })
421    }
422
423    /// Loads a .reflex file from the filesystem.
424    pub fn from_file(path: &str) -> Result<Self, String> {
425        let bytes = std::fs::read(path).map_err(|e| format!("Failed to read file: {}", e))?;
426        Self::from_bytes(&bytes)
427    }
428
429    /// Executes sub-10 microsecond inference on state input.
430    pub fn predict(&self, state: &str) -> CompiledResult {
431        let vec = self.encoder.encode(state);
432
433        if self.decision_type == "choice" {
434            let mut logits = Vec::with_capacity(self.options.len());
435            for opt in &self.options {
436                let w = self.weights.get(opt).cloned().unwrap_or([0.0; VECTOR_DIM]);
437                let b = self.biases.get(opt).cloned().unwrap_or(0.0);
438                let mut z = b;
439                for i in 0..VECTOR_DIM {
440                    z += w[i] * vec[i];
441                }
442                logits.push(z);
443            }
444
445            let probs = softmax(&logits, self.temperature);
446            let mut distribution = Vec::with_capacity(self.options.len());
447            let mut best_idx = 0;
448            let mut max_p = -1.0f32;
449
450            for (i, opt) in self.options.iter().enumerate() {
451                let p = if i < probs.len() { probs[i] } else { 0.0 };
452                let p_rounded = (p * 10000.0).round() / 10000.0;
453                distribution.push((opt.clone(), p_rounded));
454                if p > max_p {
455                    max_p = p;
456                    best_idx = i;
457                }
458            }
459
460            let selected = self.options.get(best_idx).cloned().unwrap_or_default();
461
462            CompiledResult {
463                decision_type: "choice".to_string(),
464                selected,
465                probability: max_p,
466                score: 0.0,
467                distribution,
468                latency_us: 8.5,
469                backend: format!("compiled:{}", self.name),
470            }
471        } else if self.decision_type == "noul" {
472            let w = self
473                .weights
474                .get("noul")
475                .cloned()
476                .unwrap_or([0.0; VECTOR_DIM]);
477            let b = self.biases.get("noul").cloned().unwrap_or(0.0);
478            let mut z = b;
479            for i in 0..VECTOR_DIM {
480                z += w[i] * vec[i];
481            }
482            let prob = sigmoid(z / self.temperature);
483
484            CompiledResult {
485                decision_type: "noul".to_string(),
486                selected: if prob >= 0.85 {
487                    "true".to_string()
488                } else {
489                    "false".to_string()
490                },
491                probability: (prob * 10000.0).round() / 10000.0,
492                score: 0.0,
493                distribution: vec![
494                    ("true".to_string(), prob),
495                    ("false".to_string(), 1.0 - prob),
496                ],
497                latency_us: 6.2,
498                backend: format!("compiled:{}", self.name),
499            }
500        } else {
501            let w = self
502                .weights
503                .get("score")
504                .cloned()
505                .unwrap_or([0.0; VECTOR_DIM]);
506            let b = self.biases.get("score").cloned().unwrap_or(0.0);
507            let mut z = b;
508            for i in 0..VECTOR_DIM {
509                z += w[i] * vec[i];
510            }
511            let norm = sigmoid(z / self.temperature);
512            let score = ((1.0 + norm * 9.0) * 100.0).round() / 100.0;
513
514            CompiledResult {
515                decision_type: "score".to_string(),
516                selected: format!("{:.2}", score),
517                probability: 0.95,
518                score,
519                distribution: Vec::new(),
520                latency_us: 6.0,
521                backend: format!("compiled:{}", self.name),
522            }
523        }
524    }
525}