diff --git a/.github/workflows/rust.yml b/.github/workflows/linux.yml
similarity index 100%
rename from .github/workflows/rust.yml
rename to .github/workflows/linux.yml
diff --git a/.github/workflows/lit.yml b/.github/workflows/lit.yml
new file mode 100644
index 0000000000..4858e5864b
--- /dev/null
+++ b/.github/workflows/lit.yml
@@ -0,0 +1,35 @@
+name: Build
+
+on:
+ # Triggers the workflow on push or pull request events but only for the master branch
+ push:
+ pull_request:
+ branches: [ master ]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ lit-linux-debug:
+ name: lit tests (Linux, debug build)
+ runs-on: ubuntu-latest
+ container: ghcr.io/plc-lang/rust-llvm:latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Run `build.sh --lit`
+ shell: bash
+ run: |
+ ./scripts/build.sh --lit
+
+ lit-linux-release:
+ name: lit tests (Linux, release build)
+ runs-on: ubuntu-latest
+ container: ghcr.io/plc-lang/rust-llvm:latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Run `build.sh --lit --release`
+ shell: bash
+ run: |
+ ./scripts/build.sh --lit --release
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index cb17664f45..3d60db547f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,7 @@
*.a
*.elf
*.out
+
+# Garbage generated by llvm-lit
+tests/lit/**/*.txt
+tests/lit/**/Output/
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 21eab6ec81..8241a1f9ec 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -20,9 +20,13 @@
}
},
"args": [
- "target/demo.st",
+ "target/demo.st"
],
- "cwd": "${workspaceFolder}"
+ "cwd": "${workspaceFolder}",
+ "env": {
+ "RUST_LOG": "rusty"
+ },
+ "terminal": "integrated"
},
{
"type": "lldb",
@@ -43,7 +47,11 @@
"args": [
"demo"
],
- "cwd": "${workspaceFolder}"
+ "cwd": "${workspaceFolder}",
+ "env": {
+ "RUST_LOG": "rusty"
+ },
+ "terminal": "integrated"
},
]
}
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 2dea6b2af6..e206b259bb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2221,9 +2221,9 @@ dependencies = [
[[package]]
name = "openssl"
-version = "0.10.64"
+version = "0.10.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
+checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
dependencies = [
"bitflags 2.4.2",
"cfg-if",
@@ -2253,9 +2253,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
-version = "0.9.101"
+version = "0.9.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
+checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
@@ -2537,6 +2537,7 @@ dependencies = [
"serde",
"serde_json",
"tempfile",
+ "toml",
]
[[package]]
@@ -3023,6 +3024,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "section_mangler"
version = "0.0.1"
+dependencies = [
+ "nom",
+]
[[package]]
name = "security-framework"
diff --git a/README.md b/README.md
index e52386e6b0..f209f5bc75 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,34 @@
-# RuSTy
-[![Rust Build](https://github.com/PLC-lang/rusty/actions/workflows/rust.yml/badge.svg)](https://github.com/PLC-lang/ruSTy/actions)
-[![codecov](https://codecov.io/gh/PLC-lang/rusty/branch/master/graph/badge.svg?token=7ZZ5XZYE9V)](https://codecov.io/gh/PLC-lang/rusty)
-[![Lines of Code](https://tokei.rs/b1/github/PLC-lang/rusty)](https://github.com/XAMPPRocky/tokei)
+
+
+
RuSTy
+
A structured text compiler written in Rust, utilizing the LLVM framework for native code compilation.
-[Structured text](https://en.wikipedia.org/wiki/Structured_text) compiler written in Rust
+
+
+
+
+
-## About RuSTy
+
+ Examples |
+ Documentation |
+ Contributing
+
-RuSTy is a structured text (ST) compiler written in Rust. RuSTy utilizes the
-LLVM framework to compile eventually to native code.
+
-## Getting started
-
-The easiest way to compile this project is to use the provided `Dockerfile`. The project offers a `.devcontainer` when using [VSCode](https://code.visualstudio.com/docs/remote/containers). The Dockerfile offers a linux-image which contains everything you need to run `cargo build` / `cargo test` in the project's root directory.
-
-If you want to build the project without docker, start [here](https://plc-lang.github.io/rusty/build_and_install.html).
-
-### Documentation
-
-The compiler's documentation can be found here: [documentation](https://plc-lang.github.io/rusty/).
-### Contributing
-
-If you want to contribute to the project you should look for some [beginner-friendly issues](https://github.com/PLC-lang/rusty/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and reach out to project's maintainers.
-
-## Why RuSTy
+## Why RuSTy?
Structured Text is a popular language in the domain of automation. A standardized specification of the language ([IEC 61131](https://en.wikipedia.org/wiki/IEC_61131)) was published in the 90s. It was updated several times in the meantime, while its initial spirit - being built for cyclic, robust and deterministic automation applications - still applies.
Several automation platform suppliers built proprietary compilers and runtime libraries, native to the vendor's hard- and software platform.
-RuSTy is aiming towards a _fast_, _modern_ and _open-source_ industry-grade ST compiler for a wide range of platforms, sticking close to the standard.
+RuSTy is aiming towards a **fast**, **modern** and **open-source** industry-grade ST compiler for a wide range of platforms, sticking close to the standard.
-## Dependencies
+## Getting started
+
+The easiest way to compile this project is to use the provided `Dockerfile`. The project offers a `.devcontainer` when using [VSCode](https://code.visualstudio.com/docs/remote/containers). The Dockerfile offers a linux-image which contains everything you need to run `cargo build` / `cargo test` in the project's root directory.
-We use the [_logos_](https://crates.io/crates/logos/)
-crate library to perform lexical analysis before a handwritten recursive decent parser creates the AST.
-Generating LLVM IR is accomplished with the help of [_inkwell_](https://github.com/TheDan64/inkwell), a Rust-wrapper around the native LLVM C-API.
+If you want to build the project without docker, start [here](https://plc-lang.github.io/rusty/build_and_install.html).
\ No newline at end of file
diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs
index 83a218d5ec..f851a8fd31 100644
--- a/compiler/plc_ast/src/ast.rs
+++ b/compiler/plc_ast/src/ast.rs
@@ -487,6 +487,7 @@ pub enum DataType {
PointerType {
name: Option,
referenced_type: Box,
+ auto_deref: Option,
},
StringType {
name: Option,
@@ -504,6 +505,18 @@ pub enum DataType {
},
}
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum AutoDerefType {
+ /// A plain pointer variable with the auto-deref trait, e.g. VAR_IN_OUT or VAR_INPUT{ref} variables
+ Default,
+
+ /// An alias pointer variable, e.g. `foo AT bar : DINT`
+ Alias,
+
+ /// A reference pointer variable, e.g. `foo : REFERENCE TO DINT;`
+ Reference,
+}
+
impl DataType {
pub fn set_name(&mut self, new_name: String) {
match self {
@@ -596,12 +609,14 @@ pub struct AstNode {
#[derive(Debug, Clone, PartialEq)]
pub enum AstStatement {
EmptyStatement(EmptyStatement),
- // a placeholder that indicates a default value of a datatype
+
+ // A placeholder which indicates a default value of a datatype
DefaultValue(DefaultValue),
+
// Literals
Literal(AstLiteral),
- CastStatement(CastStatement),
MultipliedStatement(MultipliedStatement),
+
// Expressions
ReferenceExpr(ReferenceExpr),
Identifier(String),
@@ -613,15 +628,17 @@ pub enum AstStatement {
ParenExpression(Box),
RangeStatement(RangeStatement),
VlaRangeStatement,
- // Assignment
+
+ // TODO: Merge these variants with a `kind` field?
+ // Assignments
Assignment(Assignment),
- // OutputAssignment
OutputAssignment(Assignment),
- //Call Statement
+ RefAssignment(Assignment),
+
CallStatement(CallStatement),
+
// Control Statements
ControlStatement(AstControlStatement),
-
CaseCondition(Box),
ExitStatement(()),
ContinueStatement(()),
@@ -662,6 +679,9 @@ impl Debug for AstNode {
AstStatement::OutputAssignment(Assignment { left, right }) => {
f.debug_struct("OutputAssignment").field("left", left).field("right", right).finish()
}
+ AstStatement::RefAssignment(Assignment { left, right }) => {
+ f.debug_struct("ReferenceAssignment").field("left", left).field("right", right).finish()
+ }
AstStatement::CallStatement(CallStatement { operator, parameters }) => f
.debug_struct("CallStatement")
.field("operator", operator)
@@ -735,9 +755,6 @@ impl Debug for AstNode {
}
AstStatement::ContinueStatement(..) => f.debug_struct("ContinueStatement").finish(),
AstStatement::ExitStatement(..) => f.debug_struct("ExitStatement").finish(),
- AstStatement::CastStatement(CastStatement { target, type_name }) => {
- f.debug_struct("CastStatement").field("type_name", type_name).field("target", target).finish()
- }
AstStatement::ReferenceExpr(ReferenceExpr { access, base }) => {
f.debug_struct("ReferenceExpr").field("kind", access).field("base", base).finish()
}
@@ -855,6 +872,14 @@ impl AstNode {
matches!(self.stmt, AstStatement::EmptyStatement(..))
}
+ pub fn is_assignment(&self) -> bool {
+ matches!(self.stmt, AstStatement::Assignment(..))
+ }
+
+ pub fn is_output_assignment(&self) -> bool {
+ matches!(self.stmt, AstStatement::OutputAssignment(..))
+ }
+
pub fn is_reference(&self) -> bool {
matches!(self.stmt, AstStatement::ReferenceExpr(..))
}
@@ -1280,8 +1305,11 @@ impl AstFactory {
}
/// creates a new Identifier
- pub fn create_identifier(name: &str, location: &SourceLocation, id: AstId) -> AstNode {
- AstNode::new(AstStatement::Identifier(name.to_string()), id, location.clone())
+ pub fn create_identifier(name: &str, location: T, id: AstId) -> AstNode
+ where
+ T: Into,
+ {
+ AstNode::new(AstStatement::Identifier(name.to_string()), id, location.into())
}
pub fn create_unary_expression(
@@ -1315,6 +1343,19 @@ impl AstFactory {
)
}
+ // TODO: Merge `create_assignment`, `create_output_assignment` and `create_ref_assignment`
+ // once the the Assignment AstStatements have been merged and a `kind` field is available
+ // I.e. something like `AstStatement::Assignment { data, kind: AssignmentKind { Normal, Output, Reference } }
+ // and then fn create_assignment(kind: AssignmentKind, ...)
+ pub fn create_ref_assignment(left: AstNode, right: AstNode, id: AstId) -> AstNode {
+ let location = left.location.span(&right.location);
+ AstNode::new(
+ AstStatement::RefAssignment(Assignment { left: Box::new(left), right: Box::new(right) }),
+ id,
+ location,
+ )
+ }
+
pub fn create_member_reference(member: AstNode, base: Option, id: AstId) -> AstNode {
let location = base
.as_ref()
@@ -1413,18 +1454,21 @@ impl AstFactory {
}
}
- pub fn create_call_statement(
+ pub fn create_call_statement(
operator: AstNode,
parameters: Option,
id: usize,
- location: SourceLocation,
- ) -> AstNode {
+ location: T,
+ ) -> AstNode
+ where
+ T: Into,
+ {
AstNode {
stmt: AstStatement::CallStatement(CallStatement {
operator: Box::new(operator),
parameters: parameters.map(Box::new),
}),
- location,
+ location: location.into(),
id,
}
}
diff --git a/compiler/plc_ast/src/lib.rs b/compiler/plc_ast/src/lib.rs
index 7e7d78a7de..33febadee0 100644
--- a/compiler/plc_ast/src/lib.rs
+++ b/compiler/plc_ast/src/lib.rs
@@ -7,3 +7,4 @@ pub mod control_statements;
pub mod literals;
mod pre_processor;
pub mod provider;
+pub mod visitor;
diff --git a/compiler/plc_ast/src/visitor.rs b/compiler/plc_ast/src/visitor.rs
new file mode 100644
index 0000000000..c50b7c3c1b
--- /dev/null
+++ b/compiler/plc_ast/src/visitor.rs
@@ -0,0 +1,724 @@
+//! This module defines the `AstVisitor` trait and its associated macros.
+//! The `AstVisitor` trait provides a set of methods for traversing and visiting ASTs
+
+use crate::ast::AstNode;
+use crate::ast::*;
+use crate::control_statements::{AstControlStatement, ConditionalBlock, ReturnStatement};
+use crate::literals::AstLiteral;
+
+/// Macro that calls the visitor's `visit` method for every AstNode in the passed iterator `iter`.
+macro_rules! visit_all_nodes {
+ ($visitor:expr, $iter:expr) => {
+ for node in $iter {
+ $visitor.visit(node);
+ }
+ };
+}
+
+/// Macro that calls the visitor's `visit` method for every AstNode in the passed sequence of nodes.
+macro_rules! visit_nodes {
+ ($visitor:expr, $($node:expr),*) => {
+ $(
+ $visitor.visit($node);
+ )*
+ };
+}
+
+/// The `Walker` implements the traversal of the AST nodes and Ast-related objects (e.g. CompilationUnit).
+/// The `walk` method is called on the object to visit its children.
+/// If the object passed to a `AstVisitor`'s `visit` method implements the `Walker` trait,
+/// a call to the it's walk function continues the visiting process on its children.
+///
+/// Spliting the traversal logic into a separate trait allows to call the default traversal logic
+/// from the visitor while overriding the visitor's `visit` method for specific nodes.
+///
+/// # Example
+/// ```
+/// use plc_ast::ast::AstNode;
+/// use plc_ast::visitor::Walker;
+/// use plc_ast::visitor::AstVisitor;
+///
+/// struct MyAssignment {
+/// left: AstNode,
+/// right: AstNode,
+/// }
+///
+/// impl Walker for MyAssignment {
+/// fn walk(&self, visitor: &mut V)
+/// where
+/// V: AstVisitor,
+/// {
+/// visitor.visit(&self.right);
+/// visitor.visit(&self.left);
+/// }
+/// }
+/// ```
+///
+pub trait Walker {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor;
+}
+
+/// The `AstVisitor` trait provides a set of methods for visiting different types of AST nodes.
+/// Implementors can individually override the methods they are interested in. When overriding a method,
+/// make sure to call `walk` on the visited statement to visit its children. DO NOT call walk on
+/// the node itself to avoid a recursion (last parameter). Implementors may also decide to not call
+/// the statement's `walk` method to avoid visiting the children of the statement.
+///
+/// The visitor offers strongly typed `visit_X` functions for every node type. The function's signature
+/// is `fn visit_X(&mut self, stmt: &X, node: &AstNode)`. The `stmt` parameter is the unwrapped, typed
+/// node and the `node` parameter is the `AstNode` wrapping the stmt. The `AstNode` node offers access to location
+/// information and the AstId. Note that some nodes are not wrapped in an `AstNode` node (e.g. `CompilationUnit`)
+/// and therefore only the strongly typed node is passed to the `visit_X` function.
+///
+/// # Example
+/// ```
+/// use plc_ast::{
+/// ast::{Assignment, AstNode},
+/// visitor::{AstVisitor, Walker},
+/// };
+///
+/// struct AssignmentCounter {
+/// count: usize,
+/// }
+///
+/// impl AstVisitor for AssignmentCounter {
+/// fn visit_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
+/// self.count += 1;
+/// // visit child nodes
+/// stmt.walk(self);
+/// }
+///
+/// fn visit_output_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
+/// self.count += 1;
+/// // visit child nodes
+/// stmt.walk(self);
+/// }
+/// }
+/// ```
+pub trait AstVisitor: Sized {
+ /// Visits this `AstNode`. The default implementation calls the `walk` method on the node
+ /// and will eventually call the strongly typed `visit` method for the node (e.g. visit_assignment
+ /// if the node is an `AstStatement::Assignment`).
+ /// # Arguments
+ /// * `node` - The `AstNode` node to visit.
+ fn visit(&mut self, node: &AstNode) {
+ node.walk(self)
+ }
+
+ /// Visits a `CompilationUnit` node.
+ /// Make sure to call `walk` on the `CompilationUnit` node to visit its children.
+ /// # Arguments
+ /// * `unit` - The unwraped, typed `CompilationUnit` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_compilation_unit(&mut self, unit: &CompilationUnit) {
+ unit.walk(self)
+ }
+
+ /// Visits an `Implementation` node.
+ /// Make sure to call `walk` on the `Implementation` node to visit its children.
+ /// # Arguments
+ /// * `implementation` - The unwraped, typed `Implementation` node to visit.
+ fn visit_implementation(&mut self, implementation: &Implementation) {
+ implementation.walk(self);
+ }
+
+ /// Visits a `DataTypeDeclaration` node.
+ /// Make sure to call `walk` on the `VariableBlock` node to visit its children.
+ /// # Arguments
+ /// * `block` - The unwraped, typed `VariableBlock` node to visit.
+ fn visit_variable_block(&mut self, block: &VariableBlock) {
+ block.walk(self)
+ }
+
+ /// Visits a `Variable` node.
+ /// Make sure to call `walk` on the `Variable` node to visit its children.
+ /// # Arguments
+ /// * `variable` - The unwraped, typed `Variable` node to visit.
+ fn visit_variable(&mut self, variable: &Variable) {
+ variable.walk(self);
+ }
+
+ /// Visits an enum element `AstNode` node.
+ /// Make sure to call `walk` on the `AstNode` node to visit its children.
+ /// # Arguments
+ /// * `element` - The unwraped, typed `AstNode` node to visit.
+ fn visit_enum_element(&mut self, element: &AstNode) {
+ element.walk(self);
+ }
+
+ /// Visits a `DataTypeDeclaration` node.
+ /// Make sure to call `walk` on the `DataTypeDeclaration` node to visit its children.
+ /// # Arguments
+ /// * `data_type_declaration` - The unwraped, typed `DataTypeDeclaration` node to visit.
+ fn visit_data_type_declaration(&mut self, data_type_declaration: &DataTypeDeclaration) {
+ data_type_declaration.walk(self);
+ }
+
+ /// Visits a `UserTypeDeclaration` node.
+ /// Make sure to call `walk` on the `UserTypeDeclaration` node to visit its children.
+ /// # Arguments
+ /// * `user_type` - The unwraped, typed `UserTypeDeclaration` node to visit.
+ fn visit_user_type_declaration(&mut self, user_type: &UserTypeDeclaration) {
+ user_type.walk(self);
+ }
+
+ /// Visits a `UserTypeDeclaration` node.
+ /// Make sure to call `walk` on the `DataType` node to visit its children.
+ /// # Arguments
+ /// * `data_type` - The unwraped, typed `DataType` node to visit.
+ fn visit_data_type(&mut self, data_type: &DataType) {
+ data_type.walk(self);
+ }
+
+ /// Visits a `Pou` node.
+ /// Make sure to call `walk` on the `Pou` node to visit its children.
+ /// # Arguments
+ /// * `pou` - The unwraped, typed `Pou` node to visit.
+ fn visit_pou(&mut self, pou: &Pou) {
+ pou.walk(self);
+ }
+
+ /// Visits an `EmptyStatement` node.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `EmptyStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_empty_statement(&mut self, _stmt: &EmptyStatement, _node: &AstNode) {}
+
+ /// Visits a `DefaultValue` node.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `DefaultValue` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_default_value(&mut self, _stmt: &DefaultValue, _node: &AstNode) {}
+
+ /// Visits an `AstLiteral` node.
+ /// Make sure to call `walk` on the `AstLiteral` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `AstLiteral` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_literal(&mut self, stmt: &AstLiteral, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `MultipliedStatement` node.
+ /// Make sure to call `walk` on the `MultipliedStatement` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `MultipliedStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_multiplied_statement(&mut self, stmt: &MultipliedStatement, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `ReferenceExpr` node.
+ /// Make sure to call `walk` on the `ReferenceExpr` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `ReferenceExpr` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_reference_expr(&mut self, stmt: &ReferenceExpr, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits an `Identifier` node.
+ /// Make sure to call `walk` on the `Identifier` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `Identifier` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_identifier(&mut self, _stmt: &str, _node: &AstNode) {}
+
+ /// Visits a `DirectAccess` node.
+ /// Make sure to call `walk` on the `DirectAccess` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `DirectAccess` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_direct_access(&mut self, stmt: &DirectAccess, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `HardwareAccess` node.
+ /// Make sure to call `walk` on the `HardwareAccess` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `HardwareAccess` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_hardware_access(&mut self, stmt: &HardwareAccess, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `BinaryExpression` node.
+ /// Make sure to call `walk` on the `BinaryExpression` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `BinaryExpression` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_binary_expression(&mut self, stmt: &BinaryExpression, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `UnaryExpression` node.
+ /// Make sure to call `walk` on the `UnaryExpression` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `UnaryExpression` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_unary_expression(&mut self, stmt: &UnaryExpression, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits an `ExpressionList` node.
+ /// Make sure to call `walk` on the `Vec` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `ExpressionList` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_expression_list(&mut self, stmt: &Vec, _node: &AstNode) {
+ visit_all_nodes!(self, stmt);
+ }
+
+ /// Visits a `ParenExpression` node.
+ /// Make sure to call `walk` on the inner `AstNode` node to visit its children.
+ /// # Arguments
+ /// * `inner` - The unwraped, typed inner `AstNode` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_paren_expression(&mut self, inner: &AstNode, _node: &AstNode) {
+ inner.walk(self)
+ }
+
+ /// Visits a `RangeStatement` node.
+ /// Make sure to call `walk` on the `RangeStatement` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `RangeStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_range_statement(&mut self, stmt: &RangeStatement, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `VlaRangeStatement` node.
+ /// # Arguments
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_vla_range_statement(&mut self, _node: &AstNode) {}
+
+ /// Visits an `Assignment` node.
+ /// Make sure to call `walk` on the `Assignment` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `Assignment` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits an `OutputAssignment` node.
+ /// Make sure to call `walk` on the `Assignment` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `Assignment` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_output_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits an `RefAssignment` node.
+ /// Make sure to call `walk` on the `Assignment` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `Assignment` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_ref_assignment(&mut self, stmt: &Assignment, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `CallStatement` node.
+ /// Make sure to call `walk` on the `CallStatement` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `CallStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_call_statement(&mut self, stmt: &CallStatement, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits an `AstControlStatement` node.
+ /// Make sure to call `walk` on the `AstControlStatement` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `AstControlStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_control_statement(&mut self, stmt: &AstControlStatement, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `CaseCondition` node.
+ /// Make sure to call `walk` on the child-`AstNode` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `CaseCondition` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_case_condition(&mut self, child: &AstNode, _node: &AstNode) {
+ child.walk(self)
+ }
+
+ /// Visits an `ExitStatement` node.
+ /// # Arguments
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_exit_statement(&mut self, _node: &AstNode) {}
+
+ /// Visits a `ContinueStatement` node.
+ /// # Arguments
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_continue_statement(&mut self, _node: &AstNode) {}
+
+ /// Visits a `ReturnStatement` node.
+ /// Make sure to call `walk` on the `ReturnStatement` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `ReturnStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_return_statement(&mut self, stmt: &ReturnStatement, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `JumpStatement` node.
+ /// Make sure to call `walk` on the `JumpStatement` node to visit its children.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `JumpStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_jump_statement(&mut self, stmt: &JumpStatement, _node: &AstNode) {
+ stmt.walk(self)
+ }
+
+ /// Visits a `LabelStatement` node.
+ /// # Arguments
+ /// * `stmt` - The unwraped, typed `LabelStatement` node to visit.
+ /// * `node` - The wrapped `AstNode` node to visit. Offers access to location information and AstId
+ fn visit_label_statement(&mut self, _stmt: &LabelStatement, _node: &AstNode) {}
+}
+
+/// Helper method that walks through a slice of `ConditionalBlock` and applies the visitor's `walk` method to each node.
+fn walk_conditional_blocks(visitor: &mut V, blocks: &[ConditionalBlock])
+where
+ V: AstVisitor,
+{
+ for b in blocks {
+ visit_nodes!(visitor, &b.condition);
+ visit_all_nodes!(visitor, &b.body);
+ }
+}
+
+impl Walker for AstLiteral {
+ fn walk(&self, _visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ // do nothing
+ }
+}
+
+impl Walker for MultipliedStatement {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visitor.visit(&self.element)
+ }
+}
+
+impl Walker for ReferenceExpr {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ if let Some(base) = &self.base {
+ visitor.visit(base);
+ }
+
+ match &self.access {
+ ReferenceAccess::Member(t) | ReferenceAccess::Index(t) | ReferenceAccess::Cast(t) => {
+ visitor.visit(t)
+ }
+ _ => {}
+ }
+ }
+}
+
+impl Walker for DirectAccess {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_nodes!(visitor, &self.index);
+ }
+}
+
+impl Walker for HardwareAccess {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_all_nodes!(visitor, &self.address);
+ }
+}
+
+impl Walker for BinaryExpression {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_nodes!(visitor, &self.left, &self.right);
+ }
+}
+
+impl Walker for UnaryExpression {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_nodes!(visitor, &self.value);
+ }
+}
+
+impl Walker for Assignment {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_nodes!(visitor, &self.left, &self.right);
+ }
+}
+
+impl Walker for RangeStatement {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_nodes!(visitor, &self.start, &self.end);
+ }
+}
+
+impl Walker for CallStatement {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_nodes!(visitor, &self.operator);
+ if let Some(params) = &self.parameters {
+ visit_nodes!(visitor, params);
+ }
+ }
+}
+
+impl Walker for AstControlStatement {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ match self {
+ AstControlStatement::If(stmt) => {
+ walk_conditional_blocks(visitor, &stmt.blocks);
+ visit_all_nodes!(visitor, &stmt.else_block);
+ }
+ AstControlStatement::WhileLoop(stmt) | AstControlStatement::RepeatLoop(stmt) => {
+ visit_nodes!(visitor, &stmt.condition);
+ visit_all_nodes!(visitor, &stmt.body);
+ }
+ AstControlStatement::ForLoop(stmt) => {
+ visit_nodes!(visitor, &stmt.counter, &stmt.start, &stmt.end);
+ visit_all_nodes!(visitor, &stmt.by_step);
+ visit_all_nodes!(visitor, &stmt.body);
+ }
+ AstControlStatement::Case(stmt) => {
+ visit_nodes!(visitor, &stmt.selector);
+ walk_conditional_blocks(visitor, &stmt.case_blocks);
+ visit_all_nodes!(visitor, &stmt.else_block);
+ }
+ }
+ }
+}
+
+impl Walker for ReturnStatement {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_all_nodes!(visitor, &self.condition);
+ }
+}
+
+impl Walker for JumpStatement {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_nodes!(visitor, &self.condition, &self.target);
+ }
+}
+
+impl Walker for AstNode {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ let node = self;
+ match &self.stmt {
+ AstStatement::EmptyStatement(stmt) => visitor.visit_empty_statement(stmt, node),
+ AstStatement::DefaultValue(stmt) => visitor.visit_default_value(stmt, node),
+ AstStatement::Literal(stmt) => visitor.visit_literal(stmt, node),
+ AstStatement::MultipliedStatement(stmt) => visitor.visit_multiplied_statement(stmt, node),
+ AstStatement::ReferenceExpr(stmt) => visitor.visit_reference_expr(stmt, node),
+ AstStatement::Identifier(stmt) => visitor.visit_identifier(stmt, node),
+ AstStatement::DirectAccess(stmt) => visitor.visit_direct_access(stmt, node),
+ AstStatement::HardwareAccess(stmt) => visitor.visit_hardware_access(stmt, node),
+ AstStatement::BinaryExpression(stmt) => visitor.visit_binary_expression(stmt, node),
+ AstStatement::UnaryExpression(stmt) => visitor.visit_unary_expression(stmt, node),
+ AstStatement::ExpressionList(stmt) => visitor.visit_expression_list(stmt, node),
+ AstStatement::ParenExpression(stmt) => visitor.visit_paren_expression(stmt, node),
+ AstStatement::RangeStatement(stmt) => visitor.visit_range_statement(stmt, node),
+ AstStatement::VlaRangeStatement => visitor.visit_vla_range_statement(node),
+ AstStatement::Assignment(stmt) => visitor.visit_assignment(stmt, node),
+ AstStatement::OutputAssignment(stmt) => visitor.visit_output_assignment(stmt, node),
+ AstStatement::RefAssignment(stmt) => visitor.visit_ref_assignment(stmt, node),
+ AstStatement::CallStatement(stmt) => visitor.visit_call_statement(stmt, node),
+ AstStatement::ControlStatement(stmt) => visitor.visit_control_statement(stmt, node),
+ AstStatement::CaseCondition(stmt) => visitor.visit_case_condition(stmt, node),
+ AstStatement::ExitStatement(_stmt) => visitor.visit_exit_statement(node),
+ AstStatement::ContinueStatement(_stmt) => visitor.visit_continue_statement(node),
+ AstStatement::ReturnStatement(stmt) => visitor.visit_return_statement(stmt, node),
+ AstStatement::JumpStatement(stmt) => visitor.visit_jump_statement(stmt, node),
+ AstStatement::LabelStatement(stmt) => visitor.visit_label_statement(stmt, node),
+ }
+ }
+}
+
+impl Walker for CompilationUnit {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ for block in &self.global_vars {
+ visitor.visit_variable_block(block);
+ }
+
+ for user_type in &self.user_types {
+ visitor.visit_user_type_declaration(user_type);
+ }
+
+ for pou in &self.units {
+ visitor.visit_pou(pou);
+ }
+
+ for i in &self.implementations {
+ visitor.visit_implementation(i);
+ }
+ }
+}
+
+impl Walker for UserTypeDeclaration {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visitor.visit_data_type(&self.data_type);
+ visit_all_nodes!(visitor, &self.initializer);
+ }
+}
+
+impl Walker for VariableBlock {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ for v in self.variables.iter() {
+ visitor.visit_variable(v);
+ }
+ }
+}
+
+impl Walker for Variable {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ visit_all_nodes!(visitor, &self.address);
+ visitor.visit_data_type_declaration(&self.data_type_declaration);
+ visit_all_nodes!(visitor, &self.initializer);
+ }
+}
+
+impl Walker for DataType {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ match self {
+ DataType::StructType { variables, .. } => {
+ for v in variables.iter() {
+ visitor.visit_variable(v);
+ }
+ }
+ DataType::EnumType { elements, .. } => {
+ for ele in flatten_expression_list(elements) {
+ visitor.visit_enum_element(ele);
+ }
+ }
+ DataType::SubRangeType { bounds, .. } => {
+ visit_all_nodes!(visitor, bounds);
+ }
+ DataType::ArrayType { bounds, referenced_type, .. } => {
+ visitor.visit(bounds);
+ visitor.visit_data_type_declaration(referenced_type);
+ }
+ DataType::PointerType { referenced_type, .. } => {
+ visitor.visit_data_type_declaration(referenced_type);
+ }
+ DataType::StringType { size, .. } => {
+ visit_all_nodes!(visitor, size);
+ }
+ DataType::VarArgs { referenced_type, .. } => {
+ if let Some(data_type_declaration) = referenced_type {
+ visitor.visit_data_type_declaration(data_type_declaration);
+ }
+ }
+ DataType::GenericType { .. } => {
+ //no further visits
+ }
+ }
+ }
+}
+
+impl Walker for DataTypeDeclaration {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ if let DataTypeDeclaration::DataTypeDefinition { data_type, .. } = self {
+ visitor.visit_data_type(data_type);
+ }
+ }
+}
+
+impl Walker for Option
+where
+ T: Walker,
+{
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ if let Some(node) = self {
+ node.walk(visitor);
+ }
+ }
+}
+
+impl Walker for Pou {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ for block in &self.variable_blocks {
+ visitor.visit_variable_block(block);
+ }
+
+ self.return_type.as_ref().inspect(|rt| visitor.visit_data_type_declaration(rt));
+ }
+}
+
+impl Walker for Implementation {
+ fn walk(&self, visitor: &mut V)
+ where
+ V: AstVisitor,
+ {
+ for n in &self.statements {
+ visitor.visit(n);
+ }
+ }
+}
diff --git a/compiler/plc_diagnostics/src/diagnostics.rs b/compiler/plc_diagnostics/src/diagnostics.rs
index d45546a92d..1aa1441df2 100644
--- a/compiler/plc_diagnostics/src/diagnostics.rs
+++ b/compiler/plc_diagnostics/src/diagnostics.rs
@@ -2,6 +2,7 @@ use std::fmt::Display;
use serde::{Deserialize, Serialize};
+use crate::diagnostics::diagnostics_registry::DIAGNOSTICS;
use plc_ast::ast::AstNode;
use plc_source::{
source_location::{SourceLocation, SourceLocationFactory},
@@ -88,8 +89,10 @@ impl Diagnostic {
self
}
- pub fn with_error_code(mut self, error_code: &'static str) -> Self {
- self.error_code = error_code;
+ pub fn with_error_code(mut self, code: &'static str) -> Self {
+ debug_assert!(DIAGNOSTICS.get(code).is_some(), "Error {code} does not exist");
+
+ self.error_code = code;
self
}
@@ -199,12 +202,26 @@ impl Diagnostic {
.with_error_code("E006")
}
- pub fn invalid_parameter_count(expected: usize, received: usize, location: SourceLocation) -> Diagnostic {
- Diagnostic::new(
- format!(
- "Invalid parameter count. Received {received} parameters while {expected} parameters were expected.",
- )).with_error_code("E032")
- .with_location(location)
+ pub fn invalid_argument_count(expected: usize, actual: usize, location: T) -> Diagnostic
+ where
+ T: Into,
+ {
+ // Let's be extra fancy here 🕺
+ fn message(value: usize) -> String {
+ match value {
+ 1 => format!("{value} argument"),
+ _ => format!("{value} arguments"),
+ }
+ }
+
+ Diagnostic::new(format!(
+ "this POU takes {expected} but {actual} {was_or_were} supplied",
+ expected = message(expected),
+ actual = message(actual),
+ was_or_were = if actual == 1 { "was" } else { "were" }
+ ))
+ .with_error_code("E032")
+ .with_location(location.into())
}
pub fn unknown_type(type_name: &str, location: SourceLocation) -> Diagnostic {
@@ -217,7 +234,10 @@ impl Diagnostic {
.with_location(location)
}
- pub fn invalid_assignment(right_type: &str, left_type: &str, location: SourceLocation) -> Diagnostic {
+ pub fn invalid_assignment(right_type: &str, left_type: &str, location: T) -> Diagnostic
+ where
+ T: Into,
+ {
Diagnostic::new(format!("Invalid assignment: cannot assign '{right_type}' to '{left_type}'"))
.with_error_code("E037")
.with_location(location)
diff --git a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs
index b9999558bc..f3db285d77 100644
--- a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs
+++ b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs
@@ -99,296 +99,110 @@ impl From<&DiagnosticsRegistry> for DiagnosticsConfiguration {
}
}
+#[rustfmt::skip]
lazy_static! {
- static ref DIAGNOSTICS: FxHashMap<&'static str, DiagnosticEntry> = add_diagnostic!(
- E001,
- Error,
- include_str!("./error_codes/E001.md"), //General Error
- E002,
- Error,
- include_str!("./error_codes/E002.md"), //General IO Error
- E003,
- Error,
- include_str!("./error_codes/E003.md"), //Parameter Error
- E004,
- Error,
- include_str!("./error_codes/E004.md"), //Duplicate Symbol
- E005,
- Error,
- include_str!("./error_codes/E005.md"), //Generic LLVM Error
- E006,
- Error,
- include_str!("./error_codes/E006.md"), //Missing Token
- E007,
- Error,
- include_str!("./error_codes/E007.md"), //Unexpected Token
- E008,
- Error,
- include_str!("./error_codes/E008.md"), //Invalid Range
- E009,
- Error,
- include_str!("./error_codes/E009.md"), //Mismatched Parantheses
- E010,
- Error,
- include_str!("./error_codes/E010.md"), //Invalid Time Literal
- E011,
- Error,
- include_str!("./error_codes/E011.md"), //Invalid Number
- E012,
- Error,
- include_str!("./error_codes/E012.md"), //Missing Case Condition
- E013,
- Warning,
- include_str!("./error_codes/E013.md"), //Keywords shoud contain underscores
- E014,
- Warning,
- include_str!("./error_codes/E014.md"), //Wrong parantheses type
- E015,
- Warning,
- include_str!("./error_codes/E015.md"), //Pointer is not standard
- E016,
- Warning,
- include_str!("./error_codes/E016.md"), //Return types cannot have a default value
- E017,
- Error,
- include_str!("./error_codes/E017.md"), //Classes cannot contain implementations
- E018,
- Error,
- include_str!("./error_codes/E018.md"), //Duplicate Label
- E019,
- Error,
- include_str!("./error_codes/E019.md"), //Classes cannot contain IN_OUT variables
- E020,
- Error,
- include_str!("./error_codes/E020.md"), //Classes cannot contain a return type
- E021,
- Error,
- include_str!("./error_codes/E021.md"), //POUs cannot be extended
- E022,
- Warning,
- include_str!("./error_codes/E022.md"), //Missing action container
- E023,
- Warning,
- include_str!("./error_codes/E023.md"), //Statement with no effect
- E024,
- Warning,
- include_str!("./error_codes/E024.md"), //Invalid pragma location
- E025,
- Error,
- include_str!("./error_codes/E025.md"), // Missing return type
- E026,
- Error,
- include_str!("./error_codes/E026.md"),
- E027,
- Error,
- include_str!("./error_codes/E027.md"),
- E028,
- Error,
- include_str!("./error_codes/E028.md"),
- E029,
- Error,
- include_str!("./error_codes/E029.md"),
- E030,
- Error,
- include_str!("./error_codes/E030.md"),
- E031,
- Error,
- include_str!("./error_codes/E031.md"),
- E032,
- Error,
- include_str!("./error_codes/E032.md"),
- E033,
- Error,
- include_str!("./error_codes/E033.md"),
- E034,
- Error,
- include_str!("./error_codes/E034.md"),
- E035,
- Error,
- include_str!("./error_codes/E035.md"),
- E036,
- Error,
- include_str!("./error_codes/E036.md"),
- E037,
- Error,
- include_str!("./error_codes/E037.md"),
- E038,
- Error,
- include_str!("./error_codes/E038.md"), //Missing type
- E039,
- Warning,
- include_str!("./error_codes/E039.md"),
- E040,
- Error,
- include_str!("./error_codes/E040.md"),
- E041,
- Error,
- include_str!("./error_codes/E041.md"),
- E042,
- Warning,
- include_str!("./error_codes/E042.md"), //Assignment to reference
- E043,
- Error,
- include_str!("./error_codes/E043.md"),
- E044,
- Error,
- include_str!("./error_codes/E044.md"),
- E045,
- Error,
- include_str!("./error_codes/E045.md"),
- E046,
- Error,
- include_str!("./error_codes/E046.md"),
- E047,
- Warning,
- include_str!("./error_codes/E047.md"), //VLAs are always by reference
- E048,
- Error,
- include_str!("./error_codes/E048.md"),
- E049,
- Error,
- include_str!("./error_codes/E049.md"),
- E050,
- Error,
- include_str!("./error_codes/E050.md"),
- E051,
- Error,
- include_str!("./error_codes/E051.md"),
- E052,
- Error,
- include_str!("./error_codes/E052.md"),
- E053,
- Error,
- include_str!("./error_codes/E053.md"),
- E054,
- Error,
- include_str!("./error_codes/E054.md"),
- E055,
- Error,
- include_str!("./error_codes/E055.md"),
- E056,
- Error,
- include_str!("./error_codes/E056.md"),
- E057,
- Error,
- include_str!("./error_codes/E057.md"),
- E058,
- Error,
- include_str!("./error_codes/E058.md"),
- E059,
- Error,
- include_str!("./error_codes/E059.md"),
- E060,
- Info,
- include_str!("./error_codes/E060.md"), //Variable direct access with %
- E061,
- Error,
- include_str!("./error_codes/E061.md"),
- E062,
- Error,
- include_str!("./error_codes/E062.md"),
- E063,
- Error,
- include_str!("./error_codes/E063.md"),
- E064,
- Error,
- include_str!("./error_codes/E064.md"),
- E065,
- Error,
- include_str!("./error_codes/E065.md"),
- E066,
- Error,
- include_str!("./error_codes/E066.md"),
- E067,
- Warning,
- include_str!("./error_codes/E067.md"), //Implicit typecast
- E068,
- Error,
- include_str!("./error_codes/E068.md"),
- E069,
- Error,
- include_str!("./error_codes/E069.md"),
- E070,
- Error,
- include_str!("./error_codes/E070.md"),
- E071,
- Error,
- include_str!("./error_codes/E071.md"),
- E072,
- Error,
- include_str!("./error_codes/E072.md"),
- E073,
- Error,
- include_str!("./error_codes/E073.md"),
- E074,
- Error,
- include_str!("./error_codes/E074.md"),
- E075,
- Error,
- include_str!("./error_codes/E075.md"),
- E076,
- Error,
- include_str!("./error_codes/E076.md"),
- E077,
- Error,
- include_str!("./error_codes/E077.md"),
- E078,
- Error,
- include_str!("./error_codes/E078.md"),
- E079,
- Error,
- include_str!("./error_codes/E079.md"),
- E080,
- Error,
- include_str!("./error_codes/E080.md"),
- E081,
- Error,
- include_str!("./error_codes/E081.md"),
- E082,
- Error,
- include_str!("./error_codes/E082.md"),
- E083,
- Error,
- include_str!("./error_codes/E083.md"),
- E084,
- Error,
- include_str!("./error_codes/E084.md"),
- E085,
- Error,
- include_str!("./error_codes/E085.md"),
- E086,
- Error,
- include_str!("./error_codes/E086.md"),
- E087,
- Error,
- include_str!("./error_codes/E087.md"),
- E088,
- Error,
- include_str!("./error_codes/E088.md"),
- E089,
- Error,
- include_str!("./error_codes/E089.md"),
- E090,
- Warning,
- include_str!("./error_codes/E090.md"), //Incompatible reference Assignment
- E091,
- Warning,
- include_str!("./error_codes/E091.md"),
- E092,
- Info,
- include_str!("./error_codes/E092.md"),
- E093,
- Warning,
- include_str!("./error_codes/E093.md"),
- E094,
- Error,
- include_str!("./error_codes/E094.md"),
- E095,
- Error,
- include_str!("./error_codes/E095.md"), // Action call without `()`
- E096, Warning, include_str!("./error_codes/E096.md"), // Integer Condition
- E097, Error, include_str!("./error_codes/E097.md"), // Invalid Array Range
-);
+ pub static ref DIAGNOSTICS: FxHashMap<&'static str, DiagnosticEntry> = add_diagnostic!(
+ E001, Error, include_str!("./error_codes/E001.md"), //General Error
+ E002, Error, include_str!("./error_codes/E002.md"), //General IO Error
+ E003, Error, include_str!("./error_codes/E003.md"), //Parameter Error
+ E004, Error, include_str!("./error_codes/E004.md"), //Duplicate Symbol
+ E005, Error, include_str!("./error_codes/E005.md"), //Generic LLVM Error
+ E006, Error, include_str!("./error_codes/E006.md"), //Missing Token
+ E007, Error, include_str!("./error_codes/E007.md"), //Unexpected Token
+ E008, Error, include_str!("./error_codes/E008.md"), //Invalid Range
+ E009, Error, include_str!("./error_codes/E009.md"), //Mismatched Parantheses
+ E010, Error, include_str!("./error_codes/E010.md"), //Invalid Time Literal
+ E011, Error, include_str!("./error_codes/E011.md"), //Invalid Number
+ E012, Error, include_str!("./error_codes/E012.md"), //Missing Case Condition
+ E013, Warning, include_str!("./error_codes/E013.md"), //Keywords shoud contain underscores
+ E014, Warning, include_str!("./error_codes/E014.md"), //Wrong parantheses type
+ E015, Warning, include_str!("./error_codes/E015.md"), //Pointer is not standard
+ E016, Warning, include_str!("./error_codes/E016.md"), //Return types cannot have a default value
+ E017, Error, include_str!("./error_codes/E017.md"), //Classes cannot contain implementations
+ E018, Error, include_str!("./error_codes/E018.md"), //Duplicate Label
+ E019, Error, include_str!("./error_codes/E019.md"), //Classes cannot contain IN_OUT variables
+ E020, Error, include_str!("./error_codes/E020.md"), //Classes cannot contain a return type
+ E021, Error, include_str!("./error_codes/E021.md"), //POUs cannot be extended
+ E022, Warning, include_str!("./error_codes/E022.md"), //Missing action container
+ E023, Warning, include_str!("./error_codes/E023.md"), //Statement with no effect
+ E024, Warning, include_str!("./error_codes/E024.md"), //Invalid pragma location
+ E025, Error, include_str!("./error_codes/E025.md"), // Missing return type
+ E026, Error, include_str!("./error_codes/E026.md"),
+ E027, Error, include_str!("./error_codes/E027.md"),
+ E028, Error, include_str!("./error_codes/E028.md"),
+ E029, Error, include_str!("./error_codes/E029.md"),
+ E030, Error, include_str!("./error_codes/E030.md"),
+ E031, Error, include_str!("./error_codes/E031.md"),
+ E032, Error, include_str!("./error_codes/E032.md"),
+ E033, Error, include_str!("./error_codes/E033.md"),
+ E034, Error, include_str!("./error_codes/E034.md"),
+ E035, Error, include_str!("./error_codes/E035.md"),
+ E036, Error, include_str!("./error_codes/E036.md"),
+ E037, Error, include_str!("./error_codes/E037.md"),
+ E038, Error, include_str!("./error_codes/E038.md"), //Missing type
+ E039, Warning, include_str!("./error_codes/E039.md"),
+ E040, Error, include_str!("./error_codes/E040.md"),
+ E041, Error, include_str!("./error_codes/E041.md"),
+ E042, Warning, include_str!("./error_codes/E042.md"), //Assignment to reference
+ E043, Error, include_str!("./error_codes/E043.md"),
+ E044, Error, include_str!("./error_codes/E044.md"),
+ E045, Error, include_str!("./error_codes/E045.md"),
+ E046, Error, include_str!("./error_codes/E046.md"),
+ E047, Warning, include_str!("./error_codes/E047.md"), //VLAs are always by reference
+ E048, Error, include_str!("./error_codes/E048.md"),
+ E049, Error, include_str!("./error_codes/E049.md"),
+ E050, Error, include_str!("./error_codes/E050.md"),
+ E051, Error, include_str!("./error_codes/E051.md"),
+ E052, Error, include_str!("./error_codes/E052.md"),
+ E053, Error, include_str!("./error_codes/E053.md"),
+ E054, Error, include_str!("./error_codes/E054.md"),
+ E055, Error, include_str!("./error_codes/E055.md"),
+ E056, Error, include_str!("./error_codes/E056.md"),
+ E057, Error, include_str!("./error_codes/E057.md"),
+ E058, Error, include_str!("./error_codes/E058.md"),
+ E059, Error, include_str!("./error_codes/E059.md"),
+ E060, Info, include_str!("./error_codes/E060.md"), //Variable direct access with %
+ E061, Error, include_str!("./error_codes/E061.md"),
+ E062, Error, include_str!("./error_codes/E062.md"),
+ E063, Error, include_str!("./error_codes/E063.md"),
+ E064, Error, include_str!("./error_codes/E064.md"),
+ E065, Error, include_str!("./error_codes/E065.md"),
+ E066, Error, include_str!("./error_codes/E066.md"),
+ E067, Warning, include_str!("./error_codes/E067.md"), //Implicit typecast
+ E068, Error, include_str!("./error_codes/E068.md"),
+ E069, Error, include_str!("./error_codes/E069.md"),
+ E070, Error, include_str!("./error_codes/E070.md"),
+ E071, Error, include_str!("./error_codes/E071.md"),
+ E072, Error, include_str!("./error_codes/E072.md"),
+ E073, Error, include_str!("./error_codes/E073.md"),
+ E074, Error, include_str!("./error_codes/E074.md"),
+ E075, Error, include_str!("./error_codes/E075.md"),
+ E076, Error, include_str!("./error_codes/E076.md"),
+ E077, Error, include_str!("./error_codes/E077.md"),
+ E078, Error, include_str!("./error_codes/E078.md"),
+ E079, Error, include_str!("./error_codes/E079.md"),
+ E080, Error, include_str!("./error_codes/E080.md"),
+ E081, Error, include_str!("./error_codes/E081.md"),
+ E082, Error, include_str!("./error_codes/E082.md"),
+ E083, Error, include_str!("./error_codes/E083.md"),
+ E084, Error, include_str!("./error_codes/E084.md"),
+ E085, Error, include_str!("./error_codes/E085.md"),
+ E086, Error, include_str!("./error_codes/E086.md"),
+ E087, Error, include_str!("./error_codes/E087.md"),
+ E088, Error, include_str!("./error_codes/E088.md"),
+ E089, Error, include_str!("./error_codes/E089.md"),
+ E090, Warning, include_str!("./error_codes/E090.md"), //Incompatible reference Assignment
+ E091, Warning, include_str!("./error_codes/E091.md"),
+ E092, Info, include_str!("./error_codes/E092.md"),
+ E093, Warning, include_str!("./error_codes/E093.md"),
+ E094, Error, include_str!("./error_codes/E094.md"),
+ E095, Error, include_str!("./error_codes/E095.md"), // Action call without `()`
+ E096, Warning, include_str!("./error_codes/E096.md"), // Integer Condition
+ E097, Error, include_str!("./error_codes/E097.md"), // Invalid Array Range
+ E098, Error, include_str!("./error_codes/E098.md"), // Invalid `REF=` assignment
+ E099, Error, include_str!("./error_codes/E099.md"), // Invalid `REFERENCE TO` declaration
+ E100, Error, include_str!("./error_codes/E100.md"), // Immutable variable address
+ );
}
#[cfg(test)]
diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E032.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E032.md
index 53e170129c..cb2b5ed020 100644
--- a/compiler/plc_diagnostics/src/diagnostics/error_codes/E032.md
+++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E032.md
@@ -1 +1,18 @@
-# Invalid number of parameters
+# Invalid number of arguments
+
+An invalid number of arguments was passed to a POU. For example
+
+```
+FUNCTION foo
+ (* ... *)
+END_FUNCTION
+
+FUNCTION main : DINT
+ foo('bar'); // Error, foo isn't expecting any arguments
+END_FUNCTION
+```
+
+Note that for `FUNCTION`s the argument count must match with the parameter list and can be bigger if a variadic
+parameter is present. For stateful POUs variadic parameters are not supported, thus the argument count must be equal
+or less than the parameter list depending on whether optional arguments such as `VAR_INPUT` or `VAR_OUTPUT` were
+passed or not.
diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E098.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E098.md
new file mode 100644
index 0000000000..f550271947
--- /dev/null
+++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E098.md
@@ -0,0 +1,19 @@
+# Invalid REF= assignment
+
+`REF=` assignments are considered valid if the left-hand side of the assignment is a pointer variable
+and the right-hand side is a variable of the type that is being referenced.
+
+For example assignments such as the following are invalid
+
+```smalltalk
+VAR
+ foo : DINT;
+ bar : DINT;
+ qux : SINT;
+ refFoo : REFERENCE TO DINT;
+END_VAR
+
+refFoo REF= 5; // `5` is not a variable
+foo REF= bar; // `foo` is not a pointer
+refFoo REF= qux; // `refFoo` and `qux` have different types, DINT vs SINT
+```
\ No newline at end of file
diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E099.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E099.md
new file mode 100644
index 0000000000..f78cc78448
--- /dev/null
+++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E099.md
@@ -0,0 +1,6 @@
+# Invalid `REFERENCE TO` declaration
+
+`REFERENCE TO` variable declarations are considered valid if the referenced type is not of the following form
+* `foo : REFERENCE TO REFERENCE TO (* ... *)`
+* `foo : ARRAY[...] OF REFERENCE TO (* ... *)`
+* `foo : REF_TO REFERENCE TO (* ... *)`
\ No newline at end of file
diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E100.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E100.md
new file mode 100644
index 0000000000..5d9e852ac4
--- /dev/null
+++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E100.md
@@ -0,0 +1,15 @@
+# Immutable Variable Address
+
+Alias variables are immutable with regards to their pointer address, thus re-assigning an address will return an error. For example the following code will not compile
+```ST
+FUNCTION main
+ VAR
+ foo AT bar : DINT;
+ bar : DINT;
+ baz : DINT;
+ END_VAR
+
+ foo := baz; // Valid, because we are changing the pointers dereferenced value
+ foo REF= baz; // Invalid, `foo` is immutable with regards to it's pointer address
+END_FUNCTION
+```
\ No newline at end of file
diff --git a/compiler/plc_driver/Cargo.toml b/compiler/plc_driver/Cargo.toml
index 743087961a..8eccbdd01a 100644
--- a/compiler/plc_driver/Cargo.toml
+++ b/compiler/plc_driver/Cargo.toml
@@ -16,6 +16,7 @@ plc_index = { path = "../plc_index" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
+toml = "0.5"
clap = { version = "3.0", features = ["derive"] }
rayon = "1.6.1"
tempfile = "3"
diff --git a/compiler/plc_driver/src/cli.rs b/compiler/plc_driver/src/cli.rs
index b086c79cfd..960b88fb87 100644
--- a/compiler/plc_driver/src/cli.rs
+++ b/compiler/plc_driver/src/cli.rs
@@ -22,6 +22,14 @@ pub struct CompileParameters {
#[clap(short, long, global = true, name = "output-file", help = "Write output to ")]
pub output: Option,
+ #[clap(
+ long = "ast",
+ group = "format",
+ global = true,
+ help = "Emit AST (Abstract Syntax Tree) as output"
+ )]
+ pub output_ast: bool,
+
#[clap(
long = "ir",
group = "format",
@@ -100,6 +108,18 @@ pub struct CompileParameters {
) ]
pub hardware_config: Option,
+ #[clap(
+ name = "got-layout-file",
+ long,
+ global = true,
+ help = "Obtain information about the current custom GOT layout from the given file if it exists.
+ Save information about the generated custom GOT layout to the given file.
+ Format is detected by extension.
+ Supported formats : json, toml",
+ parse(try_from_str = validate_config)
+ ) ]
+ pub got_layout_file: Option,
+
#[clap(
name = "optimization",
long,
@@ -193,6 +213,12 @@ pub struct CompileParameters {
#[clap(name = "check", long, help = "Check only, do not generate any output", global = true)]
pub check_only: bool,
+ #[clap(
+ long,
+ help = "Emit a binary with specific compilation information, suitable for online changes when ran under a conforming runtime"
+ )]
+ pub online_change: bool,
+
#[clap(subcommand)]
pub commands: Option,
}
@@ -308,7 +334,9 @@ pub fn get_config_format(name: &str) -> Option {
impl CompileParameters {
pub fn parse + AsRef>(args: &[T]) -> Result {
- CompileParameters::try_parse_from(args).and_then(|result| {
+ CompileParameters::try_parse_from(args).and_then(|mut result| {
+ result.got_layout_file = Some(String::from("tmp.json"));
+
if result.sysroot.len() > result.target.len() {
let mut cmd = CompileParameters::command();
Err(cmd.error(
@@ -379,6 +407,10 @@ impl CompileParameters {
self.hardware_config.as_deref().and_then(get_config_format)
}
+ pub fn got_layout_format(&self) -> Option {
+ self.got_layout_file.as_deref().and_then(get_config_format)
+ }
+
/// Returns the location where the build artifacts should be stored / output
pub fn get_build_location(&self) -> Option {
match &self.commands {
@@ -637,6 +669,16 @@ mod cli_tests {
fn valid_output_formats() {
let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--ir")).unwrap();
assert!(parameters.output_ir);
+ assert!(!parameters.output_ast);
+ assert!(!parameters.output_bit_code);
+ assert!(!parameters.output_obj_code);
+ assert!(!parameters.output_pic_obj);
+ assert!(!parameters.output_shared_obj);
+ assert!(!parameters.output_reloc_code);
+
+ let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--ast")).unwrap();
+ assert!(!parameters.output_ir);
+ assert!(parameters.output_ast);
assert!(!parameters.output_bit_code);
assert!(!parameters.output_obj_code);
assert!(!parameters.output_pic_obj);
@@ -645,6 +687,7 @@ mod cli_tests {
let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--bc")).unwrap();
assert!(!parameters.output_ir);
+ assert!(!parameters.output_ast);
assert!(parameters.output_bit_code);
assert!(!parameters.output_obj_code);
assert!(!parameters.output_pic_obj);
@@ -653,6 +696,7 @@ mod cli_tests {
let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--static")).unwrap();
assert!(!parameters.output_ir);
+ assert!(!parameters.output_ast);
assert!(!parameters.output_bit_code);
assert!(parameters.output_obj_code);
assert!(!parameters.output_pic_obj);
@@ -661,6 +705,7 @@ mod cli_tests {
let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--pic")).unwrap();
assert!(!parameters.output_ir);
+ assert!(!parameters.output_ast);
assert!(!parameters.output_bit_code);
assert!(!parameters.output_obj_code);
assert!(parameters.output_pic_obj);
@@ -669,6 +714,7 @@ mod cli_tests {
let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--shared")).unwrap();
assert!(!parameters.output_ir);
+ assert!(!parameters.output_ast);
assert!(!parameters.output_bit_code);
assert!(!parameters.output_obj_code);
assert!(!parameters.output_pic_obj);
@@ -677,6 +723,7 @@ mod cli_tests {
let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--relocatable")).unwrap();
assert!(!parameters.output_ir);
+ assert!(!parameters.output_ast);
assert!(!parameters.output_bit_code);
assert!(!parameters.output_obj_code);
assert!(!parameters.output_pic_obj);
@@ -685,6 +732,7 @@ mod cli_tests {
let parameters = CompileParameters::parse(vec_of_strings!("input.st")).unwrap();
assert!(!parameters.output_ir);
+ assert!(!parameters.output_ast);
assert!(!parameters.output_bit_code);
assert!(!parameters.output_obj_code);
assert!(!parameters.output_pic_obj);
diff --git a/compiler/plc_driver/src/lib.rs b/compiler/plc_driver/src/lib.rs
index 750095032c..67460c6052 100644
--- a/compiler/plc_driver/src/lib.rs
+++ b/compiler/plc_driver/src/lib.rs
@@ -19,8 +19,8 @@ use std::{
use cli::{CompileParameters, ParameterError, SubCommands};
use pipelines::AnnotatedProject;
use plc::{
- codegen::CodegenContext, linker::LinkerType, output::FormatOption, DebugLevel, ErrorFormat,
- OptimizationLevel, Target, Threads,
+ codegen::CodegenContext, linker::LinkerType, output::FormatOption, ConfigFormat, DebugLevel, ErrorFormat,
+ OnlineChange, OptimizationLevel, Target, Threads,
};
use plc_diagnostics::{diagnostician::Diagnostician, diagnostics::Diagnostic};
@@ -50,10 +50,13 @@ pub struct CompileOptions {
/// The name of the resulting compiled file
pub output: String,
pub output_format: FormatOption,
+ pub got_layout_file: Option,
+ pub got_layout_format: Option,
pub optimization: OptimizationLevel,
pub error_format: ErrorFormat,
pub debug_level: DebugLevel,
pub single_module: bool,
+ pub online_change: OnlineChange,
}
impl Default for CompileOptions {
@@ -63,10 +66,13 @@ impl Default for CompileOptions {
build_location: None,
output: String::new(),
output_format: Default::default(),
+ got_layout_file: None,
+ got_layout_format: None,
optimization: OptimizationLevel::None,
error_format: ErrorFormat::None,
debug_level: DebugLevel::None,
single_module: false,
+ online_change: OnlineChange::Disabled,
}
}
}
@@ -172,10 +178,17 @@ pub fn get_compilation_context + AsRef + Debug>(
build_location: compile_parameters.get_build_location(),
output: project.get_output_name(),
output_format,
+ got_layout_file: compile_parameters.got_layout_file.clone(),
+ got_layout_format: compile_parameters.got_layout_format(),
optimization: compile_parameters.optimization,
error_format: compile_parameters.error_format,
debug_level: compile_parameters.debug_level(),
single_module: compile_parameters.single_module,
+ online_change: if compile_parameters.online_change {
+ OnlineChange::Enabled
+ } else {
+ OnlineChange::Disabled
+ },
};
let libraries =
@@ -247,6 +260,11 @@ pub fn compile_with_options(compile_options: CompilationContext) -> Result<()> {
.index(ctxt.provider())
.annotate(ctxt.provider());
+ if compile_parameters.output_ast {
+ println!("{:#?}", annotated_project.units);
+ return Ok(());
+ }
+
// 4 : Validate
annotated_project.validate(&ctxt, &mut diagnostician)?;
diff --git a/compiler/plc_driver/src/pipelines.rs b/compiler/plc_driver/src/pipelines.rs
index 67d8991311..80dd6c7766 100644
--- a/compiler/plc_driver/src/pipelines.rs
+++ b/compiler/plc_driver/src/pipelines.rs
@@ -1,8 +1,10 @@
use std::{
+ collections::HashMap,
env,
fs::{self, File},
io::Write,
path::{Path, PathBuf},
+ sync::Mutex,
};
use crate::{CompileOptions, LinkOptions};
@@ -33,6 +35,42 @@ use project::{
use rayon::prelude::*;
use source_code::{source_location::SourceLocation, SourceContainer};
+use serde_json;
+use toml;
+
+pub fn read_got_layout(location: &str, format: ConfigFormat) -> Result, Diagnostic> {
+ if !Path::new(location).is_file() {
+ // Assume if the file doesn't exist that there is no existing GOT layout yet. write_got_layout will handle
+ // creating our file when we want to.
+ return Ok(HashMap::new());
+ }
+
+ let s = fs::read_to_string(location)
+ .map_err(|_| Diagnostic::new("GOT layout could not be read from file"))?;
+ match format {
+ ConfigFormat::JSON => serde_json::from_str(&s)
+ .map_err(|_| Diagnostic::new("Could not deserialize GOT layout from JSON")),
+ ConfigFormat::TOML => {
+ toml::de::from_str(&s).map_err(|_| Diagnostic::new("Could not deserialize GOT layout from TOML"))
+ }
+ }
+}
+
+fn write_got_layout(
+ got_entries: HashMap,
+ location: &str,
+ format: ConfigFormat,
+) -> Result<(), Diagnostic> {
+ let s = match format {
+ ConfigFormat::JSON => serde_json::to_string(&got_entries)
+ .map_err(|_| Diagnostic::new("Could not serialize GOT layout to JSON"))?,
+ ConfigFormat::TOML => toml::ser::to_string(&got_entries)
+ .map_err(|_| Diagnostic::new("Could not serialize GOT layout to TOML"))?,
+ };
+
+ fs::write(location, s).map_err(|_| Diagnostic::new("GOT layout could not be written to file"))
+}
+
///Represents a parsed project
///For this struct to be built, the project would have been parsed correctly and an AST would have
///been generated
@@ -234,8 +272,15 @@ impl AnnotatedProject {
.iter()
.map(|(unit, dependencies, literals)| {
let context = CodegenContext::create();
- self.generate_module(&context, compile_options, unit, dependencies, literals)
- .map(|it| it.persist_to_string())
+ self.generate_module(
+ &context,
+ compile_options,
+ unit,
+ dependencies,
+ literals,
+ todo!("GOT layout for codegen_to_string?"),
+ )
+ .map(|it| it.persist_to_string())
})
.collect()
}
@@ -249,7 +294,14 @@ impl AnnotatedProject {
.units
.iter()
.map(|(unit, dependencies, literals)| {
- self.generate_module(context, compile_options, unit, dependencies, literals)
+ self.generate_module(
+ context,
+ compile_options,
+ unit,
+ dependencies,
+ literals,
+ todo!("give GOT layout for single modules?"),
+ )
})
.reduce(|a, b| {
let a = a?;
@@ -269,13 +321,16 @@ impl AnnotatedProject {
unit: &CompilationUnit,
dependencies: &FxIndexSet,
literals: &StringLiterals,
+ got_layout: &Mutex