call-graph #1
5 changed files with 143 additions and 95 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -250,6 +250,7 @@ dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"parse_int",
|
"parse_int",
|
||||||
|
"petgraph",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
.0;
|
.callable_graph
|
||||||
|
.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()),
|
||||||
|
|
|
@ -64,14 +64,15 @@ pub enum Instruction {
|
||||||
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![]),
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue