From 49ab2791b861884d01488de68f63bdd49d71c1b2 Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Sun, 24 Aug 2025 09:16:11 -0400 Subject: pass annotations to js so we can highlight program text --- src/forth/parser.rs | 83 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 33 deletions(-) (limited to 'src/forth/parser.rs') diff --git a/src/forth/parser.rs b/src/forth/parser.rs index 1eb93df..c934c4a 100644 --- a/src/forth/parser.rs +++ b/src/forth/parser.rs @@ -27,6 +27,14 @@ impl std::error::Error for ParseError {} type ParseResult = Result; +// todo: the annotations should be directly tied to the wordlist so +// they can't get out of sync. +#[derive(Debug)] +pub struct Annotation { + // (start, end) index in program text + pub loc: (usize, usize), +} + #[derive(Debug)] pub struct WordCatalog<'a>(pub(super) HashMap<&'a str, usize>); @@ -44,6 +52,7 @@ pub struct Parser<'a> { // routine is always in the first entry. // todo: don't be pub, have a method to extract a wordlist pub wordlist: WordList, + pub annotations: Vec>, // catalog of word to word index in `wordlist` pub wordalog: WordCatalog<'a>, // holds a stack of indices into `wordlist` that are currently @@ -52,7 +61,7 @@ pub struct Parser<'a> { defstack: Vec, // number of clauses currently being defined for a particular ‘if’ // construct. a stack is needed in order to allow for nesting. - if_stack: Vec, + if_stack: Vec<(IfClauses, Annotation)>, } impl<'a> Parser<'a> { @@ -64,6 +73,7 @@ impl<'a> Parser<'a> { text, enumerator: text.chars().enumerate(), wordlist: WordList(wl), + annotations: vec![vec![]], wordalog: WordCatalog(HashMap::new()), defstack: vec![], if_stack: vec![], @@ -93,21 +103,25 @@ impl<'a> Parser<'a> { &self.wordlist.0[*word_index] } + fn anno_mut(&mut self) -> &mut Vec { + let word_index = self.defstack.last().unwrap_or(&0); + &mut self.annotations[*word_index] + } + // push `op` onto the currently building bytecode, as determined // by the top of the `namestack`. - fn bc_push(&mut self, op: OpCode) -> ParseResult<()> { - // let word_index = self.defstack.last().unwrap_or(&0); - // self.wordlist.0[*word_index].0.push(op); + fn bc_push(&mut self, op: OpCode, anno: Annotation) { self.bc_mut().0.push(op); - Ok(()) + self.anno_mut().push(anno); } pub fn parse(&mut self) -> ParseResult<()> { - while let Some((word, _start, end)) = self.next_word() { + while let Some((word, start, end)) = self.next_word() { + let anno = Annotation { loc: (start, end) }; if let Ok(i) = word.parse::() { - self.bc_push(OpCode::Num(i))?; + self.bc_push(OpCode::Num(i), anno); } else if let Some(i) = self.wordalog.0.get(word) { - self.bc_push(OpCode::Call(*i))?; + self.bc_push(OpCode::Call(*i), anno); } else { match word { r#"s""# => { @@ -115,13 +129,14 @@ impl<'a> Parser<'a> { self.enumerator .find(|(_i, c)| return *c == '"') .ok_or(ParseError::MissingQuote)?; - self.bc_push(OpCode::Str(end+1, s_end))?; + self.bc_push(OpCode::Str(end+1, s_end), anno); }, ":" => { let (name, _, _) = self.next_word().ok_or(ParseError::EOF)?; self.wordalog.0.insert(name, self.wordlist.0.len()); self.defstack.push(self.wordlist.0.len()); self.wordlist.0.push(ByteCode(vec![])); + self.annotations.push(vec![]); }, ";" => { match self.bc().0.last() { @@ -136,59 +151,61 @@ impl<'a> Parser<'a> { self.bc_mut().0.pop(); self.bc_mut().0.push(OpCode::TIf(t, f)); // technically only needed if ‘f’ is None, but whatever. - self.bc_mut().0.push(OpCode::Ret); + self.bc_push(OpCode::Ret, anno); }, - _ => self.bc_push(OpCode::Ret)?, + _ => self.bc_push(OpCode::Ret, anno), } self.defstack.pop().ok_or(ParseError::DefStackEmpty)?; }, "if" => { let i = self.wordlist.0.len(); self.wordlist.0.push(ByteCode(vec![])); + self.annotations.push(vec![]); self.defstack.push(i); - self.if_stack.push(IfClauses::True(i)); + self.if_stack.push((IfClauses::True(i), anno)); }, "else" => { - self.bc_push(OpCode::Ret)?; + self.bc_push(OpCode::Ret, anno); self.defstack.pop(); let i = self.wordlist.0.len(); self.wordlist.0.push(ByteCode(vec![])); + self.annotations.push(vec![]); self.defstack.push(i); - let true_clause = match self.if_stack.pop() { + let (true_clause, anno) = match self.if_stack.pop() { None => return Err(ParseError::MissingIf), - Some(IfClauses::TrueFalse(_, _)) => return Err(ParseError::MissingIf), - Some(IfClauses::True(cl)) => cl, + Some((IfClauses::TrueFalse(_, _), _)) => return Err(ParseError::MissingIf), + Some((IfClauses::True(cl), anno)) => (cl, anno), }; - self.if_stack.push(IfClauses::TrueFalse(true_clause, i)); + self.if_stack.push((IfClauses::TrueFalse(true_clause, i), anno)); }, "then" => { - self.bc_push(OpCode::Ret)?; + self.bc_push(OpCode::Ret, anno); self.defstack.pop(); match self.if_stack.pop() { None => return Err(ParseError::MissingIf), - Some(IfClauses::True(true_clause)) => { - self.bc_push(OpCode::If(true_clause, None))?; + Some((IfClauses::True(true_clause), anno)) => { + self.bc_push(OpCode::If(true_clause, None), anno); }, - Some(IfClauses::TrueFalse(true_clause, false_clause)) => { - self.bc_push(OpCode::If(true_clause, Some(false_clause)))?; + Some((IfClauses::TrueFalse(true_clause, false_clause), anno)) => { + self.bc_push(OpCode::If(true_clause, Some(false_clause)), anno); } } }, - "+" => self.bc_push(OpCode::Add)?, - "-" => self.bc_push(OpCode::Sub)?, - "*" => self.bc_push(OpCode::Mul)?, - "/" => self.bc_push(OpCode::Div)?, - "dup" => self.bc_push(OpCode::Dup)?, - "drop" => self.bc_push(OpCode::Drop)?, - "=" => self.bc_push(OpCode::EQ)?, - ">" => self.bc_push(OpCode::GT)?, - ">=" => self.bc_push(OpCode::GTE)?, - "<" => self.bc_push(OpCode::LT)?, - "<=" => self.bc_push(OpCode::LTE)?, + "+" => self.bc_push(OpCode::Add, anno), + "-" => self.bc_push(OpCode::Sub, anno), + "*" => self.bc_push(OpCode::Mul, anno), + "/" => self.bc_push(OpCode::Div, anno), + "dup" => self.bc_push(OpCode::Dup, anno), + "drop" => self.bc_push(OpCode::Drop, anno), + "=" => self.bc_push(OpCode::EQ, anno), + ">" => self.bc_push(OpCode::GT, anno), + ">=" => self.bc_push(OpCode::GTE, anno), + "<" => self.bc_push(OpCode::LT, anno), + "<=" => self.bc_push(OpCode::LTE, anno), other => return Err(ParseError::UnknownWord(String::from(other))), } } -- cgit v1.3