Skip to content

Commit

Permalink
refactor: Introduce GlobalContext (PLC-lang#1058)
Browse files Browse the repository at this point in the history
* Make index and annotate steps infallible

* Improve error message for assignment suggestion

* Remove unwraps

* Add "profiling" build profile

* Make insert method fallible
  • Loading branch information
volsa authored Dec 27, 2023
1 parent faa8050 commit 9c5765b
Show file tree
Hide file tree
Showing 21 changed files with 294 additions and 149 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ plc_source = { path = "./compiler/plc_source" }
plc_ast = { path = "./compiler/plc_ast" }
plc_util = { path = "./compiler/plc_util" }
plc_diagnostics = { path = "./compiler/plc_diagnostics" }
plc_index = { path = "./compiler/plc_index" }
logos = "0.12.0"
thiserror = "1.0"
clap = { version = "3.0", features = ["derive"] }
Expand Down Expand Up @@ -69,6 +70,7 @@ members = [
"compiler/plc_util",
"compiler/plc_xml",
"compiler/plc_derive",
"compiler/plc_index",
]
default-members = [".", "compiler/plc_driver", "compiler/plc_xml"]

Expand Down
2 changes: 1 addition & 1 deletion compiler/plc_ast/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::sync::{

use crate::ast::AstId;

#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct IdProvider {
current_id: Arc<AtomicUsize>,
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/plc_diagnostics/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,10 +745,10 @@ impl Diagnostic {
}
}

pub fn assignment_instead_of_equal(range: SourceLocation) -> Diagnostic {
pub fn assignment_instead_of_equal(lhs: &str, rhs: &str, statement: &AstNode) -> Diagnostic {
Diagnostic::ImprovementSuggestion {
message: "This statement has no effect, did you mean to use `:=`?".to_string(),
range: vec![range],
message: format!("This equal statement has no effect, did you mean `{lhs} := {rhs}`?"),
range: vec![statement.get_location()],
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/plc_driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ project = { path = "../plc_project/", package = "plc_project" }
source_code = { path = "../plc_source/", package = "plc_source" }
cfc = { path = "../plc_xml/", package = "plc_xml" }
plc_diagnostics = { path = "../plc_diagnostics/" }
plc_index = { path = "../plc_index" }

serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
Expand Down
59 changes: 34 additions & 25 deletions compiler/plc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ use std::{
path::{Path, PathBuf},
};

use ast::provider::IdProvider;
use cli::{CompileParameters, ParameterError};
use pipelines::AnnotatedProject;
use plc::{
codegen::CodegenContext, output::FormatOption, DebugLevel, ErrorFormat, OptimizationLevel, Threads,
};

use plc_diagnostics::{diagnostician::Diagnostician, diagnostics::Diagnostic};
use plc_index::GlobalContext;
use project::project::{LibraryInformation, Project};
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use source_code::SourceContainer;
Expand Down Expand Up @@ -138,7 +138,6 @@ pub fn compile<T: AsRef<str> + AsRef<OsStr> + Debug>(args: &[T]) -> Result<(), C
log::debug!("LIB_LOCATION={}", location.to_string_lossy());
env::set_var("LIB_LOCATION", location);
}
let id_provider = IdProvider::default();
let mut diagnostician = match compile_parameters.error_format {
ErrorFormat::Rich => Diagnostician::default(),
ErrorFormat::Clang => Diagnostician::clang_format_diagnostician(),
Expand All @@ -159,19 +158,29 @@ pub fn compile<T: AsRef<str> + AsRef<OsStr> + Debug>(args: &[T]) -> Result<(), C
log::info!("{err}")
}

// 1 : Parse
let annotated_project = pipelines::ParsedProject::parse(
&project,
compile_parameters.encoding,
id_provider.clone(),
&mut diagnostician,
)?
// 2 : Index
.index(id_provider.clone())?
// 3 : Resolve
.annotate(id_provider, &diagnostician)?;
// TODO: This can be improved quite a bit, e.g. `GlobalContext::new(project);`, to do that see the
// commented `project` method in the GlobalContext implementation block
let ctxt = GlobalContext::new()
.with_source(project.get_sources(), compile_parameters.encoding)?
.with_source(project.get_includes(), compile_parameters.encoding)?
.with_source(
project
.get_libraries()
.iter()
.flat_map(LibraryInformation::get_includes)
.collect::<Vec<_>>()
.as_slice(),
None,
)?;

// 1 : Parse, 2. Index and 3. Resolve / Annotate
let annotated_project = pipelines::ParsedProject::parse(&ctxt, &project, &mut diagnostician)?
.index(ctxt.provider())
.annotate(ctxt.provider());

// 4 : Validate
annotated_project.validate(&mut diagnostician)?;
annotated_project.validate(&ctxt, &mut diagnostician)?;

// 5 : Codegen
if !compile_parameters.is_check() {
let res = generate(
Expand Down Expand Up @@ -201,20 +210,20 @@ pub fn compile<T: AsRef<str> + AsRef<OsStr> + Debug>(args: &[T]) -> Result<(), C
pub fn parse_and_annotate<T: SourceContainer>(
name: &str,
src: Vec<T>,
) -> Result<AnnotatedProject, Diagnostic> {
) -> Result<(GlobalContext, AnnotatedProject), Diagnostic> {
// Parse the source to ast
let project = Project::new(name.to_string()).with_sources(src);
let id_provider = IdProvider::default();
let ctxt = GlobalContext::new().with_source(project.get_sources(), None)?;
let mut diagnostician = Diagnostician::default();
pipelines::ParsedProject::parse(&project, None, id_provider.clone(), &mut diagnostician)?
// Create an index, add builtins
.index(id_provider.clone())?
// Resolve
.annotate(id_provider, &diagnostician)
let parsed = pipelines::ParsedProject::parse(&ctxt, &project, &mut diagnostician)?;

// Create an index, add builtins then resolve
let provider = ctxt.provider();
Ok((ctxt, parsed.index(provider.clone()).annotate(provider)))
}

/// Generates an IR string from a list of sources. Useful for tests or api calls
pub fn generate_to_string<T: SourceContainer>(name: &str, src: Vec<T>) -> Result<String, Diagnostic> {
pub fn generate_to_string<T: SourceContainer>(name: &'static str, src: Vec<T>) -> Result<String, Diagnostic> {
generate_to_string_internal(name, src, false)
}

Expand All @@ -229,10 +238,10 @@ fn generate_to_string_internal<T: SourceContainer>(
debug: bool,
) -> Result<String, Diagnostic> {
let mut diagnostician = Diagnostician::default();
let project = parse_and_annotate(name, src)?;
let (ctxt, project) = parse_and_annotate(name, src)?;

// Validate
project.validate(&mut diagnostician)?;
project.validate(&ctxt, &mut diagnostician)?;

// Generate
let context = CodegenContext::create();
Expand All @@ -253,7 +262,7 @@ fn generate(
compile_parameters: CompileParameters,
project: Project<PathBuf>,
output_format: FormatOption,
annotated_project: pipelines::AnnotatedProject,
annotated_project: AnnotatedProject,
build_location: Option<PathBuf>,
lib_location: Option<PathBuf>,
) -> Result<(), Diagnostic> {
Expand Down
58 changes: 22 additions & 36 deletions compiler/plc_driver/src/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use ast::{
ast::{pre_process, CompilationUnit, LinkageType},
provider::IdProvider,
};
use encoding_rs::Encoding;
use indexmap::IndexSet;
use plc::{
codegen::{CodegenContext, GeneratedModule},
Expand All @@ -26,6 +25,7 @@ use plc_diagnostics::{
diagnostics::Diagnostic,
errno::ErrNo,
};
use plc_index::GlobalContext;
use project::{
object::Object,
project::{LibraryInformation, Project},
Expand All @@ -42,9 +42,8 @@ impl ParsedProject {
/// Parses a giving project, transforming it to a `ParsedProject`
/// Reports parsing diagnostics such as Syntax error on the fly
pub fn parse<T: SourceContainer>(
ctxt: &GlobalContext,
project: &Project<T>,
encoding: Option<&'static Encoding>,
id_provider: IdProvider,
diagnostician: &mut Diagnostician,
) -> Result<Self, Diagnostic> {
//TODO in parallel
Expand All @@ -55,50 +54,37 @@ impl ParsedProject {
.get_sources()
.iter()
.map(|it| {
let loaded_source = it.load_source(encoding).map_err(|err| {
Diagnostic::io_read_error(
&it.get_location().expect("Location should not be empty").to_string_lossy(),
&err,
)
})?;

let parse_func = match loaded_source.get_type() {
let source = ctxt.get(it.get_location_str()).expect("All sources should've been read");

let parse_func = match source.get_type() {
source_code::SourceType::Text => parse_file,
source_code::SourceType::Xml => cfc::xml_parser::parse_file,
source_code::SourceType::Unknown => unreachable!(),
};
Ok(parse_func(loaded_source, LinkageType::Internal, id_provider.clone(), diagnostician))
Ok(parse_func(source, LinkageType::Internal, ctxt.provider(), diagnostician))
})
.collect::<Result<Vec<_>, Diagnostic>>()?;
units.extend(sources);

//Parse the includes
let includes = project
.get_includes()
.iter()
.map(|it| {
let loaded_source = it.load_source(encoding).map_err(|err| {
Diagnostic::io_read_error(
&it.get_location().expect("Location should not be empty").to_string_lossy(),
&err,
)
})?;
Ok(parse_file(loaded_source, LinkageType::External, id_provider.clone(), diagnostician))
let source = ctxt.get(it.get_location_str()).expect("All sources should've been read");
Ok(parse_file(source, LinkageType::External, ctxt.provider(), diagnostician))
})
.collect::<Result<Vec<_>, Diagnostic>>()?;
units.extend(includes);

//For each lib, parse the includes
let lib_includes = project
.get_libraries()
.iter()
.flat_map(LibraryInformation::get_includes)
.map(|it| {
let loaded_source = it.load_source(encoding).map_err(|err| {
Diagnostic::io_read_error(
&it.get_location().expect("Location should not be empty").to_string_lossy(),
&err,
)
})?;
Ok(parse_file(loaded_source, LinkageType::External, id_provider.clone(), diagnostician))
let source = ctxt.get(it.get_location_str()).expect("All sources should've been read");
Ok(parse_file(source, LinkageType::External, ctxt.provider(), diagnostician))
})
.collect::<Result<Vec<_>, Diagnostic>>()?;
units.extend(lib_includes);
Expand All @@ -107,7 +93,7 @@ impl ParsedProject {
}

/// Creates an index out of a pased project. The index could then be used to query datatypes
pub fn index(self, id_provider: IdProvider) -> Result<IndexedProject, Diagnostic> {
pub fn index(self, id_provider: IdProvider) -> IndexedProject {
let indexed_units = self
.0
.into_par_iter()
Expand Down Expand Up @@ -136,7 +122,7 @@ impl ParsedProject {
let builtins = plc::builtins::parse_built_ins(id_provider);
global_index.import(plc::index::visitor::visit(&builtins));

Ok(IndexedProject { units, index: global_index })
IndexedProject { units, index: global_index }
}
}

Expand All @@ -149,11 +135,7 @@ pub struct IndexedProject {

impl IndexedProject {
/// Creates annotations on the project in order to facilitate codegen and validation
pub fn annotate(
self,
mut id_provider: IdProvider,
_diagnostician: &Diagnostician,
) -> Result<AnnotatedProject, Diagnostic> {
pub fn annotate(self, mut id_provider: IdProvider) -> AnnotatedProject {
//Resolve constants
//TODO: Not sure what we are currently doing with unresolvables
let (mut full_index, _unresolvables) = plc::resolver::const_evaluator::evaluate_constants(self.index);
Expand All @@ -180,7 +162,7 @@ impl IndexedProject {

let annotations = AstAnnotations::new(all_annotations, id_provider.next_id());

Ok(AnnotatedProject { units: annotated_units, index: full_index, annotations })
AnnotatedProject { units: annotated_units, index: full_index, annotations }
}
}

Expand All @@ -193,9 +175,13 @@ pub struct AnnotatedProject {

impl AnnotatedProject {
/// Validates the project, reports any new diagnostics on the fly
pub fn validate(&self, diagnostician: &mut Diagnostician) -> Result<(), Diagnostic> {
pub fn validate(
&self,
ctxt: &GlobalContext,
diagnostician: &mut Diagnostician,
) -> Result<(), Diagnostic> {
// perform global validation
let mut validator = Validator::new();
let mut validator = Validator::new(ctxt);
validator.perform_global_validation(&self.index);
let diagnostics = validator.diagnostics();
let mut severity = diagnostician.handle(&diagnostics);
Expand Down
11 changes: 5 additions & 6 deletions compiler/plc_driver/src/runner.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{pipelines::ParsedProject, CompileOptions};

use ast::provider::IdProvider;
use plc::codegen::{CodegenContext, GeneratedModule};
use plc_diagnostics::diagnostician::Diagnostician;
use plc_index::GlobalContext;
use project::project::Project;
use source_code::Compilable;

Expand All @@ -26,12 +26,11 @@ impl Default for MainType {
pub fn compile<T: Compilable>(context: &CodegenContext, source: T) -> GeneratedModule<'_> {
let source = source.containers();
let project = Project::new("TestProject".to_string()).with_sources(source);
let ctxt = GlobalContext::new().with_source(project.get_sources(), None).unwrap();
let mut diagnostician = Diagnostician::null_diagnostician();
let id_provider = IdProvider::default();
let parsed_project =
ParsedProject::parse(&project, None, id_provider.clone(), &mut diagnostician).unwrap();
let indexed_project = parsed_project.index(id_provider.clone()).unwrap();
let annotated_project = indexed_project.annotate(id_provider, &diagnostician).unwrap();
let parsed_project = ParsedProject::parse(&ctxt, &project, &mut diagnostician).unwrap();
let indexed_project = parsed_project.index(ctxt.provider());
let annotated_project = indexed_project.annotate(ctxt.provider());
let compile_options = CompileOptions {
optimization: plc::OptimizationLevel::None,
debug_level: plc::DebugLevel::None,
Expand Down
Loading

0 comments on commit 9c5765b

Please sign in to comment.