neural/rune/rs/lex/lib.rs

/// Token stream for rune. Both classic and pure registers share this lexer.
/// The parser disambiguates register by context (file pragma or fenced block tag).
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
    // Literals
    Int(u64),
    Float(String),
    Str(String),
    Ident(String),
    // Sigils โ€” atomic semantics defined in cybermark
    Hash,   // # โ€” particle
    Pat,    // @ โ€” neuron / identity
    Sig,    // ~ โ€” name / annotation
    Fas,    // / โ€” scope / path
    Buc,    // $ โ€” token / economic
    Ket,    // ^ โ€” abstract / lift
    Zap,    // ! โ€” effect / action
    Dot,    // . โ€” transform / apply
    Bar,    // | โ€” core / composition
    Tis,    // = โ€” bind / equivalence
    Wut,    // ? โ€” test / decision
    Col,    // : โ€” pair / two-way
    Lus,    // + โ€” augment
    // Classic register keywords
    Fn, Let, If, Else, Loop, Return, Struct, Enum, Impl, Pub, Use, Async, Await,
    // Punctuation
    LParen, RParen, LBrace, RBrace, LBrack, RBrack,
    Comma, Semi, Bang, Amp, Pipe, Percent, Star, Slash,
    Arrow,      // ->
    FatArrow,   // =>
    ColCol,     // ::
    DotDot,     // ..
    // Layout (significant in pure register tall form)
    Newline,
    Indent,
    Dedent,
    Eof,
}

#[derive(Debug, Clone)]
pub struct Span {
    pub start: usize,
    pub end: usize,
}

#[derive(Debug, Clone)]
pub struct Spanned<T> {
    pub inner: T,
    pub span: Span,
}

pub struct Lexer<'src> {
    src: &'src str,
    pos: usize,
}

impl<'src> Lexer<'src> {
    pub fn new(src: &'src str) -> Self {
        Lexer { src, pos: 0 }
    }

    pub fn tokenize(mut self) -> Vec<Spanned<Token>> {
        let mut out = Vec::new();
        loop {
            self.skip_horizontal_whitespace();
            let start = self.pos;
            match self.next_token() {
                Some(tok) => out.push(Spanned { inner: tok, span: Span { start, end: self.pos } }),
                None if self.pos >= self.src.len() => break,
                None => { self.pos += 1; } // skip unknown char
            }
        }
        out.push(Spanned { inner: Token::Eof, span: Span { start: self.pos, end: self.pos } });
        out
    }

    fn next_token(&mut self) -> Option<Token> {
        // Skip line comments
        if self.peek2() == Some((':', ':')) {
            while !matches!(self.peek(), Some('\n') | None) { self.advance(); }
            return None;
        }
        // Skip block comments `/* ... */`
        if self.peek2() == Some(('/', '*')) {
            self.advance(); self.advance();
            while self.peek2() != Some(('*', '/')) && self.pos < self.src.len() { self.advance(); }
            self.advance(); self.advance();
            return None;
        }

        let ch = self.peek()?;
        Some(match ch {
            '\n' => { self.advance(); Token::Newline }
            '#' => { self.advance(); Token::Hash }
            '@' => { self.advance(); Token::Pat }
            '~' => { self.advance(); Token::Sig }
            '/' => { self.advance(); Token::Fas }
            '$' => { self.advance(); Token::Buc }
            '^' => { self.advance(); Token::Ket }
            '!' => { self.advance(); Token::Zap }
            '.' => {
                self.advance();
                if self.peek() == Some('.') { self.advance(); Token::DotDot }
                else { Token::Dot }
            }
            '|' => { self.advance(); Token::Bar }
            '=' => {
                self.advance();
                if self.peek() == Some('>') { self.advance(); Token::FatArrow }
                else { Token::Tis }
            }
            '?' => { self.advance(); Token::Wut }
            ':' => {
                self.advance();
                if self.peek() == Some(':') { self.advance(); Token::ColCol }
                else { Token::Col }
            }
            '+' => { self.advance(); Token::Lus }
            '-' => {
                self.advance();
                if self.peek() == Some('>') { self.advance(); Token::Arrow }
                else { Token::Ident("-".into()) }
            }
            '(' => { self.advance(); Token::LParen }
            ')' => { self.advance(); Token::RParen }
            '{' => { self.advance(); Token::LBrace }
            '}' => { self.advance(); Token::RBrace }
            '[' => { self.advance(); Token::LBrack }
            ']' => { self.advance(); Token::RBrack }
            ',' => { self.advance(); Token::Comma }
            ';' => { self.advance(); Token::Semi }
            '&' => { self.advance(); Token::Amp }
            '%' => { self.advance(); Token::Percent }
            '*' => { self.advance(); Token::Star }
            '<' => {
                self.advance();
                if self.peek() == Some('=') { self.advance(); Token::Ident("<=".into()) }
                else { Token::Ident("<".into()) }
            }
            '>' => {
                self.advance();
                if self.peek() == Some('=') { self.advance(); Token::Ident(">=".into()) }
                else { Token::Ident(">".into()) }
            }
            '"' => self.lex_str(),
            '0'..='9' => self.lex_number(),
            'a'..='z' | 'A'..='Z' | '_' => self.lex_ident(),
            _ => return None,
        })
    }

    fn lex_number(&mut self) -> Token {
        let start = self.pos;
        while matches!(self.peek(), Some('0'..='9' | '_')) { self.advance(); }
        if self.peek() == Some('.') && matches!(self.peek_at(1), Some('0'..='9')) {
            self.advance();
            while matches!(self.peek(), Some('0'..='9' | '_')) { self.advance(); }
            Token::Float(self.src[start..self.pos].to_string())
        } else {
            let s: String = self.src[start..self.pos].chars().filter(|c| *c != '_').collect();
            Token::Int(s.parse().unwrap_or(0))
        }
    }

    fn lex_str(&mut self) -> Token {
        self.advance();
        let start = self.pos;
        while matches!(self.peek(), Some(c) if c != '"') {
            if self.peek() == Some('\\') { self.advance(); }
            self.advance();
        }
        let s = self.src[start..self.pos].to_string();
        if self.peek() == Some('"') { self.advance(); }
        Token::Str(s)
    }

    fn lex_ident(&mut self) -> Token {
        let start = self.pos;
        while matches!(self.peek(), Some('a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-')) {
            self.advance();
        }
        match &self.src[start..self.pos] {
            "fn"     => Token::Fn,
            "let"    => Token::Let,
            "if"     => Token::If,
            "else"   => Token::Else,
            "loop"   => Token::Loop,
            "return" => Token::Return,
            "struct" => Token::Struct,
            "enum"   => Token::Enum,
            "impl"   => Token::Impl,
            "pub"    => Token::Pub,
            "use"    => Token::Use,
            "async"  => Token::Async,
            "await"  => Token::Await,
            word     => Token::Ident(word.to_string()),
        }
    }

    fn skip_horizontal_whitespace(&mut self) {
        while matches!(self.peek(), Some(' ' | '\t' | '\r')) { self.advance(); }
    }

    fn peek(&self) -> Option<char> { self.src[self.pos..].chars().next() }
    fn peek_at(&self, offset: usize) -> Option<char> {
        self.src[self.pos..].chars().nth(offset)
    }
    fn peek2(&self) -> Option<(char, char)> {
        let mut it = self.src[self.pos..].chars();
        Some((it.next()?, it.next()?))
    }
    fn advance(&mut self) {
        if let Some(c) = self.src[self.pos..].chars().next() {
            self.pos += c.len_utf8();
        }
    }
}

Homonyms

cyb/optica/src/lib.rs
soft3/strata/src/lib.rs
cyb/honeycrisp/src/lib.rs
warriors/trisha/honeycrisp/lib.rs
warriors/trisha/wgpu/lib.rs
soft3/glia/import/lib.rs
soft3/foculus/src/lib.rs
soft3/nox/rs/lib.rs
soft3/cybergraph/src/lib.rs
soft3/tru/rs/lib.rs
soft3/mudra/src/lib.rs
soft3/glia/run/lib.rs
cyb/prysm/rs/lib.rs
warriors/trisha/rs/lib.rs
cyb/src-tauri/src/lib.rs
soft3/mir/src/lib.rs
soft3/lens/src/lib.rs
neural/trident/src/lib.rs
neural/rune/rs/subject/lib.rs
cyb/cyb/cyb-services/src/lib.rs
soft3/strata/nebu/rs/lib.rs
soft3/lens/core/src/lib.rs
neural/rs/mir-format/src/lib.rs
soft3/zheng/rs/src/lib.rs
neural/rune/rs/interp/lib.rs
soft3/radio/iroh-willow/src/lib.rs
neural/rune/rs/parse/lib.rs
neural/eidos/rs/src/lib.rs
neural/rs/darwin-sys/src/lib.rs
soft3/radio/iroh-gossip/src/lib.rs
soft3/radio/iroh-ffi/src/lib.rs
soft3/radio/iroh-car/src/lib.rs
soft3/radio/iroh-relay/src/lib.rs
soft3/bbg/rs/src/lib.rs
soft3/radio/iroh-docs/src/lib.rs
soft3/lens/ikat/src/lib.rs
cyb/honeycrisp/aruminium/src/lib.rs
soft3/hemera/rs/src/lib.rs
neural/rune/rs/ast/lib.rs
soft3/radio/iroh-blobs/src/lib.rs
cyb/honeycrisp/acpu/src/lib.rs
soft3/lens/porphyry/src/lib.rs
cyb/honeycrisp/rane/src/lib.rs
neural/rune/rs/compile/lib.rs
neural/rune/rs/parse-pure/lib.rs
neural/rs/codegen/src/lib.rs
soft3/lens/binius/src/lib.rs
neural/rune/rs/prysm/lib.rs
neural/rs/link/src/lib.rs
neural/rune/rs/mold/lib.rs
soft3/strata/proof/src/lib.rs
soft3/lens/brakedown/src/lib.rs
soft3/strata/kuro/rs/lib.rs
soft3/lens/assayer/src/lib.rs
neural/rs/core/src/lib.rs
neural/rs/macros/src/lib.rs
soft3/radio/cyber-bao/src/lib.rs
soft3/strata/compute/src/lib.rs
soft3/radio/iroh-base/src/lib.rs
soft3/radio/iroh-dns-server/src/lib.rs
neural/rune/rs/lower/lib.rs
soft3/strata/ext/src/lib.rs
soft3/strata/core/src/lib.rs
soft3/hemera/wgsl/src/lib.rs
soft3/radio/iroh/src/lib.rs
cyb/honeycrisp/unimem/src/lib.rs
cyb/evy/crates/evy_engine_tasks/src/lib.rs
cyb/evy/crates/evy_dialect/src/lib.rs
cyb/wysm/crates/wasi/src/lib.rs
cyb/wysm/crates/fuzz/src/lib.rs
soft3/strata/genies/rs/src/lib.rs
cyb/evy/crates/evy_platform_caps/src/lib.rs
neural/inf/rs/oracle/src/lib.rs
soft3/strata/jali/wgsl/src/lib.rs
cyb/evy/forks/bevy_transform/src/lib.rs
soft3/tape/impl/rust/src/lib.rs
cyb/wysm/crates/wasmi/src/lib.rs
cyb/evy/forks/bevy_render/src/lib.rs
cyb/evy/crates/evy_ecs_storage/src/lib.rs
cyb/evy/forks/naga/src/lib.rs
soft3/strata/trop/wgsl/src/lib.rs
cyb/wysm/crates/c_api/artifact/lib.rs
cyb/evy/forks/bevy_ecs/src/lib.rs
cyb/wysm/crates/ir/src/lib.rs
cyb/evy/forks/bevy_animation/src/lib.rs
cyb/evy/forks/bevy_sprite_render/src/lib.rs
cyb/wysm/crates/c_api/src/lib.rs
neural/inf/rs/parse/src/lib.rs
soft3/strata/trop/rs/src/lib.rs
soft3/strata/kuro/wgsl/src/lib.rs
neural/trident/editor/zed/src/lib.rs
cyb/evy/forks/bevy_mesh/src/lib.rs
cyb/evy/crates/evy_radio/src/lib.rs
cyb/evy/forks/bevy_anti_alias/src/lib.rs
soft3/strata/jali/rs/src/lib.rs
cyb/wysm/crates/wast/src/lib.rs
neural/inf/rs/plan/src/lib.rs
neural/rs/tests/macro-integration/src/lib.rs
soft3/radio/iroh-ffi/iroh-js/src/lib.rs
cyb/evy/forks/bevy_image/src/lib.rs
cyb/evy/forks/bevy_post_process/src/lib.rs
neural/inf/rs/source/src/lib.rs
cyb/wysm/crates/core/src/lib.rs
cyb/evy/crates/evy_diagnostic/src/lib.rs
cyb/evy/crates/evy_engine_dispatch/src/lib.rs
cyb/evy/forks/bevy_pbr/src/lib.rs
cyb/evy/forks/bevy_gizmos/src/lib.rs
cyb/evy/forks/bevy_gizmos_render/src/lib.rs
soft3/radio/iroh/bench/src/lib.rs
neural/inf/rs/lex/src/lib.rs
neural/inf/rs/ast/src/lib.rs
soft3/strata/genies/wgsl/src/lib.rs
soft3/strata/nebu/wgsl/src/lib.rs
cyb/wysm/crates/collections/src/lib.rs
neural/inf/rs/lower/src/lib.rs
cyb/evy/forks/bevy_sprite/src/lib.rs
cyb/evy/forks/bevy_diagnostic/src/lib.rs
neural/inf/rs/eval/src/lib.rs
cyb/wysm/crates/c_api/macro/lib.rs
cyb/evy/forks/bevy_tasks/src/lib.rs
cyb/evy/forks/bevy_core_pipeline/src/lib.rs
cyb/evy/crates/evy_prysm_core/src/lib.rs
neural/inf/rs/value/src/lib.rs
cyb/evy/crates/evy_engine_core/src/lib.rs
soft3/radio/tests/integration/src/lib.rs
bootloader/go-cyber/cw/packages/cyber-std-test/src/lib.rs
bootloader/go-cyber/cw/contracts/std-test/src/lib.rs
bootloader/go-cyber/cw/contracts/graph-filter/src/lib.rs
bootloader/go-cyber/cw/packages/cyber-std/src/lib.rs

Graph