Skip to content

Commit

Permalink
feat(plc.json-validation): Add validation for build description file (P…
Browse files Browse the repository at this point in the history
…LC-lang#994)

* initial commit

* add schema, optional fields, validation method, tests, diagnostics
adjust integration tests

* remove unnecessary serde attr, move schema to file

* add clang error locations to serde errors/diagnostics
  • Loading branch information
mhasel authored Nov 8, 2023
1 parent bc1c805 commit d359949
Show file tree
Hide file tree
Showing 21 changed files with 921 additions and 109 deletions.
432 changes: 431 additions & 1 deletion Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions compiler/plc_diagnostics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ edition = "2021"
codespan-reporting = "0.11.1"
plc_ast = { path = "../plc_ast" }
plc_source = { path = "../plc_source" }
serde_json = "1"
46 changes: 45 additions & 1 deletion compiler/plc_diagnostics/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::{error::Error, fmt::Display, ops::Range};

use plc_ast::ast::{AstNode, DataTypeDeclaration, DiagnosticInfo, PouType};
use plc_source::source_location::SourceLocation;

use plc_source::{
source_location::{SourceLocation, SourceLocationFactory},
BuildDescriptionSource,
};

use crate::errno::ErrNo;

Expand All @@ -16,6 +20,13 @@ pub enum Diagnostic {
CombinedDiagnostic { message: String, inner_diagnostics: Vec<Diagnostic>, err_no: ErrNo },
}

#[derive(Debug)]
pub struct SerdeError {
message: String,
line: usize,
column: usize,
}

impl Display for Diagnostic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.get_type(), self.get_message())?;
Expand Down Expand Up @@ -784,6 +795,39 @@ impl Diagnostic {
err_no: ErrNo::cfc__unnamed_control,
}
}

pub fn invalid_build_description_file(message: String, location: Option<SourceLocation>) -> Diagnostic {
let range = if let Some(range) = location { vec![range] } else { vec![SourceLocation::internal()] };
Diagnostic::SemanticError { message, range, err_no: ErrNo::plc_json__invalid }
}
}

// Necessary in-between step to convert serde error to diagnostics, since there is
// a conflicting `From<T: Error>` impl for `Diagnostic`
impl From<serde_json::Error> for SerdeError {
fn from(value: serde_json::Error) -> Self {
let line = value.line();
let column = value.column();

// remove line, column from message
let message = value.to_string();
let message = if let Some(pos) = message.find("at line") {
message.chars().take(pos).collect()
} else {
message
};

SerdeError { message, line, column }
}
}

impl SerdeError {
pub fn into_diagnostic(self, src: &BuildDescriptionSource) -> Diagnostic {
let factory = SourceLocationFactory::for_source(src);
let range = factory.create_range_to_end_of_line(self.line, self.column);

Diagnostic::invalid_build_description_file(self.message, Some(range))
}
}

#[cfg(test)]
Expand Down
3 changes: 3 additions & 0 deletions compiler/plc_diagnostics/src/errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ pub enum ErrNo {
cfc__cyclic_connection,
cfc__no_associated_connector,
cfc__unnamed_control,

// Project description file
plc_json__invalid,
}

impl Display for ErrNo {
Expand Down
4 changes: 4 additions & 0 deletions compiler/plc_project/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ source_code = { path = "../plc_source/", package = "plc_source" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
regex = "1"
jsonschema = "0.17"
encoding_rs.workspace = true
encoding_rs_io.workspace = true
glob = "*"

[dev-dependencies]
insta = "1.31.0"
77 changes: 77 additions & 0 deletions compiler/plc_project/schema/plc-json.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "Schema for plc.json",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
},
"format_version": {
"type": "string"
},
"files": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"compile_type": {
"type": "string"
},
"output": {
"type": "string"
},
"libraries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"package": {
"type": "string"
},
"include_path": {
"type": "array",
"items": {
"type": "string"
}
},
"architectures": {
"type": "array",
"items": {
"type": "object"
}
}
},
"additionalProperties": false,
"required": [
"name",
"path",
"package",
"include_path"
]
}
},
"package_commands": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"name",
"files",
"compile_type"
]
}
Loading

0 comments on commit d359949

Please sign in to comment.