forked from syndicate-lang/preserves
314 lines
9.8 KiB
Rust
314 lines
9.8 KiB
Rust
use crate::*;
|
|
use crate::syntax::block::Item;
|
|
use crate::syntax::block::escape_string;
|
|
use crate::syntax::block::constructors::*;
|
|
use crate::gen::schema::*;
|
|
|
|
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::CompilerConfig;
|
|
use super::names;
|
|
use super::types;
|
|
|
|
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 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.module_aliases.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!("&_ctxt.{}", 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());
|
|
}
|
|
}
|
|
|
|
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 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.module_aliases.get(&r.module.0) {
|
|
None =>
|
|
if r.module.0.is_empty() {
|
|
item(r.name.to_owned())
|
|
} 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(r.name.to_owned()));
|
|
item(name(items))
|
|
}
|
|
Some(s) =>
|
|
item(name![s.to_owned(), r.name.to_owned()])
|
|
};
|
|
let q = self.ref_has_embedded(r);
|
|
match style {
|
|
RefRenderStyle::Bare =>
|
|
base,
|
|
RefRenderStyle::Qualified =>
|
|
if q { item(seq![base, anglebrackets!["_Value"]]) } else { base },
|
|
}
|
|
}
|
|
|
|
pub fn any_type(&self) -> &'static str {
|
|
"_Value"
|
|
}
|
|
|
|
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 generic_decls(&self) -> Item {
|
|
item(anglebrackets!["_Value: preserves::value::NestedValue"])
|
|
}
|
|
|
|
pub fn generic_decls_with_defaults(&self) -> Item {
|
|
item(anglebrackets![
|
|
seq!["_Value: preserves::value::NestedValue = ",
|
|
if self.schema.embedded_type == EmbeddedTypeName::False {
|
|
"preserves::value::IOValue"
|
|
} else {
|
|
"_Any"
|
|
}]])
|
|
}
|
|
|
|
pub fn generic_arg(&self) -> Item {
|
|
item("<_Value>")
|
|
}
|
|
|
|
pub fn language_type(&self) -> Item {
|
|
item(seq![self.language_base_type(), self.generic_arg()])
|
|
}
|
|
|
|
pub fn language_base_type(&self) -> Item {
|
|
item(format!("{}::Language", self.bundle.config.fully_qualified_module_prefix))
|
|
}
|
|
|
|
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: 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: field_name,
|
|
ty: 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())]]);
|
|
}
|
|
}
|