Skip to content

Commit 23cbb87

Browse files
authored
refactor: ImlProgram compilation and finalization (#147)
* ImlProgram rework * Fix failing test
1 parent b30ea46 commit 23cbb87

File tree

4 files changed

+107
-111
lines changed

4 files changed

+107
-111
lines changed

src/compiler/compiler.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl Compiler {
148148
println!("--- Intermediate main ---\n{:#?}", main_parselet);
149149
}
150150

151-
let program = ImlProgram::new(ImlValue::from(main_parselet));
151+
let program = ImlProgram::new(main_parselet);
152152

153153
match program.compile() {
154154
Ok(program) => {

src/compiler/iml/imlparselet.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ impl ImlRefParselet {
311311
var_value
312312
.1
313313
.as_ref()
314-
.and_then(|value| Some(program.register(value))),
314+
.and_then(|value| Some(program.register(value).unwrap_or_default())),
315315
)
316316
})
317317
.collect(),

src/compiler/iml/imlprogram.rs

+103-107
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,28 @@
22
33
use super::*;
44
use crate::reader::Offset;
5-
use crate::value::Parselet;
5+
use crate::value::ParseletRef;
66
use crate::vm::Program;
77
use crate::Error;
88
use crate::{Object, RefValue};
9-
use indexmap::{indexmap, IndexMap, IndexSet};
9+
use indexmap::{indexmap, indexset, IndexMap, IndexSet};
1010
use log;
11-
use std::collections::{HashMap, HashSet};
11+
use std::cell::RefCell;
12+
use std::collections::HashMap;
1213

1314
#[derive(Debug)]
1415
pub(in crate::compiler) struct ImlProgram {
15-
main: ImlValue,
16-
statics: IndexMap<ImlValue, Option<Parselet>>, // static values with optional final parselet replacement
17-
errors: Vec<Error>, // errors collected during compilation
16+
errors: Vec<Error>, // errors collected during compilation
17+
statics: IndexSet<Result<RefValue, usize>>,
18+
parselets: IndexMap<ImlRefParselet, usize>,
1819
}
1920

2021
impl ImlProgram {
21-
pub fn new(main: ImlValue) -> Self {
22+
pub fn new(main: ImlRefParselet) -> Self {
2223
ImlProgram {
23-
main: main.clone(),
24-
statics: indexmap!(main => None),
2524
errors: Vec::new(),
25+
statics: indexset![Err(0)],
26+
parselets: indexmap![main => 0],
2627
}
2728
}
2829

@@ -37,25 +38,39 @@ impl ImlProgram {
3738
3839
In case *value* already exists inside of the current statics, the existing index will be returned,
3940
otherwiese the value is cloned and put into the statics table. */
40-
pub fn register(&mut self, value: &ImlValue) -> usize {
41+
pub fn register(&mut self, value: &ImlValue) -> Result<usize, ()> {
4142
match value {
4243
ImlValue::Shared(value) => return self.register(&*value.borrow()),
43-
ImlValue::Parselet(_) | ImlValue::Value(_) => match self.statics.get_index_of(value) {
44-
None => return self.statics.insert_full(value.clone(), None).0,
45-
Some(idx) => return idx,
44+
ImlValue::Parselet(parselet) => match self.parselets.get(parselet) {
45+
Some(idx) => Ok(*idx),
46+
None => {
47+
let idx = self.statics.insert_full(Err(self.parselets.len() + 1)).0;
48+
self.parselets.insert(parselet.clone(), idx);
49+
Ok(idx)
50+
}
4651
},
47-
ImlValue::Variable { offset, name, .. } => self.errors.push(Error::new(
48-
offset.clone(),
49-
format!("Variable '{}' used in static context", name),
50-
)),
52+
ImlValue::Value(value) => {
53+
let value = Ok(value.clone());
54+
55+
match self.statics.get_index_of(&value) {
56+
Some(idx) => Ok(idx),
57+
None => Ok(self.statics.insert_full(value).0),
58+
}
59+
}
60+
ImlValue::Variable { offset, name, .. } => {
61+
self.errors.push(Error::new(
62+
offset.clone(),
63+
format!("Variable '{}' used in static context", name),
64+
));
65+
Err(())
66+
}
5167
ImlValue::Generic { offset, .. } | ImlValue::Instance(ImlInstance { offset, .. }) => {
5268
self.errors
5369
.push(Error::new(offset.clone(), format!("Unresolved {}", value)));
70+
Err(())
5471
}
5572
_ => unreachable!(),
5673
}
57-
58-
0
5974
}
6075

6176
/** Turns the ImlProgram and its intermediate values into a final VM program ready for execution.
@@ -65,44 +80,36 @@ impl ImlProgram {
6580
and nullable parselet detection occurs.
6681
*/
6782
pub fn compile(mut self) -> Result<Program, Vec<Error>> {
68-
log::info!("compiling {}", self.main);
69-
70-
let mut finalize = HashSet::new(); // list of consuming parselets required to be finalized
83+
log::info!("compiling {}", self.parselets[0]);
7184

72-
// Loop until end of statics is reached
73-
let mut idx = 0;
85+
// Loop until end of parselets is reached
86+
let mut count = 0;
7487

75-
// self.statics grows inside of this while loop, therefore this condition.
76-
while idx < self.statics.len() {
88+
// self.parselets grows inside of this while loop, therefore this condition.
89+
while count < self.parselets.len() {
7790
log::trace!(
78-
"idx = {: >3}, statics.len() = {: >3}",
79-
idx,
80-
self.statics.len()
91+
"count = {: >3}, parselets.len() = {: >3}",
92+
count,
93+
self.parselets.len()
8194
);
8295

83-
// Pick only intermediate parselets, other static values are directly moved
84-
let parselet = match self.statics.get_index_mut(idx).unwrap() {
85-
(_, Some(_)) => unreachable!(), // may not exist!
86-
(ImlValue::Parselet(parselet), None) => parselet.clone(),
87-
_ => {
88-
idx += 1;
89-
continue;
90-
}
91-
};
92-
96+
let (parselet, idx) = self
97+
.parselets
98+
.get_index(count)
99+
.map(|(p, idx)| (p.clone(), *idx))
100+
.unwrap();
93101
log::trace!("idx = {: >3}, parselet = {:?}", idx, parselet.borrow().name);
94102

95-
// Memoize parselets required to be finalized (needs a general rework later...)
96-
if parselet.borrow().model.borrow().is_consuming {
97-
//fixme...
98-
finalize.insert(parselet.clone());
99-
}
103+
// Compile static VM parselet from intermediate parselet
104+
let compiled_parselet = parselet.compile(&mut self, idx);
100105

101-
// Compile VM parselet from intermediate parselet
102-
// println!("...compiling {} {:?}", idx, parselet.name);
103-
*self.statics.get_index_mut(idx).unwrap().1 = Some(parselet.compile(&mut self, idx));
106+
// Insert new parselet before placeholder...
107+
self.statics
108+
.insert_before(idx, Ok(RefValue::from(compiled_parselet)));
109+
// ...and remove the placeholder.
110+
self.statics.shift_remove_index(idx + 1);
104111

105-
idx += 1;
112+
count += 1;
106113
}
107114

108115
// Stop on any raised error
@@ -111,36 +118,16 @@ impl ImlProgram {
111118
}
112119

113120
// Finalize parselets
114-
log::info!("{} has {} parselets to finalize", self.main, finalize.len());
115-
116-
for (i, parselet) in finalize.iter().enumerate() {
117-
log::trace!(" {: >3} => {:#?}", i, parselet);
118-
}
119-
120-
let leftrec = self.finalize(finalize);
121+
self.finalize();
121122

122123
// Assemble all statics to be transferred into a Program
123124
let statics: Vec<RefValue> = self
124125
.statics
125126
.into_iter()
126-
.map(|(iml, parselet)| {
127-
if let Some(mut parselet) = parselet {
128-
if let ImlValue::Parselet(imlparselet) = iml {
129-
parselet.consuming = leftrec
130-
.get(&imlparselet)
131-
.map_or(None, |leftrec| Some(*leftrec));
132-
133-
//println!("{:?} => {:?}", imlparselet.borrow().name, parselet.consuming);
134-
}
135-
136-
RefValue::from(parselet)
137-
} else {
138-
iml.unwrap()
139-
}
140-
})
127+
.map(|value| value.unwrap())
141128
.collect();
142129

143-
log::info!("{} has {} statics compiled", self.main, statics.len());
130+
log::info!("{:?} has {} statics compiled", statics[0], statics.len());
144131

145132
for (i, value) in statics.iter().enumerate() {
146133
log::trace!(" {: >3} : {:#?}", i, value);
@@ -156,11 +143,11 @@ impl ImlProgram {
156143
- nullable parselets
157144
- left-recursive parselets
158145
159-
until no more changes occur.
146+
until no more changes to these flag configurations occur.
160147
161148
It can only be run on a previously compiled program without any unresolved usages.
162149
*/
163-
fn finalize(&mut self, parselets: HashSet<ImlRefParselet>) -> HashMap<ImlRefParselet, bool> {
150+
fn finalize(&mut self) {
164151
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
165152
struct Consumable {
166153
leftrec: bool,
@@ -172,18 +159,18 @@ impl ImlProgram {
172159
value: &ImlValue,
173160
current: &ImlRefParselet,
174161
visited: &mut IndexSet<ImlRefParselet>,
175-
configs: &mut HashMap<ImlRefParselet, Consumable>,
162+
configs: &HashMap<ImlRefParselet, RefCell<Consumable>>,
176163
) -> Option<Consumable> {
177164
match value {
178165
ImlValue::Shared(value) => {
179166
finalize_value(&*value.borrow(), current, visited, configs)
180167
}
181168
ImlValue::SelfToken => {
182-
configs.get_mut(current).unwrap().leftrec = true;
169+
configs[current].borrow_mut().leftrec = true;
183170

184171
Some(Consumable {
185172
leftrec: true,
186-
nullable: configs[current].nullable,
173+
nullable: configs[current].borrow().nullable,
187174
})
188175
}
189176
ImlValue::Parselet(parselet) => {
@@ -221,7 +208,7 @@ impl ImlProgram {
221208
op: &ImlOp,
222209
current: &ImlRefParselet,
223210
visited: &mut IndexSet<ImlRefParselet>,
224-
configs: &mut HashMap<ImlRefParselet, Consumable>,
211+
configs: &HashMap<ImlRefParselet, RefCell<Consumable>>,
225212
) -> Option<Consumable> {
226213
match op {
227214
ImlOp::Call { target, .. } => finalize_value(target, current, visited, configs),
@@ -318,29 +305,25 @@ impl ImlProgram {
318305
fn finalize_parselet(
319306
current: &ImlRefParselet,
320307
visited: &mut IndexSet<ImlRefParselet>,
321-
configs: &mut HashMap<ImlRefParselet, Consumable>,
308+
configs: &HashMap<ImlRefParselet, RefCell<Consumable>>,
322309
) -> Option<Consumable> {
323310
// ... only if it's generally flagged to be consuming.
324311
let parselet = current.borrow();
325312
let model = parselet.model.borrow();
326313

327-
if !model.is_consuming {
328-
return None;
329-
}
330-
331314
//println!("- {}{}", ".".repeat(visited.len()), current);
332315

333316
if let Some(idx) = visited.get_index_of(current) {
334317
// When in visited, this is a recursion
335318
Some(Consumable {
336319
// If the idx is 0, current is the seeked parselet, so it is left-recursive
337320
leftrec: if idx == 0 && !current.borrow().is_generated {
338-
configs.get_mut(current).unwrap().leftrec = true;
321+
configs[current].borrow_mut().leftrec = true;
339322
true
340323
} else {
341324
false
342325
},
343-
nullable: configs[current].nullable,
326+
nullable: configs[current].borrow().nullable,
344327
})
345328
} else {
346329
// If not already visited, add and recurse.
@@ -354,52 +337,65 @@ impl ImlProgram {
354337

355338
Some(Consumable {
356339
leftrec: false,
357-
nullable: configs[current].nullable,
340+
nullable: configs[current].borrow().nullable,
358341
})
359342
}
360343
}
361344

362-
log::trace!(
363-
"{} has {} parselets to finalize",
364-
self.statics.keys()[0],
365-
parselets.len()
366-
);
367-
368-
// Now, start the closure algorithm with left-recursive and nullable configurations for all parselets
369-
// put into the finalize list.
345+
// Now, start the closure algorithm with left-recursive and nullable configurations
346+
// for all consumable parselets.
370347
let mut changes = true;
371-
let mut configs = parselets
372-
.iter()
348+
let configs: HashMap<ImlRefParselet, RefCell<Consumable>> = self
349+
.parselets
350+
.keys()
351+
.filter(|k| k.borrow().model.borrow().is_consuming)
373352
.map(|k| {
374353
(
375354
k.clone(),
376-
Consumable {
355+
RefCell::new(Consumable {
377356
leftrec: false,
378357
nullable: false,
379-
},
358+
}),
380359
)
381360
})
382361
.collect();
383362

363+
log::info!(
364+
"{:?} has {} parselets to finalize",
365+
self.statics[0],
366+
configs.len()
367+
);
368+
369+
for (i, parselet) in configs.keys().enumerate() {
370+
log::trace!(" {: >3} => {:#?}", i, parselet);
371+
}
372+
384373
while changes {
385374
changes = false;
386375

387-
for parselet in &parselets {
388-
let result = finalize_parselet(parselet, &mut IndexSet::new(), &mut configs);
389-
changes = result > configs.get(parselet).cloned();
376+
for parselet in configs.keys() {
377+
if let Some(result) = finalize_parselet(parselet, &mut IndexSet::new(), &configs) {
378+
changes = result > *configs[parselet].borrow();
379+
}
390380
}
391381
}
392382

393-
for parselet in &parselets {
394-
log::trace!(" {:?} consuming={:?}", parselet, configs[&parselet]);
383+
// set left recursion flags
384+
for (parselet, config) in configs {
385+
// get compiled parselet from statics
386+
let parselet = self.statics[self.parselets[&parselet]].as_ref().unwrap();
387+
388+
if let Some(parselet) = parselet.borrow().object::<ParseletRef>() {
389+
parselet.0.borrow_mut().consuming = Some(config.borrow().leftrec);
390+
}
391+
392+
log::trace!(" {:?} consuming={:?}", parselet, config);
395393
}
396394

397395
log::debug!(
398-
"{} has {} parselets finalized",
399-
self.statics.keys()[0],
400-
parselets.len()
396+
"{:?} has {} parselets finalized",
397+
self.statics[0],
398+
self.parselets.len()
401399
);
402-
403-
configs.into_iter().map(|(k, v)| (k, v.leftrec)).collect()
404400
}
405401
}

src/compiler/iml/imlvalue.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -404,13 +404,13 @@ impl ImlValue {
404404
// Register new static
405405
let idx = match self {
406406
ImlValue::Parselet(parselet) => match parselet.derive(current.0) {
407-
Ok(parselet) => program.register(&ImlValue::Parselet(parselet)),
407+
Ok(parselet) => program.register(&ImlValue::Parselet(parselet)).unwrap(),
408408
Err(msg) => {
409409
program.push_error(offset.clone(), msg);
410410
return;
411411
}
412412
},
413-
ImlValue::Value(_) => program.register(self),
413+
ImlValue::Value(_) => program.register(self).unwrap(),
414414
ImlValue::SelfToken | ImlValue::SelfValue => current.1,
415415
_ => unreachable!("Can't compile {:?}", self),
416416
};

0 commit comments

Comments
 (0)