improvement: re-use the same context for multiple file compiles.
This commit is contained in:
parent
c12177947e
commit
eefe3a606e
7 changed files with 138 additions and 52 deletions
|
@ -1,13 +1,14 @@
|
||||||
use super::{Error, Function, Notice, NoticeKind, NoticeLevel, Type, TypeVariable};
|
use super::{Error, Function, Notice, NoticeKind, NoticeLevel, Type, TypeVariable};
|
||||||
use crate::source::Source;
|
use crate::source::Source;
|
||||||
use crate::span::Span;
|
use crate::span::Span;
|
||||||
|
use crate::unique_id::UniqueIdGenerator;
|
||||||
use outrun_common::{Stack, StackError};
|
use outrun_common::{Stack, StackError};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub source: Arc<Source>,
|
pub source: Option<Arc<Source>>,
|
||||||
pub types: Vec<Arc<Type>>,
|
pub types: Vec<Arc<Type>>,
|
||||||
pub functions: Vec<Arc<Function>>,
|
pub functions: Vec<Arc<Function>>,
|
||||||
pub docs: Stack<String>,
|
pub docs: Stack<String>,
|
||||||
|
@ -15,24 +16,29 @@ pub struct Context {
|
||||||
pub current_type: Stack<Arc<Type>>,
|
pub current_type: Stack<Arc<Type>>,
|
||||||
pub self_type: Stack<Arc<Type>>,
|
pub self_type: Stack<Arc<Type>>,
|
||||||
pub current_function: Stack<Arc<Function>>,
|
pub current_function: Stack<Arc<Function>>,
|
||||||
type_id: u64,
|
type_id: UniqueIdGenerator,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new(source: Arc<Source>) -> Context {
|
pub fn new() -> Context {
|
||||||
bootstrap(Context {
|
bootstrap(Context {
|
||||||
source,
|
source: None,
|
||||||
types: Vec::new(),
|
types: Vec::new(),
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
docs: Stack::new(),
|
docs: Stack::new(),
|
||||||
notices: Vec::new(),
|
notices: Vec::new(),
|
||||||
type_id: 0,
|
type_id: UniqueIdGenerator::new(),
|
||||||
current_type: Stack::new(),
|
current_type: Stack::new(),
|
||||||
self_type: Stack::new(),
|
self_type: Stack::new(),
|
||||||
current_function: 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> {
|
pub fn find_named_type<T: ToString>(&self, name: T) -> Result<Arc<Type>, Error> {
|
||||||
let name = name.to_string();
|
let name = name.to_string();
|
||||||
|
|
||||||
|
@ -59,8 +65,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbound_type(&mut self) -> Result<Arc<Type>, Error> {
|
pub fn unbound_type(&mut self) -> Result<Arc<Type>, Error> {
|
||||||
let type_id = self.type_id;
|
let type_id = self.type_id.next();
|
||||||
self.type_id += 1;
|
|
||||||
let r#type = RefCell::new(TypeVariable::Unbound { id: type_id });
|
let r#type = RefCell::new(TypeVariable::Unbound { id: type_id });
|
||||||
let r#type = Arc::new(Type::Variable { r#type, span: None });
|
let r#type = Arc::new(Type::Variable { r#type, span: None });
|
||||||
self.types.push(r#type.clone());
|
self.types.push(r#type.clone());
|
||||||
|
@ -108,31 +113,47 @@ impl Context {
|
||||||
.ok_or_else(|| StackError::Underflow.into())
|
.ok_or_else(|| StackError::Underflow.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn info(&mut self, span: Option<Span>, kind: NoticeKind) {
|
pub fn info(&mut self, span: Option<Span>, kind: NoticeKind) -> Result<(), Error> {
|
||||||
self.notices.push(Notice {
|
let source = self.assert_has_source()?;
|
||||||
source: self.source.clone(),
|
|
||||||
|
let notice = Notice {
|
||||||
|
source: source,
|
||||||
level: NoticeLevel::Info,
|
level: NoticeLevel::Info,
|
||||||
kind,
|
kind,
|
||||||
span,
|
span,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
self.notices.push(notice);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn warning(&mut self, span: Option<Span>, kind: NoticeKind) {
|
pub fn warning(&mut self, span: Option<Span>, kind: NoticeKind) -> Result<(), Error> {
|
||||||
self.notices.push(Notice {
|
let source = self.assert_has_source()?;
|
||||||
source: self.source.clone(),
|
|
||||||
|
let notice = Notice {
|
||||||
|
source: source,
|
||||||
level: NoticeLevel::Warning,
|
level: NoticeLevel::Warning,
|
||||||
kind,
|
kind,
|
||||||
span,
|
span,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
self.notices.push(notice);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(&mut self, span: Option<Span>, kind: NoticeKind) {
|
pub fn error(&mut self, span: Option<Span>, kind: NoticeKind) -> Result<(), Error> {
|
||||||
self.notices.push(Notice {
|
let source = self.assert_has_source()?;
|
||||||
source: self.source.clone(),
|
|
||||||
|
let notice = Notice {
|
||||||
|
source: source,
|
||||||
level: NoticeLevel::Error,
|
level: NoticeLevel::Error,
|
||||||
kind,
|
kind,
|
||||||
span,
|
span,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
self.notices.push(notice);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_type<F>(&self, predicate: F) -> Option<Arc<Type>>
|
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()
|
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 {
|
fn bootstrap(mut module: Context) -> Context {
|
||||||
|
|
|
@ -24,6 +24,8 @@ pub enum Error {
|
||||||
},
|
},
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Stack(#[from] StackError),
|
Stack(#[from] StackError),
|
||||||
|
#[error("Internal compiler error: {0}")]
|
||||||
|
InternalError(String, Option<Span>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
@ -33,6 +35,7 @@ impl Error {
|
||||||
Error::UnknownType(_) => None,
|
Error::UnknownType(_) => None,
|
||||||
Error::MissingProperty { span, .. } => Some(*span),
|
Error::MissingProperty { span, .. } => Some(*span),
|
||||||
Error::Stack(_) => None,
|
Error::Stack(_) => None,
|
||||||
|
Error::InternalError(_, span) => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub fn link_type_references(mut context: Context) -> Result<Context, Error> {
|
||||||
if let Some(target) = maybe_type {
|
if let Some(target) = maybe_type {
|
||||||
ty.link_to(target);
|
ty.link_to(target);
|
||||||
} else {
|
} else {
|
||||||
context.error(ty.span(), NoticeKind::CannotResolveType(target_name));
|
context.error(ty.span(), NoticeKind::CannotResolveType(target_name))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ fn eval_type_union(mut context: Context, node: Node) -> Result<(Context, Arc<Typ
|
||||||
} else if name.starts_with("Self.") {
|
} else if name.starts_with("Self.") {
|
||||||
let (mut context, parent) = self_type(context)?;
|
let (mut context, parent) = self_type(context)?;
|
||||||
if parent.is_named() {
|
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))?;
|
let ty = context.reference_type(name, Some(node.span))?;
|
||||||
Ok((context, ty))
|
Ok((context, ty))
|
||||||
} else {
|
} else {
|
||||||
|
@ -236,7 +236,7 @@ fn visit_atom(mut context: Context, node: Node) -> Result<(Context, Expression),
|
||||||
context.info(
|
context.info(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnconventionalAtom(value.clone(), suggestion),
|
NoticeKind::UnconventionalAtom(value.clone(), suggestion),
|
||||||
)
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = ExpressionValue::Atom(value);
|
let value = ExpressionValue::Atom(value);
|
||||||
|
@ -347,7 +347,7 @@ fn visit_function_statement(mut context: Context, node: Node) -> Result<Context,
|
||||||
context.info(
|
context.info(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnconventionalFunctionName(name.clone(), suggestion),
|
NoticeKind::UnconventionalFunctionName(name.clone(), suggestion),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let associated_type = context.current_type.peek().cloned()?;
|
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(
|
context.info(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnconventionalKeyword(key.clone(), suggestion),
|
NoticeKind::UnconventionalKeyword(key.clone(), suggestion),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
argument_names.push(key);
|
argument_names.push(key);
|
||||||
|
@ -447,7 +447,7 @@ fn visit_function_statement(mut context: Context, node: Node) -> Result<Context,
|
||||||
context.warning(
|
context.warning(
|
||||||
Some(span),
|
Some(span),
|
||||||
NoticeKind::UnexpectedFunctionProperty(key.to_string()),
|
NoticeKind::UnexpectedFunctionProperty(key.to_string()),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(context)
|
Ok(context)
|
||||||
|
@ -467,7 +467,7 @@ fn visit_get_local(mut context: Context, node: Node) -> Result<(Context, Express
|
||||||
context.info(
|
context.info(
|
||||||
Some(span),
|
Some(span),
|
||||||
NoticeKind::UnconventionalVariableName(local.clone(), suggestion),
|
NoticeKind::UnconventionalVariableName(local.clone(), suggestion),
|
||||||
)
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let r#type = context.unbound_type()?;
|
let r#type = context.unbound_type()?;
|
||||||
|
@ -534,7 +534,7 @@ fn visit_impl(mut context: Context, node: Node) -> Result<(Context, Arc<Type>),
|
||||||
context.warning(
|
context.warning(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnexpectedImplProperty(key.to_string()),
|
NoticeKind::UnexpectedImplProperty(key.to_string()),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((context, ty))
|
Ok((context, ty))
|
||||||
|
@ -556,7 +556,7 @@ fn visit_index(context: Context, node: Node) -> Result<(Context, Expression), Er
|
||||||
context.info(
|
context.info(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnconventionalFieldName(field_name.clone(), suggestion),
|
NoticeKind::UnconventionalFieldName(field_name.clone(), suggestion),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = ExpressionValue::Index {
|
let value = ExpressionValue::Index {
|
||||||
|
@ -644,7 +644,7 @@ fn visit_let_expression(mut context: Context, node: Node) -> Result<(Context, Ex
|
||||||
context.info(
|
context.info(
|
||||||
Some(span),
|
Some(span),
|
||||||
NoticeKind::UnconventionalVariableName(name.clone(), suggestion),
|
NoticeKind::UnconventionalVariableName(name.clone(), suggestion),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let expression_type;
|
let expression_type;
|
||||||
|
@ -792,7 +792,7 @@ fn visit_protocol(mut context: Context, node: Node) -> Result<(Context, Arc<Type
|
||||||
context.warning(
|
context.warning(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnexpectedProtocolProperty(key.to_string()),
|
NoticeKind::UnexpectedProtocolProperty(key.to_string()),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((context, ty))
|
Ok((context, ty))
|
||||||
|
@ -882,7 +882,7 @@ fn visit_struct(mut context: Context, node: Node) -> Result<(Context, Arc<Type>)
|
||||||
context.info(
|
context.info(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnconventionalFieldName(key.clone(), suggestion),
|
NoticeKind::UnconventionalFieldName(key.clone(), suggestion),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (new_context, value) = eval_type_union(context, value.clone())?;
|
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(
|
context.warning(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnexpectedStructProperty(key.to_string()),
|
NoticeKind::UnexpectedStructProperty(key.to_string()),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((context, r#type))
|
Ok((context, r#type))
|
||||||
|
@ -988,7 +988,7 @@ fn visit_use_statement(mut context: Context, node: Node) -> Result<Context, Erro
|
||||||
context.warning(
|
context.warning(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnexpectedUseProperty(key.to_string()),
|
NoticeKind::UnexpectedUseProperty(key.to_string()),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(alias) = props.get("as") {
|
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(
|
context.info(
|
||||||
Some(node.span),
|
Some(node.span),
|
||||||
NoticeKind::UnconventionalTypeName(value.clone(), suggestion),
|
NoticeKind::UnconventionalTypeName(value.clone(), suggestion),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
|
|
|
@ -19,6 +19,7 @@ mod grammar;
|
||||||
mod ir;
|
mod ir;
|
||||||
mod source;
|
mod source;
|
||||||
mod span;
|
mod span;
|
||||||
|
mod unique_id;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -27,10 +28,10 @@ pub use ir::Context;
|
||||||
pub use ir::Type;
|
pub use ir::Type;
|
||||||
pub use source::Source;
|
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 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::visit_nodes(context, ast).map_err(|e| (source.clone(), e))?;
|
||||||
let context = ir::improve(context).map_err(|e| (source.clone(), e))?;
|
let context = ir::improve(context).map_err(|e| (source.clone(), e))?;
|
||||||
|
|
||||||
|
|
53
outrun-compiler/src/unique_id.rs
Normal file
53
outrun-compiler/src/unique_id.rs
Normal 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);
|
||||||
|
}
|
|
@ -6,23 +6,22 @@ use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
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::new(env!("CARGO_MANIFEST_DIR"));
|
||||||
let path = path.join("lib");
|
let path = path.join("lib");
|
||||||
let path = path.as_path();
|
let path = path.as_path();
|
||||||
|
|
||||||
let files = collect_files(path, Vec::new())?;
|
let files = collect_files(path, Vec::new())?;
|
||||||
|
|
||||||
let mut results = Vec::new();
|
let mut context = Context::new();
|
||||||
|
|
||||||
for path in files.iter().take(1) {
|
for path in files.iter().take(1) {
|
||||||
let source = Source::from_file(path)?;
|
let source = Source::from_file(path)?;
|
||||||
let source = Arc::new(source);
|
let source = Arc::new(source);
|
||||||
let context = outrun_compiler::compile(source)?;
|
context = outrun_compiler::compile(context, source)?;
|
||||||
results.push(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(results)
|
Ok(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_files(path: &Path, mut files: Vec<PathBuf>) -> Result<Vec<PathBuf>, Error> {
|
fn collect_files(path: &Path, mut files: Vec<PathBuf>) -> Result<Vec<PathBuf>, Error> {
|
||||||
|
@ -52,21 +51,19 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_init() -> Result<()> {
|
fn test_init() -> Result<()> {
|
||||||
let results = init()?;
|
let context = init()?;
|
||||||
|
|
||||||
// println!("results: {:#?}", results);
|
// println!("results: {:#?}", results);
|
||||||
|
|
||||||
for context in results {
|
let unbound_types: Vec<Arc<Type>> = context
|
||||||
let unbound_types: Vec<Arc<Type>> = context
|
.types
|
||||||
.types
|
.iter()
|
||||||
.iter()
|
.filter(|t| t.is_unbound())
|
||||||
.filter(|t| t.is_unbound())
|
.cloned()
|
||||||
.cloned()
|
.collect();
|
||||||
.collect();
|
println!("unbound_types: {:#?}", unbound_types);
|
||||||
println!("unbound_types: {:#?}", unbound_types);
|
|
||||||
|
|
||||||
assert_eq!(unbound_types.len(), 0);
|
assert_eq!(unbound_types.len(), 0);
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue