Use BTree{Map,Set} instead of Hash{Map,Set} to ensure deterministic output

Fixes #8
This commit is contained in:
Nick Fitzgerald
2021-10-12 11:04:17 -07:00
committed by Chris Fallin
parent efe7df7f45
commit 0e02ec4cba
4 changed files with 82 additions and 71 deletions

View File

@@ -4,11 +4,11 @@ use crate::ir::{ExprInst, InstId, PatternInst, Value};
use crate::sema::ExternalSig; use crate::sema::ExternalSig;
use crate::sema::{TermEnv, TermId, Type, TypeEnv, TypeId, Variant}; use crate::sema::{TermEnv, TermId, Type, TypeEnv, TypeId, Variant};
use crate::trie::{TrieEdge, TrieNode, TrieSymbol}; use crate::trie::{TrieEdge, TrieNode, TrieSymbol};
use std::collections::{HashMap, HashSet}; use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Write; use std::fmt::Write;
/// Emit Rust source code for the given type and term environments. /// Emit Rust source code for the given type and term environments.
pub fn codegen(typeenv: &TypeEnv, termenv: &TermEnv, tries: &HashMap<TermId, TrieNode>) -> String { pub fn codegen(typeenv: &TypeEnv, termenv: &TermEnv, tries: &BTreeMap<TermId, TrieNode>) -> String {
Codegen::compile(typeenv, termenv, tries).generate_rust() Codegen::compile(typeenv, termenv, tries).generate_rust()
} }
@@ -16,20 +16,20 @@ pub fn codegen(typeenv: &TypeEnv, termenv: &TermEnv, tries: &HashMap<TermId, Tri
struct Codegen<'a> { struct Codegen<'a> {
typeenv: &'a TypeEnv, typeenv: &'a TypeEnv,
termenv: &'a TermEnv, termenv: &'a TermEnv,
functions_by_term: &'a HashMap<TermId, TrieNode>, functions_by_term: &'a BTreeMap<TermId, TrieNode>,
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct BodyContext { struct BodyContext {
/// For each value: (is_ref, ty). /// For each value: (is_ref, ty).
values: HashMap<Value, (bool, TypeId)>, values: BTreeMap<Value, (bool, TypeId)>,
} }
impl<'a> Codegen<'a> { impl<'a> Codegen<'a> {
fn compile( fn compile(
typeenv: &'a TypeEnv, typeenv: &'a TypeEnv,
termenv: &'a TermEnv, termenv: &'a TermEnv,
tries: &'a HashMap<TermId, TrieNode>, tries: &'a BTreeMap<TermId, TrieNode>,
) -> Codegen<'a> { ) -> Codegen<'a> {
Codegen { Codegen {
typeenv, typeenv,
@@ -682,7 +682,7 @@ impl<'a> Codegen<'a> {
let mut i = 0; let mut i = 0;
while i < edges.len() { while i < edges.len() {
let mut last = i; let mut last = i;
let mut adjacent_variants = HashSet::new(); let mut adjacent_variants = BTreeSet::new();
let mut adjacent_variant_input = None; let mut adjacent_variant_input = None;
log::trace!("edge: {:?}", edges[i]); log::trace!("edge: {:?}", edges[i]);
while last < edges.len() { while last < edges.len() {

View File

@@ -2,7 +2,7 @@
use crate::lexer::Pos; use crate::lexer::Pos;
use crate::sema::*; use crate::sema::*;
use std::collections::HashMap; use std::collections::BTreeMap;
declare_id!( declare_id!(
/// The id of an instruction in a `PatternSequence`. /// The id of an instruction in a `PatternSequence`.
@@ -333,7 +333,7 @@ impl PatternSequence {
typeenv: &TypeEnv, typeenv: &TypeEnv,
termenv: &TermEnv, termenv: &TermEnv,
pat: &Pattern, pat: &Pattern,
vars: &mut HashMap<VarId, Value>, vars: &mut BTreeMap<VarId, Value>,
) { ) {
match pat { match pat {
&Pattern::BindPattern(_ty, var, ref subpat) => { &Pattern::BindPattern(_ty, var, ref subpat) => {
@@ -578,7 +578,7 @@ impl ExprSequence {
typeenv: &TypeEnv, typeenv: &TypeEnv,
termenv: &TermEnv, termenv: &TermEnv,
expr: &Expr, expr: &Expr,
vars: &HashMap<VarId, Value>, vars: &BTreeMap<VarId, Value>,
) -> Value { ) -> Value {
log::trace!("gen_expr: expr {:?}", expr); log::trace!("gen_expr: expr {:?}", expr);
match expr { match expr {
@@ -651,7 +651,7 @@ pub fn lower_rule(
expr_seq.pos = termenv.rules[rule.index()].pos; expr_seq.pos = termenv.rules[rule.index()].pos;
let ruledata = &termenv.rules[rule.index()]; let ruledata = &termenv.rules[rule.index()];
let mut vars = HashMap::new(); let mut vars = BTreeMap::new();
let root_term = ruledata let root_term = ruledata
.lhs .lhs
.root_term() .root_term()

View File

@@ -16,8 +16,8 @@
use crate::ast; use crate::ast;
use crate::error::*; use crate::error::*;
use crate::lexer::Pos; use crate::lexer::Pos;
use std::collections::HashMap; use std::collections::BTreeMap;
use std::collections::HashSet; use std::collections::BTreeSet;
use std::sync::Arc; use std::sync::Arc;
declare_id!( declare_id!(
@@ -70,7 +70,7 @@ pub struct TypeEnv {
pub syms: Vec<String>, pub syms: Vec<String>,
/// Map of already-interned symbol names to their `Sym` ids. /// Map of already-interned symbol names to their `Sym` ids.
pub sym_map: HashMap<String, Sym>, pub sym_map: BTreeMap<String, Sym>,
/// Arena of type definitions. /// Arena of type definitions.
/// ///
@@ -78,10 +78,10 @@ pub struct TypeEnv {
pub types: Vec<Type>, pub types: Vec<Type>,
/// A map from a type name symbol to its `TypeId`. /// A map from a type name symbol to its `TypeId`.
pub type_map: HashMap<Sym, TypeId>, pub type_map: BTreeMap<Sym, TypeId>,
/// The types of constant symbols. /// The types of constant symbols.
pub const_types: HashMap<Sym, TypeId>, pub const_types: BTreeMap<Sym, TypeId>,
/// Type errors that we've found so far during type checking. /// Type errors that we've found so far during type checking.
pub errors: Vec<Error>, pub errors: Vec<Error>,
@@ -181,7 +181,7 @@ pub struct TermEnv {
pub terms: Vec<Term>, pub terms: Vec<Term>,
/// A map from am interned `Term`'s name to its `TermId`. /// A map from am interned `Term`'s name to its `TermId`.
pub term_map: HashMap<Sym, TermId>, pub term_map: BTreeMap<Sym, TermId>,
/// Arena of interned rules defined in this ISLE program. /// Arena of interned rules defined in this ISLE program.
/// ///
@@ -544,10 +544,10 @@ impl TypeEnv {
filenames: defs.filenames.clone(), filenames: defs.filenames.clone(),
file_texts: defs.file_texts.clone(), file_texts: defs.file_texts.clone(),
syms: vec![], syms: vec![],
sym_map: HashMap::new(), sym_map: BTreeMap::new(),
types: vec![], types: vec![],
type_map: HashMap::new(), type_map: BTreeMap::new(),
const_types: HashMap::new(), const_types: BTreeMap::new(),
errors: vec![], errors: vec![],
}; };
@@ -753,7 +753,7 @@ impl TermEnv {
pub fn from_ast(tyenv: &mut TypeEnv, defs: &ast::Defs) -> Result<TermEnv> { pub fn from_ast(tyenv: &mut TypeEnv, defs: &ast::Defs) -> Result<TermEnv> {
let mut env = TermEnv { let mut env = TermEnv {
terms: vec![], terms: vec![],
term_map: HashMap::new(), term_map: BTreeMap::new(),
rules: vec![], rules: vec![],
}; };
@@ -935,7 +935,7 @@ impl TermEnv {
} }
fn collect_extractor_templates(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) { fn collect_extractor_templates(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {
let mut extractor_call_graph = HashMap::new(); let mut extractor_call_graph = BTreeMap::new();
for def in &defs.defs { for def in &defs.defs {
if let &ast::Def::Extractor(ref ext) = def { if let &ast::Def::Extractor(ref ext) = def {
@@ -954,7 +954,7 @@ impl TermEnv {
let template = ext.template.make_macro_template(&ext.args[..]); let template = ext.template.make_macro_template(&ext.args[..]);
log::trace!("extractor def: {:?} becomes template {:?}", def, template); log::trace!("extractor def: {:?} becomes template {:?}", def, template);
let mut callees = HashSet::new(); let mut callees = BTreeSet::new();
template.terms(&mut |pos, t| { template.terms(&mut |pos, t| {
let t = tyenv.intern_mut(t); let t = tyenv.intern_mut(t);
callees.insert(t); callees.insert(t);
@@ -1008,7 +1008,7 @@ impl TermEnv {
} }
// Check for cycles in the extractor call graph. // Check for cycles in the extractor call graph.
let mut seen = HashSet::new(); let mut seen = BTreeSet::new();
let mut stack = vec![]; let mut stack = vec![];
'outer: for root in extractor_call_graph.keys().copied() { 'outer: for root in extractor_call_graph.keys().copied() {
seen.clear(); seen.clear();
@@ -1828,7 +1828,7 @@ impl TermEnv {
mod test { mod test {
use super::*; use super::*;
use crate::ast::Ident; use crate::ast::Ident;
use crate::lexer::Lexer; use crate::lexer::{Lexer, Pos};
use crate::parser::parse; use crate::parser::parse;
#[test] #[test]
@@ -1868,51 +1868,62 @@ mod test {
assert_eq!(tyenv.type_map.get(&sym_u32).unwrap(), &TypeId(0)); assert_eq!(tyenv.type_map.get(&sym_u32).unwrap(), &TypeId(0));
assert_eq!(tyenv.type_map.get(&sym_a).unwrap(), &TypeId(1)); assert_eq!(tyenv.type_map.get(&sym_a).unwrap(), &TypeId(1));
assert_eq!( let expected_types = vec![
tyenv.types, Type::Primitive(
vec![ TypeId(0),
Type::Primitive(TypeId(0), sym_u32), sym_u32,
Type::Enum { Pos {
name: sym_a, file: 0,
id: TypeId(1), offset: 19,
is_extern: true, line: 2,
variants: vec![ col: 0,
Variant { },
name: sym_b, ),
fullname: sym_a_b, Type::Enum {
id: VariantId(0), name: sym_a,
fields: vec![ id: TypeId(1),
Field { is_extern: true,
name: sym_f1, variants: vec![
id: FieldId(0), Variant {
ty: TypeId(0), name: sym_b,
}, fullname: sym_a_b,
Field { id: VariantId(0),
name: sym_f2, fields: vec![
id: FieldId(1), Field {
ty: TypeId(0),
},
],
},
Variant {
name: sym_c,
fullname: sym_a_c,
id: VariantId(1),
fields: vec![Field {
name: sym_f1, name: sym_f1,
id: FieldId(0), id: FieldId(0),
ty: TypeId(0), ty: TypeId(0),
},], },
}, Field {
], name: sym_f2,
pos: Pos { id: FieldId(1),
file: 0, ty: TypeId(0),
offset: 58, },
line: 3, ],
col: 18,
}, },
Variant {
name: sym_c,
fullname: sym_a_c,
id: VariantId(1),
fields: vec![Field {
name: sym_f1,
id: FieldId(0),
ty: TypeId(0),
}],
},
],
pos: Pos {
file: 0,
offset: 58,
line: 3,
col: 0,
}, },
] },
); ];
assert_eq!(tyenv.types.len(), expected_types.len());
for (i, (actual, expected)) in tyenv.types.iter().zip(&expected_types).enumerate() {
assert_eq!(expected, actual, "`{}`th type is not equal!", i);
}
} }
} }

View File

@@ -2,10 +2,10 @@
use crate::ir::{lower_rule, ExprSequence, PatternInst, PatternSequence}; use crate::ir::{lower_rule, ExprSequence, PatternInst, PatternSequence};
use crate::sema::{RuleId, TermEnv, TermId, TypeEnv}; use crate::sema::{RuleId, TermEnv, TermId, TypeEnv};
use std::collections::HashMap; use std::collections::BTreeMap;
/// Construct the tries for each term. /// Construct the tries for each term.
pub fn build_tries(typeenv: &TypeEnv, termenv: &TermEnv) -> HashMap<TermId, TrieNode> { pub fn build_tries(typeenv: &TypeEnv, termenv: &TermEnv) -> BTreeMap<TermId, TrieNode> {
let mut builder = TermFunctionsBuilder::new(typeenv, termenv); let mut builder = TermFunctionsBuilder::new(typeenv, termenv);
builder.build(); builder.build();
log::trace!("builder: {:?}", builder); log::trace!("builder: {:?}", builder);
@@ -511,7 +511,7 @@ impl TermFunctionBuilder {
struct TermFunctionsBuilder<'a> { struct TermFunctionsBuilder<'a> {
typeenv: &'a TypeEnv, typeenv: &'a TypeEnv,
termenv: &'a TermEnv, termenv: &'a TermEnv,
builders_by_term: HashMap<TermId, TermFunctionBuilder>, builders_by_term: BTreeMap<TermId, TermFunctionBuilder>,
} }
impl<'a> TermFunctionsBuilder<'a> { impl<'a> TermFunctionsBuilder<'a> {
@@ -519,7 +519,7 @@ impl<'a> TermFunctionsBuilder<'a> {
log::trace!("typeenv: {:?}", typeenv); log::trace!("typeenv: {:?}", typeenv);
log::trace!("termenv: {:?}", termenv); log::trace!("termenv: {:?}", termenv);
Self { Self {
builders_by_term: HashMap::new(), builders_by_term: BTreeMap::new(),
typeenv, typeenv,
termenv, termenv,
} }
@@ -546,12 +546,12 @@ impl<'a> TermFunctionsBuilder<'a> {
} }
} }
fn finalize(self) -> HashMap<TermId, TrieNode> { fn finalize(self) -> BTreeMap<TermId, TrieNode> {
let functions_by_term = self let functions_by_term = self
.builders_by_term .builders_by_term
.into_iter() .into_iter()
.map(|(term, builder)| (term, builder.trie)) .map(|(term, builder)| (term, builder.trie))
.collect::<HashMap<_, _>>(); .collect::<BTreeMap<_, _>>();
functions_by_term functions_by_term
} }
} }