use crate::gen::schema::*; use crate::syntax::block::constructors::*; use crate::syntax::block::escape_string; use crate::syntax::block::Item; use crate::*; use convert_case::{Case, Casing}; use lazy_static::lazy_static; use preserves::value::IOValue; use preserves::value::Map; use preserves::value::NestedValue; use preserves::value::Value; use super::names; use super::types; use super::types::Purpose; use super::CompilerConfig; pub struct BundleContext<'b> { pub config: &'b CompilerConfig, pub types: Map, pub literals: Map, } #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] pub enum ModuleContextMode { TargetModule, TargetToplevel, TargetGeneric, } pub struct ModuleContext<'m, 'b> { pub bundle: &'m mut BundleContext<'b>, pub module_path: ModulePath, pub schema: &'m Schema, pub typedefs: Vec, pub functiondefs: Vec, pub mode: ModuleContextMode, } pub struct FunctionContext<'a, 'm, 'b> { pub error_context: String, pub m: &'a mut ModuleContext<'m, 'b>, pub temp_counter: usize, pub captures: Vec, pub capture_mode: CaptureMode, } pub struct Capture { pub field_name: String, pub ty: types::TField, pub source_expr: String, } pub enum CaptureMode { Definite, Indefinite(Vec), } pub enum RefRenderStyle { Bare, Qualified, } lazy_static! { static ref ID_RE: regex::Regex = regex::Regex::new(r"^[a-zA-Z][a-zA-Z_0-9]*$").unwrap(); } impl<'b> BundleContext<'b> { pub fn new(config: &'b CompilerConfig) -> Self { BundleContext { config, types: config.build_type_cache(), literals: Map::new(), } } pub fn any_type(&self) -> &'static str { "_Value" } pub fn lookup_definition(&self, r: &Ref) -> Option<(&Definition, Purpose)> { self.config .bundle .get(&r.module.0) .and_then(|s| s.0.definitions.0.get(&r.name).map(|d| (d, s.1))) } pub fn type_for_name(&self, r: &Ref) -> Option<&types::TDefinition> { if r.module.0.is_empty() { panic!( "BundleContext::type_for_name with module-relative ref {:?}", r ); } let result = self.types.get(r); if result.is_none() && !self.config.external_modules.contains_key(&r.module.0) { panic!("Attempted to lookup unknown type {:?}", r) } result } pub fn define_literal(&mut self, v: &IOValue) -> String { let prefix = format!("LIT_{}", self.literals.len()); let next_id = match v.value() { Value::Boolean(b) => prefix + "_" + &b.to_string(), Value::Symbol(s) => { if ID_RE.is_match(&s) { prefix + "_" + s } else { prefix } } Value::String(s) => { if ID_RE.is_match(&s) { prefix + "_" + s } else { prefix } } Value::SignedInteger(n) => prefix + "_" + &n.to_string(), _ => prefix, }; let next_id = next_id.to_case(Case::UpperSnake); format!( "&<_L as Into<&'a {}>>::into(_ctxt).{}", self.language_type(), self.literals.entry(v.clone()).or_insert(next_id) ) } pub fn generate_module( &mut self, path: &Vec, schema: &Schema, mode: ModuleContextMode, items: &mut Map>, f: F, ) { let mut m = ModuleContext::new(self, &ModulePath(path.clone()), schema, mode); f(&mut m); items.entry(mode).or_default().extend(m.extract()); } pub fn language_struct_name(&self) -> &'static str { "Language" } pub fn language_type_base(&self) -> String { format!( "{}::{}", self.config.fully_qualified_module_prefix.clone(), self.language_struct_name() ) } pub fn language_type(&self) -> String { format!("{}<{}>", self.language_type_base(), self.any_type()) } } impl<'m, 'b> ModuleContext<'m, 'b> { pub fn new( bundle: &'m mut BundleContext<'b>, module_path: &ModulePath, schema: &'m Schema, mode: ModuleContextMode, ) -> Self { ModuleContext { bundle, module_path: module_path.to_owned(), schema, typedefs: Vec::new(), functiondefs: Vec::new(), mode, } } pub fn any_type(&self) -> &'static str { self.bundle.any_type() } pub fn reset_mode(&mut self) { self.mode = ModuleContextMode::TargetToplevel; } pub fn define_literal(&mut self, v: &IOValue) -> String { self.bundle.define_literal(v) } pub fn define_type(&mut self, i: Item) { self.typedefs.push(i) } pub fn define_function Item>( &mut self, error_context: &str, f: F, ) { let i = f(FunctionContext::new(self, error_context)); self.functiondefs.push(i) } pub fn render_ref(&self, r: &Ref, style: RefRenderStyle) -> Item { let base = match self.bundle.config.external_modules.get(&r.module.0) { None => { if r.module.0.is_empty() { item(names::render_constructor(&r.name)) } else { let mut items = Vec::new(); items.push(item( self.bundle.config.fully_qualified_module_prefix.to_owned(), )); for p in &r.module.0 { items.push(item(names::render_modname(p))) } items.push(item(names::render_constructor(&r.name))); item(name(items)) } } Some(xm) => item(name![ xm.rust_namespace.clone(), names::render_constructor(&r.name) ]), }; let q = self.ref_has_embedded(r); match style { RefRenderStyle::Bare => base, RefRenderStyle::Qualified => { if q { item(seq![base, anglebrackets![self.any_type()]]) } else { base } } } } pub fn ref_has_embedded(&self, r: &Ref) -> bool { let r = r.qualify(&self.module_path); self.bundle .type_for_name(&r) .map(|ty| ty.has_embedded(self.bundle)) .unwrap_or(false) // ^ TODO: should the "false" be configurable? } pub fn parse_unparse_generic_decls(&self, ty: &types::TDefinition) -> Item { let mut lts = ty.language_types(self.bundle); lts.insert(self.bundle.language_type()); item(anglebrackets![ "'a", seq![ "_L: Copy", seq(lts .into_iter() .map(|t| item(seq![" + Into<&'a ", t, ">"])) .collect()) ], seq![self.any_type(), ": preserves::value::NestedValue + 'a"] ]) } pub fn extract(&mut self) -> Vec { let mut items = std::mem::take(&mut self.typedefs); items.extend(std::mem::take(&mut self.functiondefs)); items } } impl<'a, 'm, 'b> FunctionContext<'a, 'm, 'b> { pub fn new(m: &'a mut ModuleContext<'m, 'b>, error_context: &str) -> Self { FunctionContext { error_context: error_context.to_owned(), m, temp_counter: 0, captures: Vec::new(), capture_mode: CaptureMode::Definite, } } pub fn capture(&mut self, field_name: String, ty: types::TField, source_expr: String) { self.captures.push(Capture { field_name, ty, source_expr: match self.capture_mode { CaptureMode::Definite => source_expr, CaptureMode::Indefinite(_) => format!( "{}.ok_or_else(|| {:?})?", source_expr, self.conformance_err_code() ), }, }) } pub fn lookup_capture(&self, field_name: &str) -> &Capture { for c in &self.captures { if c.field_name == field_name { return c; } } panic!("No capture for field {:?} available", field_name) } pub fn gentempname(&mut self) -> String { let i = self.temp_counter; self.temp_counter += 1; format!("_tmp{}", i) } pub fn declare_compound(&self, body: &mut Vec, name: &str, init_expr: Item) { body.push(item(seq![ "let mut ", name.to_owned(), " = ", init_expr, ";" ])); } pub fn define_atom(&mut self, body: &mut Vec, name: &str, val_expr: Item) { body.push(match &mut self.capture_mode { CaptureMode::Definite => item(seq!["let ", name.to_owned(), " = ", val_expr, ";"]), CaptureMode::Indefinite(items) => { items.push(item(seq!["let mut ", name.to_owned(), " = None;"])); item(seq![name.to_owned(), " = Some(", val_expr, ");"]) } }) } pub fn with_definite_mode R>(&mut self, f: F) -> R { let saved_mode = std::mem::replace(&mut self.capture_mode, CaptureMode::Definite); let result = f(self); match std::mem::replace(&mut self.capture_mode, saved_mode) { CaptureMode::Definite => (), CaptureMode::Indefinite(_) => panic!("corrupt capture_mode"), } result } pub fn with_indefinite_mode ()>(&mut self, f: F) -> Vec { let saved_mode = std::mem::replace(&mut self.capture_mode, CaptureMode::Indefinite(Vec::new())); f(self); match std::mem::replace(&mut self.capture_mode, saved_mode) { CaptureMode::Definite => panic!("corrupt capture_mode"), CaptureMode::Indefinite(declarations) => declarations, } } pub fn branch R>(&mut self, f: F) -> R { let saved_temp_counter = self.temp_counter; let saved_capture_count = self.captures.len(); let result = f(self); self.temp_counter = saved_temp_counter; self.captures.truncate(saved_capture_count); result } pub fn err_code(&self) -> Item { return item(seq!["Err", parens![self.conformance_err_code()]]); } pub fn fully_qualified_error_context(&self) -> String { self.m.module_path.0.join(".") + "." + &self.error_context } pub fn conformance_err_code(&self) -> Item { return item(seq![ "_support::ParseError::conformance_error", parens![escape_string(&self.fully_qualified_error_context())] ]); } }