[WIP] Callable graph

This commit is contained in:
Dominic Grimm 2022-09-28 18:31:41 +02:00
parent 072653935e
commit 040ab13911
No known key found for this signature in database
GPG key ID: A6C051C716D2CE65
5 changed files with 143 additions and 95 deletions

1
Cargo.lock generated
View file

@ -250,6 +250,7 @@ dependencies = [
"itertools", "itertools",
"lazy_static", "lazy_static",
"parse_int", "parse_int",
"petgraph",
] ]
[[package]] [[package]]

View file

@ -24,4 +24,5 @@ parse_int = "0.6.0"
indexmap = "1.9.1" indexmap = "1.9.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
dependency-graph = "0.1.5" dependency-graph = "0.1.5"
petgraph = "0.6.2"

View file

@ -1,3 +1,6 @@
: test1 40 ;
: test2 test1 2 ;
1 1 = debug 1 1 = debug
if 69 else 42 then debug if 69 else 42 then debug

View file

@ -1,7 +1,8 @@
use anyhow::{bail, Result}; use anyhow::{bail, Context, Result};
use indexmap::IndexSet; use indexmap::IndexSet;
use itertools::Itertools; use itertools::Itertools;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use petgraph::Graph;
use std::collections::HashMap; use std::collections::HashMap;
use crate::parser; use crate::parser;
@ -9,31 +10,6 @@ use crate::parser;
mod instruction; mod instruction;
pub use crate::compiler::instruction::Instruction; pub use crate::compiler::instruction::Instruction;
pub trait Compilable<T, U> {
fn compile(&self, data: &T) -> Result<U>;
}
#[derive(Debug)]
pub struct Word {
pub id: usize,
pub instructions: Vec<Instruction>,
pub times_used: usize,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Condition {
pub if_instructions: Vec<Instruction>,
pub else_instructions: Vec<Instruction>,
}
#[derive(Debug)]
pub struct Data {
pub words: HashMap<String, Word>,
// pub word_graph:
pub conditions: IndexSet<Condition>,
pub strings: IndexSet<String>,
}
pub const TEMPLATE_ASM: &str = include_str!("compiler/template.asm"); pub const TEMPLATE_ASM: &str = include_str!("compiler/template.asm");
lazy_static! { lazy_static! {
@ -45,12 +21,50 @@ lazy_static! {
.body; .body;
} }
pub trait Compilable<T, U> {
fn compile(&self, data: &T) -> Result<U>;
}
#[derive(Debug)]
pub struct Word {
pub id: usize,
pub instructions: Vec<Instruction>,
pub times_used: usize,
pub callable_graph_node: petgraph::graph::NodeIndex,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Condition {
pub if_instructions: Vec<Instruction>,
pub else_instructions: Vec<Instruction>,
pub callable_graph_node: petgraph::graph::NodeIndex,
}
#[derive(Debug)]
pub enum CallableId {
Word(String),
Condition(usize),
}
#[derive(Debug)]
pub struct Data {
// pub words: HashMap<String, Word>,
// pub conditions: IndexSet<Condition>,
pub strings: IndexSet<String>,
pub callable_graph: Graph<CallableId, ()>,
pub words: HashMap<String, Word>,
pub conditions: Vec<Condition>,
}
impl Data { impl Data {
pub fn default() -> Self { pub fn default() -> Self {
Self { Self {
words: HashMap::new(), // words: HashMap::new(),
conditions: IndexSet::new(), // conditions: IndexSet::new(),
strings: IndexSet::new(), strings: IndexSet::new(),
callable_graph: Graph::new(),
words: HashMap::new(),
conditions: vec![],
} }
} }
@ -104,28 +118,55 @@ impl Data {
bail!("Word already exists as user word definition: {}", name); bail!("Word already exists as user word definition: {}", name);
} }
let ins = self.generate_instructions(body, optimize)?; let origin = self
.callable_graph
.add_node(CallableId::Word(name.to_string()));
self.words.insert( self.words.insert(
name, name.to_string(),
Word { Word {
id: self.words.len(), id: self.words.len(),
instructions: ins, instructions: vec![],
times_used: 0, times_used: 0,
callable_graph_node: origin,
}, },
); );
let ins = self.generate_instructions(body, optimize)?;
self.words
.get_mut(&name)
.context(format!("Could not get word: {}", name))?
.instructions = ins.clone();
for instruction in ins {
match instruction {
Instruction::Call(x) => {
self.callable_graph.add_edge(
origin,
self.words
.get(&x)
.context(format!("Could not get referenced word: {}", x))?
.callable_graph_node,
(),
);
}
Instruction::Condition(x) => {}
_ => {}
}
}
dbg!(&self);
} }
parser::ast::Node::Condition { if_body, else_body } => { parser::ast::Node::Condition { if_body, else_body } => {
let if_instructions = self.generate_instructions(if_body, optimize)?; let if_instructions = self.generate_instructions(if_body, optimize)?;
let else_instructions = self.generate_instructions(else_body, optimize)?; let else_instructions = self.generate_instructions(else_body, optimize)?;
let id = self let id = self.conditions.len();
.conditions self.conditions.push(Condition {
.insert_full(Condition { if_instructions,
if_instructions, else_instructions,
else_instructions, callable_graph_node: self
}) .callable_graph
.0; .add_node(CallableId::Condition(id)),
});
instructions.push(Instruction::Condition(id)); instructions.push(Instruction::Condition(id));
self.callable_graph.add_node(CallableId::Condition(id));
} }
parser::ast::Node::Word(x) => { parser::ast::Node::Word(x) => {
if let Some(ins) = Instruction::from_word(&x) { if let Some(ins) = Instruction::from_word(&x) {
@ -159,42 +200,42 @@ impl Data {
} }
// conditions // conditions
for (id, c) in self.conditions.iter().enumerate() { // for (id, c) in self.conditions.iter().enumerate() {
x.push(hence::parser::ast::Node::Label(format!( // x.push(hence::parser::ast::Node::Label(format!(
"conditions_if_{}", // "conditions_if_{}",
id // id
))); // )));
x.extend(c.if_instructions.iter().map(|ins| ins.compile(self)).collect::<Result<Vec<_>>>()?.into_iter().flatten()); // x.extend(c.if_instructions.iter().map(|ins| ins.compile(self)).collect::<Result<Vec<_>>>()?.into_iter().flatten());
x.push(hence::parser::ast::Node::Label(format!("conditions_else_{}", id))); // x.push(hence::parser::ast::Node::Label(format!("conditions_else_{}", id)));
x.extend(c.else_instructions.iter().map(|ins| ins.compile(self)).collect::<Result<Vec<_>>>()?.into_iter().flatten()); // x.extend(c.else_instructions.iter().map(|ins| ins.compile(self)).collect::<Result<Vec<_>>>()?.into_iter().flatten());
} // }
// words // words
for (name, word) in &self // for (name, word) in &self
.words // .words
.iter() // .iter()
.filter(|(_, w)| w.times_used > 1) // .filter(|(_, w)| w.times_used > 1)
.sorted_by(|a, b| Ord::cmp(&a.1.id, &b.1.id)) // .sorted_by(|a, b| Ord::cmp(&a.1.id, &b.1.id))
.collect::<Vec<_>>() // .collect::<Vec<_>>()
{ // {
x.extend(vec![ // x.extend(vec![
hence::parser::ast::Node::Label(format!("words_{}", word.id)), // hence::parser::ast::Node::Label(format!("words_{}", word.id)),
hence::parser::ast::Node::Comment(format!("word: \"{}\"", name)), // hence::parser::ast::Node::Comment(format!("word: \"{}\"", name)),
]); // ]);
x.extend( // x.extend(
word.instructions // word.instructions
.iter() // .iter()
.map(|ins| ins.compile(self)) // .map(|ins| ins.compile(self))
.collect::<Result<Vec<hence::parser::ast::Body>>>() // .collect::<Result<Vec<hence::parser::ast::Body>>>()
.unwrap() // .unwrap()
.into_iter() // .into_iter()
.flatten(), // .flatten(),
); // );
x.push(hence::parser::ast::Node::MacroCall { // x.push(hence::parser::ast::Node::MacroCall {
name: "return_call_stack_jump".to_string(), // name: "return_call_stack_jump".to_string(),
args: vec![], // args: vec![],
}); // });
} // }
x.extend([ x.extend([
hence::parser::ast::Node::Label("main".to_string()), hence::parser::ast::Node::Label("main".to_string()),

View file

@ -63,15 +63,16 @@ pub enum Instruction {
TwoMinus, TwoMinus,
Times, Times,
Divide, Divide,
Mod, Mod,
Call(String),
AsmQuote(String), AsmQuote(String),
Condition(usize),
Multiple { Multiple {
instruction: Box<Self>, instruction: Box<Self>,
count: usize, count: usize,
}, },
Call(String),
Condition(usize),
} }
impl compiler::Compilable<compiler::Data, hence::parser::ast::Body> for Instruction { impl compiler::Compilable<compiler::Data, hence::parser::ast::Body> for Instruction {
@ -1222,25 +1223,26 @@ impl compiler::Compilable<compiler::Data, hence::parser::ast::Body> for Instruct
}, },
]), ]),
Instruction::Mod => Ok(vec![]), Instruction::Mod => Ok(vec![]),
Instruction::Call(x) => match data.words.get(x) { // Instruction::Call(x) => match data.words.get(x) {
Some(w) => { // Some(w) => {
if w.times_used > 1 { // if w.times_used > 1 {
Ok(vec![hence::parser::ast::Node::MacroCall { // Ok(vec![hence::parser::ast::Node::MacroCall {
name: "call_stack_jump".to_string(), // name: "call_stack_jump".to_string(),
args: vec![hence::arg::Arg::Variable(format!("words_{}", w.id)), hence::arg::Arg::Variable("OFFSET".to_string())], // args: vec![hence::arg::Arg::Variable(format!("words_{}", w.id)), hence::arg::Arg::Variable("OFFSET".to_string())],
}]) // }])
} else { // } else {
Ok(w.instructions // Ok(w.instructions
.iter() // .iter()
.map(|ins| ins.compile(data)) // .map(|ins| ins.compile(data))
.collect::<Result<Vec<hence::parser::ast::Body>>>()? // .collect::<Result<Vec<hence::parser::ast::Body>>>()?
.into_iter() // .into_iter()
.flatten() // .flatten()
.collect()) // .collect())
} // }
} // }
None => bail!("Unknown word: {}", x), // None => bail!("Unknown word: {}", x),
}, // },
Instruction::Call(x) => bail!("Unknown word: {}", x),
Instruction::AsmQuote(x) => Ok(hence::parser::parse(hence::lexer::lex(x)?)?.body), Instruction::AsmQuote(x) => Ok(hence::parser::parse(hence::lexer::lex(x)?)?.body),
Instruction::Condition(x) => Ok(vec![]), Instruction::Condition(x) => Ok(vec![]),