improvement: re-use the same context for multiple file compiles.

This commit is contained in:
James Harton 2022-08-29 19:09:31 +12:00
parent c12177947e
commit eefe3a606e
7 changed files with 138 additions and 52 deletions

View file

@ -1,13 +1,14 @@
use super::{Error, Function, Notice, NoticeKind, NoticeLevel, Type, TypeVariable};
use crate::source::Source;
use crate::span::Span;
use crate::unique_id::UniqueIdGenerator;
use outrun_common::{Stack, StackError};
use std::cell::RefCell;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Context {
pub source: Arc<Source>,
pub source: Option<Arc<Source>>,
pub types: Vec<Arc<Type>>,
pub functions: Vec<Arc<Function>>,
pub docs: Stack<String>,
@ -15,24 +16,29 @@ pub struct Context {
pub current_type: Stack<Arc<Type>>,
pub self_type: Stack<Arc<Type>>,
pub current_function: Stack<Arc<Function>>,
type_id: u64,
type_id: UniqueIdGenerator,
}
impl Context {
pub fn new(source: Arc<Source>) -> Context {
pub fn new() -> Context {
bootstrap(Context {
source,
source: None,
types: Vec::new(),
functions: Vec::new(),
docs: Stack::new(),
notices: Vec::new(),
type_id: 0,
type_id: UniqueIdGenerator::new(),
current_type: Stack::new(),
self_type: Stack::new(),
current_function: Stack::new(),
})
}
pub fn with_source(mut self, source: Arc<Source>) -> Self {
self.source = Some(source);
self
}
pub fn find_named_type<T: ToString>(&self, name: T) -> Result<Arc<Type>, Error> {
let name = name.to_string();
@ -59,8 +65,7 @@ impl Context {
}
pub fn unbound_type(&mut self) -> Result<Arc<Type>, Error> {
let type_id = self.type_id;
self.type_id += 1;
let type_id = self.type_id.next();
let r#type = RefCell::new(TypeVariable::Unbound { id: type_id });
let r#type = Arc::new(Type::Variable { r#type, span: None });
self.types.push(r#type.clone());
@ -108,31 +113,47 @@ impl Context {
.ok_or_else(|| StackError::Underflow.into())
}
pub fn info(&mut self, span: Option<Span>, kind: NoticeKind) {
self.notices.push(Notice {
source: self.source.clone(),
pub fn info(&mut self, span: Option<Span>, kind: NoticeKind) -> Result<(), Error> {
let source = self.assert_has_source()?;
let notice = Notice {
source: source,
level: NoticeLevel::Info,
kind,
span,
})
};
self.notices.push(notice);
Ok(())
}
pub fn warning(&mut self, span: Option<Span>, kind: NoticeKind) {
self.notices.push(Notice {
source: self.source.clone(),
pub fn warning(&mut self, span: Option<Span>, kind: NoticeKind) -> Result<(), Error> {
let source = self.assert_has_source()?;
let notice = Notice {
source: source,
level: NoticeLevel::Warning,
kind,
span,
})
};
self.notices.push(notice);
Ok(())
}
pub fn error(&mut self, span: Option<Span>, kind: NoticeKind) {
self.notices.push(Notice {
source: self.source.clone(),
pub fn error(&mut self, span: Option<Span>, kind: NoticeKind) -> Result<(), Error> {
let source = self.assert_has_source()?;
let notice = Notice {
source: source,
level: NoticeLevel::Error,
kind,
span,
})
};
self.notices.push(notice);
Ok(())
}
pub fn search_type<F>(&self, predicate: F) -> Option<Arc<Type>>
@ -141,6 +162,17 @@ impl Context {
{
self.types.iter().find(|t| predicate((*t).clone())).cloned()
}
fn assert_has_source(&self) -> Result<Arc<Source>, Error> {
if let Some(source) = self.source.as_ref() {
return Ok(source.clone());
}
Err(Error::InternalError(
"Attempt use context without source".to_string(),
None,
))
}
}
fn bootstrap(mut module: Context) -> Context {

View file

@ -24,6 +24,8 @@ pub enum Error {
},
#[error("{0}")]
Stack(#[from] StackError),
#[error("Internal compiler error: {0}")]
InternalError(String, Option<Span>),
}
impl Error {
@ -33,6 +35,7 @@ impl Error {
Error::UnknownType(_) => None,
Error::MissingProperty { span, .. } => Some(*span),
Error::Stack(_) => None,
Error::InternalError(_, span) => *span,
}
}
}

View file

@ -17,7 +17,7 @@ pub fn link_type_references(mut context: Context) -> Result<Context, Error> {
if let Some(target) = maybe_type {
ty.link_to(target);
} else {
context.error(ty.span(), NoticeKind::CannotResolveType(target_name));
context.error(ty.span(), NoticeKind::CannotResolveType(target_name))?;
}
}

View file

@ -81,7 +81,7 @@ fn eval_type_union(mut context: Context, node: Node) -> Result<(Context, Arc<Typ
} else if name.starts_with("Self.") {
let (mut context, parent) = self_type(context)?;
if parent.is_named() {
let name = parent.to_string();
let name = name.replace("Self", &parent.to_string());
let ty = context.reference_type(name, Some(node.span))?;
Ok((context, ty))
} else {
@ -236,7 +236,7 @@ fn visit_atom(mut context: Context, node: Node) -> Result<(Context, Expression),
context.info(
Some(node.span),
NoticeKind::UnconventionalAtom(value.clone(), suggestion),
)
)?;
}
let value = ExpressionValue::Atom(value);
@ -347,7 +347,7 @@ fn visit_function_statement(mut context: Context, node: Node) -> Result<Context,
context.info(
Some(node.span),
NoticeKind::UnconventionalFunctionName(name.clone(), suggestion),
);
)?;
}
let associated_type = context.current_type.peek().cloned()?;
@ -361,7 +361,7 @@ fn visit_function_statement(mut context: Context, node: Node) -> Result<Context,
context.info(
Some(node.span),
NoticeKind::UnconventionalKeyword(key.clone(), suggestion),
);
)?;
}
argument_names.push(key);
@ -447,7 +447,7 @@ fn visit_function_statement(mut context: Context, node: Node) -> Result<Context,
context.warning(
Some(span),
NoticeKind::UnexpectedFunctionProperty(key.to_string()),
);
)?;
}
Ok(context)
@ -467,7 +467,7 @@ fn visit_get_local(mut context: Context, node: Node) -> Result<(Context, Express
context.info(
Some(span),
NoticeKind::UnconventionalVariableName(local.clone(), suggestion),
)
)?;
}
let r#type = context.unbound_type()?;
@ -534,7 +534,7 @@ fn visit_impl(mut context: Context, node: Node) -> Result<(Context, Arc<Type>),
context.warning(
Some(node.span),
NoticeKind::UnexpectedImplProperty(key.to_string()),
);
)?;
}
Ok((context, ty))
@ -556,7 +556,7 @@ fn visit_index(context: Context, node: Node) -> Result<(Context, Expression), Er
context.info(
Some(node.span),
NoticeKind::UnconventionalFieldName(field_name.clone(), suggestion),
);
)?;
}
let value = ExpressionValue::Index {
@ -644,7 +644,7 @@ fn visit_let_expression(mut context: Context, node: Node) -> Result<(Context, Ex
context.info(
Some(span),
NoticeKind::UnconventionalVariableName(name.clone(), suggestion),
);
)?;
}
let expression_type;
@ -792,7 +792,7 @@ fn visit_protocol(mut context: Context, node: Node) -> Result<(Context, Arc<Type
context.warning(
Some(node.span),
NoticeKind::UnexpectedProtocolProperty(key.to_string()),
);
)?;
}
Ok((context, ty))
@ -882,7 +882,7 @@ fn visit_struct(mut context: Context, node: Node) -> Result<(Context, Arc<Type>)
context.info(
Some(node.span),
NoticeKind::UnconventionalFieldName(key.clone(), suggestion),
);
)?;
}
let (new_context, value) = eval_type_union(context, value.clone())?;
@ -921,7 +921,7 @@ fn visit_struct(mut context: Context, node: Node) -> Result<(Context, Arc<Type>)
context.warning(
Some(node.span),
NoticeKind::UnexpectedStructProperty(key.to_string()),
);
)?;
}
Ok((context, r#type))
@ -988,7 +988,7 @@ fn visit_use_statement(mut context: Context, node: Node) -> Result<Context, Erro
context.warning(
Some(node.span),
NoticeKind::UnexpectedUseProperty(key.to_string()),
);
)?;
}
if let Some(alias) = props.get("as") {
@ -1013,7 +1013,7 @@ fn typename_to_string(context: &mut Context, node: &Node) -> Result<String, Erro
context.info(
Some(node.span),
NoticeKind::UnconventionalTypeName(value.clone(), suggestion),
);
)?;
}
Ok(value)

View file

@ -19,6 +19,7 @@ mod grammar;
mod ir;
mod source;
mod span;
mod unique_id;
use std::sync::Arc;
@ -27,10 +28,10 @@ pub use ir::Context;
pub use ir::Type;
pub use source::Source;
pub fn compile(source: Arc<Source>) -> Result<Context, Error> {
pub fn compile(context: Context, source: Arc<Source>) -> Result<Context, Error> {
let ast = grammar::parse(source.clone(), None).map_err(|e| (source.clone(), e))?;
let context = Context::new(source.clone());
let context = context.with_source(source.clone());
let context = ir::visit_nodes(context, ast).map_err(|e| (source.clone(), e))?;
let context = ir::improve(context).map_err(|e| (source.clone(), e))?;

View file

@ -0,0 +1,53 @@
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
/// A unique identifier generator.
///
/// Blatantly stolen from https://github.com/gleam-lang/gleam/blob/main/compiler-core/src/uid.rs
/// A generator of unique ids. Only one should be used per compilation run to
/// ensure ids do not get reused.
#[derive(Debug, Clone, Default)]
pub struct UniqueIdGenerator {
id: Arc<AtomicU64>,
}
impl UniqueIdGenerator {
/// Fork the id generator into a new one that does not share the same
/// internal id. It will increment independently. Not to be used in the same
/// compilation run.
pub fn fork(&self) -> Self {
let current = self.id.load(Ordering::Relaxed);
let id = AtomicU64::new(current);
Self { id: Arc::new(id) }
}
pub fn new() -> Self {
Self::default()
}
pub fn next(&self) -> u64 {
self.id.fetch_add(1, Ordering::Relaxed)
}
}
#[test]
fn id_generation() {
let ids = UniqueIdGenerator::new();
let ids2 = ids.clone();
assert_eq!(ids.next(), 0);
assert_eq!(ids.next(), 1);
assert_eq!(ids.next(), 2);
// Cloned ones use the same counter
assert_eq!(ids2.next(), 3);
assert_eq!(ids2.next(), 4);
assert_eq!(ids2.next(), 5);
// The original is updated
assert_eq!(ids.next(), 6);
assert_eq!(ids.next(), 7);
}

View file

@ -6,23 +6,22 @@ use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
pub fn init() -> Result<Vec<Context>, Error> {
pub fn init() -> Result<Context, Error> {
let path = Path::new(env!("CARGO_MANIFEST_DIR"));
let path = path.join("lib");
let path = path.as_path();
let files = collect_files(path, Vec::new())?;
let mut results = Vec::new();
let mut context = Context::new();
for path in files.iter().take(1) {
let source = Source::from_file(path)?;
let source = Arc::new(source);
let context = outrun_compiler::compile(source)?;
results.push(context);
context = outrun_compiler::compile(context, source)?;
}
Ok(results)
Ok(context)
}
fn collect_files(path: &Path, mut files: Vec<PathBuf>) -> Result<Vec<PathBuf>, Error> {
@ -52,11 +51,10 @@ mod test {
#[test]
fn test_init() -> Result<()> {
let results = init()?;
let context = init()?;
// println!("results: {:#?}", results);
for context in results {
let unbound_types: Vec<Arc<Type>> = context
.types
.iter()
@ -66,7 +64,6 @@ mod test {
println!("unbound_types: {:#?}", unbound_types);
assert_eq!(unbound_types.len(), 0);
}
Ok(())
}