Skip to content

Commit

Permalink
Decouple components from packages and program entries
Browse files Browse the repository at this point in the history
Components no longer need to be part of a package.
Single source files are no longer wrapped up in a dummy package.
The nonsensical concept of “single-file packages” is now gone.

Components no longer store the potential program entry (`main`).
Instead, the resolver now offers a method that looks it up.
This makes the concept of components simpler and more light-weight.
A program entry is part of a different layer: The layer of the
“driver” (e.g. `main.rs`).
Currently, we still store the “component type” in the component.
However, this also violates the layer model: Component types are
actually part of the package system and are very high-level:
Benchmark suites, test suites etc should not be coupled so tightly
to the concept of components. In the future, that field needs to go,
too.
  • Loading branch information
fmease committed Jun 22, 2022
1 parent d5c54a6 commit 6d6325a
Show file tree
Hide file tree
Showing 15 changed files with 370 additions and 314 deletions.
46 changes: 25 additions & 21 deletions compiler/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use crate::{
component::Component,
hir::{self, Declaration, DeclarationIndex, Expression, Number},
session::BuildSession,
syntax::lowered_ast::attributes::AttributeName,
utility::HashMap,
};
Expand All @@ -15,10 +16,20 @@ use std::{default::default, fmt};
mod instruction; // @Note naming swap out with name&concept bytecode
pub(crate) mod interpreter; // @Task move stuff from here to there

#[derive(PartialEq, Eq)]
enum LambdaParent {
Lambda,
Declaration,
// @Temporary
pub fn compile_and_interpret_declaration(
declaration: &Declaration,
component: &Component,
session: &BuildSession,
) -> Result<(), Error> {
let mut compiler = Compiler::new(component, session);
compiler.compile_declaration(declaration)?;
eprintln!("{}", compiler.print_chunks());

let mut interpreter = ByteCodeInterpreter::new(&compiler);
interpreter.execute()?;

Ok(())
}

struct Compiler<'a> {
Expand All @@ -30,17 +41,19 @@ struct Compiler<'a> {
entry: Option<ChunkIndex>,
declaration_mapping: HashMap<DeclarationIndex, ChunkIndex>,
component: &'a Component,
session: &'a BuildSession,
}

impl<'a> Compiler<'a> {
fn new(component: &'a Component) -> Self {
fn new(component: &'a Component, session: &'a BuildSession) -> Self {
Self {
chunks: IndexMap::new(),
constants: Vec::new(),
lambda_amount: 0,
entry: None,
declaration_mapping: default(),
component,
session,
}
}

Expand Down Expand Up @@ -133,7 +146,7 @@ impl<'a> Compiler<'a> {
// @Task obsolete once we map any ComponentIndex to a chunk identifier
if self
.component
.entry
.program_entry(self.session)
.as_ref()
.map_or(false, |entry| entry == &function.binder)
{
Expand Down Expand Up @@ -251,6 +264,12 @@ impl fmt::Debug for Value {
}
}

#[derive(PartialEq, Eq)]
enum LambdaParent {
Lambda,
Declaration,
}

// @Temporary
pub enum Error {
Compiletime(CompilationError),
Expand All @@ -271,21 +290,6 @@ impl From<CompilationError> for Error {

pub enum CompilationError {}

// @Temporary
pub fn compile_and_interpret_declaration(
declaration: &Declaration,
component: &Component,
) -> Result<(), Error> {
let mut compiler = Compiler::new(component);
compiler.compile_declaration(declaration)?;
eprintln!("{}", compiler.print_chunks());

let mut interpreter = ByteCodeInterpreter::new(&compiler);
interpreter.execute()?;

Ok(())
}

// @Question what should the relation be *actually* like?
//const FRAME_SIZE: usize = 64;
//const STACK_SIZE: usize = FRAME_SIZE * u8::MAX as usize;
Expand Down
152 changes: 70 additions & 82 deletions compiler/component.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
entity::Entity,
hir::{Identifier, LocalDeclarationIndex},
package::{Package, PackageIndex},
hir::LocalDeclarationIndex,
package::Package,
session::BuildSession,
span::Spanned,
syntax::Word,
Expand All @@ -10,37 +10,52 @@ use crate::{
use colored::Colorize;
use derivation::{Elements, FromStr, Str};
use index_map::IndexMap;
use std::{default::default, fmt, path::PathBuf};
use std::{
default::default,
fmt,
path::{Path, PathBuf},
};

pub type Components = IndexMap<ComponentIndex, Component>;

/// A sealed container of modules regarded as one unit embodying libraries and executables[^1].
///
/// [^1]: And integration and system tests, benchmarks and other things in the future.
pub struct Component {
pub metadata: ComponentMetadata,
name: Word,
index: ComponentIndex,
// @Beacon @Task make this a Spanned<AbsolutePathBuf>,
path: Spanned<PathBuf>,
// @Task document this! @Note this is used by the lang-server which gets the document content by the client
// and which should not open the file at the given path to avoid TOC-TOU bugs / data races
// @Beacon @Question should this be put on `Component` instead???
pub(crate) content: Option<String>,
// @Note I am not pumped about the current component type including such high-level types like "benchmark-suite"
// I feel like we are breaking layers of abstraction here, too. can we get rid of this field??
type_: ComponentType,
/// Resolved dependencies.
pub(crate) dependencies: HashMap<Word, ComponentIndex>,
// @Task don't make this a field! decouple the concept of "library vs executable" from components
// instead, at the very least, make this a method that looks for a top-level `main`.
// by nature "after the fact", i.e. after name resolution happens.
// I feel like us registering the program entry during name resolution breaks layers of abstraction
/// The `main` function (_program entry_) for executable components.
pub entry: Option<Identifier>,
/// All bindings inside of the component.
// The first element has to be the root module.
pub(crate) bindings: IndexMap<LocalDeclarationIndex, Entity>,
}

impl Component {
pub(crate) fn new(
metadata: ComponentMetadata,
name: Word,
index: ComponentIndex,
path: Spanned<PathBuf>,
content: Option<String>,
type_: ComponentType,
dependencies: HashMap<Word, ComponentIndex>,
) -> Self {
Self {
metadata,
name,
index,
path,
content,
type_,
dependencies,
entry: default(),
bindings: default(),
}
}
Expand All @@ -52,14 +67,11 @@ impl Component {
let name = Word::parse("test".into()).ok().unwrap();

let mut component = Self::new(
ComponentMetadata::new(
name.clone(),
ComponentIndex(0),
PackageIndex::new_unchecked(0),
Spanned::new(default(), PathBuf::new()),
None,
ComponentType::Library,
),
name.clone(),
ComponentIndex(0),
Spanned::new(default(), PathBuf::new()),
None,
ComponentType::Library,
HashMap::default(),
);
component.bindings.insert(Entity {
Expand All @@ -73,19 +85,19 @@ impl Component {
}

pub fn name(&self) -> &Word {
&self.metadata.name
&self.name
}

pub(crate) fn index(&self) -> ComponentIndex {
self.metadata.index
self.index
}

pub fn path(&self) -> Spanned<&std::path::Path> {
self.metadata.path.as_deref()
self.path.as_deref()
}

pub fn type_(&self) -> ComponentType {
self.metadata.type_
self.type_
}

pub fn is_library(&self) -> bool {
Expand All @@ -96,25 +108,32 @@ impl Component {
self.type_() == ComponentType::Executable
}

pub fn package<'s>(&self, session: &'s BuildSession) -> &'s Package {
&session[self.metadata.package]
}

/// Test if this component is the standard library `core`.
pub fn is_core_library(&self, session: &BuildSession) -> bool {
self.package(session).is_core() && self.is_library()
session
.package_of(self.identifier())
.map_or(false, Package::is_core)
&& self.is_library()
}

pub fn is_goal(&self, session: &BuildSession) -> bool {
self.index() == session.goal_component()
}

// @Task replace this with a method on BuildSession that looks through a `Package`s list of components!
// @Note I want to get rid of the backreference `package` in `Component`s
// @Update however, it's not as easy, since a `Component` needs to be fully built to be able to
// occur inside of a `Package` since a `ComponentIndex` has to be created
pub fn in_goal_package(&self, session: &BuildSession) -> bool {
self.metadata.package == session.goal_package()
pub fn identifier(&self) -> ComponentIdentifier<'_> {
ComponentIdentifier {
index: self.index,
path: &self.path.bare,
}
}

pub fn outline(&self) -> ComponentOutline {
ComponentOutline {
name: self.name.clone(),
index: self.index,
path: self.path.clone(),
type_: self.type_,
}
}
}

Expand All @@ -139,14 +158,7 @@ impl DisplayWith for Component {
type Context<'a> = &'a BuildSession;

fn format(&self, session: &BuildSession, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"{} {} ({:?}) {:?}",
self.type_(),
self.name(),
self.index(),
self.metadata.package
)?;
writeln!(f, "{} {} ({:?})", self.type_(), self.name(), self.index())?;

writeln!(f, " bindings:")?;

Expand All @@ -163,55 +175,31 @@ impl DisplayWith for Component {
}
}

/// Metadata of a [`Component`].
// @Beacon @Task remove this type. it is only used by the documenter and that only because
// Component cannot really be used for some reason. investigate
#[derive(Clone)]
// @Note I don't like this name!
pub struct ComponentMetadata {
pub struct ComponentOutline {
pub(crate) name: Word,
pub(crate) index: ComponentIndex,
// @Beacon @Beacon @Task not all components need to have a corresp. package!!!!
// make this field optional! even better if we could get rid of it entirely!
// we are kind of breaking layers of abstraction here: components vs packages!
// @Update
// It's not as easy, since a `Package` needs a list of `ComponentIndex`es
// but they aren't created until the components in question are fully built
// however, in many cases, we want to get the package of the current component
// (which ofc hasn't been built yet)
// One solution (maybe): identifiy components in Packages via an
// enum { Unbuilt { name: Word, "type", path: PathButh }, Built(ComponentIndex) }
// ooorrr, store `ComponentMetadata` in variant Unbuilt and make
// @Beacon ComponentMetadata.index an Option<ComponentIndex> / PossiblyUnresolved<ComponentIndex>
pub(crate) package: PackageIndex,
pub(crate) path: Spanned<PathBuf>,
// @Task document this! @Note this is used by the lang-server which gets the document content by the client
// and which should not open the file at the given path to avoid TOC-TOU bugs / data races
// @Beacon @Question should this be put on `Component` instead???
pub(crate) content: Option<String>,
// @Note I am not pumped about the current component type including such high-level types like "benchmark-suite"
// I feel like we are breaking layers of abstraction here, too. can we get rid of this field??
pub(crate) type_: ComponentType,
}

impl ComponentMetadata {
pub(crate) fn new(
name: Word,
index: ComponentIndex,
package: PackageIndex,
path: Spanned<PathBuf>,
content: Option<String>,
type_: ComponentType,
) -> Self {
Self {
name,
index,
package,
path,
content,
type_,
impl ComponentOutline {
pub(crate) fn identifier(&self) -> ComponentIdentifier<'_> {
ComponentIdentifier {
index: self.index,
path: &self.path.bare,
}
}
}

#[derive(Clone, Copy)]
pub struct ComponentIdentifier<'a> {
pub(crate) index: ComponentIndex,
pub(crate) path: &'a Path,
}

#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct ComponentIndex(pub(crate) u16);

Expand Down
Loading

0 comments on commit 6d6325a

Please sign in to comment.