378 lines
11 KiB
Rust
378 lines
11 KiB
Rust
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<Ref, types::TDefinition>,
|
|
pub literals: Map<IOValue, String>,
|
|
}
|
|
|
|
#[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<Item>,
|
|
pub functiondefs: Vec<Item>,
|
|
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<Capture>,
|
|
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<Item>),
|
|
}
|
|
|
|
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<F: FnOnce(&mut ModuleContext)>(
|
|
&mut self,
|
|
path: &Vec<String>,
|
|
schema: &Schema,
|
|
mode: ModuleContextMode,
|
|
items: &mut Map<ModuleContextMode, Vec<Item>>,
|
|
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<F: FnOnce(FunctionContext) -> 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<Item> {
|
|
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<Item>, 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<Item>, 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, F: FnOnce(&mut Self) -> 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<F: FnOnce(&mut Self) -> ()>(&mut self, f: F) -> Vec<Item> {
|
|
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, F: FnOnce(&mut Self) -> 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())]
|
|
]);
|
|
}
|
|
}
|