summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/forth/parser.rs83
-rwxr-xr-xsrc/lib.rs60
2 files changed, 109 insertions, 34 deletions
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<T> = Result<T, ParseError>;
+// 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<Vec<Annotation>>,
// 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<usize>,
// number of clauses currently being defined for a particular ‘if’
// construct. a stack is needed in order to allow for nesting.
- if_stack: Vec<IfClauses>,
+ 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<Annotation> {
+ 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::<i32>() {
- 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))),
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 850070a..71f7b7d 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -50,15 +50,55 @@ impl ExportedByteCode {
}
}
+// ibid.
+#[wasm_bindgen]
+#[derive(Clone)]
+pub struct ExportedAnnotation {
+ pub start: usize,
+ pub end: usize,
+}
+
+impl From<(usize, usize)> for ExportedAnnotation {
+ fn from(v: (usize, usize)) -> Self {
+ Self {
+ start: v.0,
+ end: v.1,
+ }
+ }
+}
+
+// ibid.
+#[wasm_bindgen]
+#[derive(Clone)]
+pub struct ExportedWordAnnotations(Vec<ExportedAnnotation>);
+
+impl ExportedWordAnnotations {
+ pub fn from_word_annotations(annos: &Vec<forth::parser::Annotation>) -> Self {
+ ExportedWordAnnotations(annos.iter().map(|anno| anno.loc.into()).collect())
+ }
+}
+
+#[wasm_bindgen]
+impl ExportedWordAnnotations {
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ pub fn at(&self, offset: usize) -> ExportedAnnotation {
+ self.0[offset].clone()
+ }
+}
+
#[wasm_bindgen]
pub struct ExportedInterp {
+ annos: Vec<ExportedWordAnnotations>,
i: Option<forth::interp::Interp>,
}
#[wasm_bindgen]
impl ExportedInterp {
fn new() -> Self {
- Self { i: None }
+ Self { annos: vec![], i: None }
}
pub fn compile(&mut self, text: &str) -> bool {
@@ -67,6 +107,16 @@ impl ExportedInterp {
error!("couldn't parse program text: {:?}", e);
return false
}
+ self.annos = p.annotations.iter()
+ .map(|word_anno| -> ExportedWordAnnotations {
+ let v = word_anno.into_iter()
+ .map(|anno| -> ExportedAnnotation {
+ anno.loc.into()
+ })
+ .collect();
+ ExportedWordAnnotations(v)
+ })
+ .collect();
let interp = forth::interp::Interp::new(p.wordlist);
let _ = self.i.insert(interp);
true
@@ -105,6 +155,14 @@ impl ExportedInterp {
interp.wordlist.iter().map(|bc| ExportedByteCode::from_bc(bc)).collect()
}
+ pub fn annotations(&self) -> Vec<ExportedWordAnnotations> {
+ self.annos.clone()
+ }
+
+ pub fn annotation_at(&self, ip: &ExportedInstructionPointer) -> ExportedAnnotation {
+ self.annos[ip.word].0[ip.offset].clone()
+ }
+
pub fn callstack(&self) -> Vec<ExportedInstructionPointer> {
let Some(interp) = &self.i else {
return vec![]