Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.1.0"),
.package(url: "https://github.com/mipalgu/VHDLMachines", from: "1.2.4"),
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"),
.package(url: "https://github.com/mipalgu/VHDLMachines", from: "2.0.0"),
.package(url: "https://github.com/mipalgu/VHDLParsing", from: "2.4.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
.package(url: "https://github.com/mipalgu/VHDLKripkeStructureGenerator.git", from: "0.1.2"),
.package(url: "https://github.com/mipalgu/swift_helpers.git", from: "2.0.0")
.package(url: "https://github.com/mipalgu/VHDLKripkeStructureGenerator.git", from: "0.2.0"),
.package(url: "https://github.com/CPSLabGU/SwiftUtils.git", from: "0.1.0")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand All @@ -41,7 +41,7 @@ let package = Package(
.product(name: "VHDLMachines", package: "VHDLMachines"),
.product(name: "VHDLParsing", package: "VHDLParsing"),
.product(name: "VHDLKripkeStructureGenerator", package: "VHDLKripkeStructureGenerator"),
.product(name: "IO", package: "swift_helpers")
.product(name: "SwiftUtils", package: "SwiftUtils")
]
),
.target(
Expand All @@ -63,7 +63,7 @@ let package = Package(
.product(name: "VHDLParsing", package: "VHDLParsing"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "VHDLKripkeStructureGenerator", package: "VHDLKripkeStructureGenerator"),
.product(name: "IO", package: "swift_helpers")
.product(name: "SwiftUtils", package: "SwiftUtils")
]
),
.testTarget(
Expand Down
2 changes: 1 addition & 1 deletion Sources/MachineGenerator/Generate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ struct Generate: ParsableCommand {
@inlinable
func createMachine() throws {
let model = try decoder.decode(MachineModel.self, from: model)
guard let machine = Machine(model: model, path: pathURL) else {
guard let machine = Machine(model: model) else {
throw GenerationError.invalidGeneration(message: "Cannot create valid machine from model.")
}
let data = try encoder.encode(machine)
Expand Down
24 changes: 16 additions & 8 deletions Sources/MachineGenerator/VHDLGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,11 @@

import ArgumentParser
import Foundation
import SwiftUtils
import VHDLKripkeStructureGenerator
import VHDLMachines
import VHDLParsing

#if os(Linux)
import IO
#endif

/// A sub-command that generates VHDL source files from LLFSM definitions.
struct VHDLGenerator: ParsableCommand {

Expand All @@ -87,9 +84,20 @@ struct VHDLGenerator: ParsableCommand {
.appendingPathComponent("machine.json", isDirectory: false)
let data = try Data(contentsOf: path)
let machine = try JSONDecoder().decode(Machine.self, from: data)
guard let representation = MachineRepresentation(machine: machine) else {
let nameRaw = self.options.pathURL.lastPathComponent
guard
nameRaw.hasSuffix(".machine"),
nameRaw != ".machine",
let name = VariableName(rawValue: String(nameRaw.dropLast(8)))
else {
throw GenerationError.invalidFormat(
message: "The machine specified is invalid. " +
"Please make sure you specify a machine with the .machine extension and valid name."
)
}
guard let representation = MachineRepresentation(machine: machine, name: name) else {
throw GenerationError.invalidGeneration(
message: "Failed to generate VHDL for \(machine.name.rawValue)."
message: "Failed to generate VHDL for \(name.rawValue)."
)
}
let machinePath = URL(fileURLWithPath: options.path, isDirectory: true)
Expand All @@ -98,15 +106,15 @@ struct VHDLGenerator: ParsableCommand {
let file = VHDLFile(representation: representation)
let vhdlFolder = buildFolder.appendingPathComponent("vhdl", isDirectory: true)
let vhdlPath = vhdlFolder.appendingPathComponent(
"\(machine.name.rawValue).vhd", isDirectory: false
"\(representation.entity.name.rawValue).vhd", isDirectory: false
)
try FileManager.default.createDirectory(at: vhdlFolder, withIntermediateDirectories: true)
try (file.rawValue + "\n").write(to: vhdlPath, atomically: true, encoding: .utf8)
return
}
guard let files = VHDLKripkeStructureGenerator().generateAll(representation: representation) else {
throw GenerationError.invalidGeneration(
message: "Failed to generate Kripke Structure for \(machine.name.rawValue)."
message: "Failed to generate Kripke Structure for \(name.rawValue)."
)
}
try files.write(to: buildFolder, options: .atomic, originalContentsURL: nil)
Expand Down
12 changes: 2 additions & 10 deletions Sources/VHDLMachineTransformations/Machine+jsInit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ extension Machine {
/// - model: The model representing this machine.
/// - path: The path where the machine is located.
@inlinable
public init?(model: MachineModel, path: URL? = nil) {
public init?(model: MachineModel) {
let actionNames = Set(["OnEntry", "OnExit", "Internal"])
.union(model.states.flatMap { $0.actions.map(\.name) })
let variableNames = actionNames.compactMap(VariableName.init(rawValue:))
Expand All @@ -94,11 +94,6 @@ extension Machine {
guard machineSignalsRaw.count == machineSignals.count else {
return nil
}
let name = path?.deletingPathExtension().lastPathComponent ?? "Machine0"
let machinePath = path ?? URL(fileURLWithPath: "/tmp/Machine0.machine", isDirectory: true)
guard let machineName = VariableName(rawValue: name) else {
return nil
}
let externalsRaw = getStatements(model.externalVariables)
let externalSignals = externalsRaw.compactMap(PortSignal.init(rawValue:))
guard externalsRaw.count == externalSignals.count else {
Expand Down Expand Up @@ -134,20 +129,17 @@ extension Machine {
+ actionVariableNames
let signalNames: [VariableName] = externalSignals.map(\.name) + machineSignals.map(\.name)
+ clocks.map(\.name)
let allNames: [VariableName] = stateNames + signalNames + [machineName]
let allNames: [VariableName] = stateNames + signalNames
// Check for duplicate names.
guard Set(allNames).count == allNames.count else {
return nil
}
self.init(
actions: actionVariableNames,
name: machineName,
path: machinePath,
includes: parsedIncludes,
externalSignals: externalSignals,
clocks: clocks,
drivingClock: 0,
dependentMachines: [:],
machineSignals: machineSignals,
isParameterised: false,
parameterSignals: [],
Expand Down
17 changes: 6 additions & 11 deletions Tests/MachineGeneratorTests/Machine0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,12 @@ import VHDLParsing
/// Create `Machine0`.
extension Machine {

// swiftlint:disable function_body_length
// swiftlint:disable closure_body_length

/// Create Machine0 in the given folder.
init?(machine0LocatedInFolder path: URL) {
let machine0Path = path.appendingPathComponent("Machine0.machine", isDirectory: true)
guard var machine = Machine.initial(path: machine0Path) else {
return nil
}
static let machine0 = {
var machine = Machine.initialSuspensible
machine.actions = [.internal, .onEntry, .onExit]
machine.name = .machine0
machine.externalSignals = [
PortSignal(type: .stdLogic, name: .x, mode: .input),
PortSignal(type: .stdLogic, name: .y, mode: .output)
Expand Down Expand Up @@ -132,11 +128,10 @@ extension Machine {
.all
])!)
]
machine.path = machine0Path
self = machine
}
return machine
}()

// swiftlint:enable function_body_length
// swiftlint:enable closure_body_length

}

Expand Down
19 changes: 2 additions & 17 deletions Tests/MachineGeneratorTests/Machine0Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,27 +74,12 @@ final class Machine0Tests: XCTestCase {

/// Test generation is correct.
func testGeneration() {
let path = URL(fileURLWithPath: pathRaw, isDirectory: true)
guard
let machine = Machine(machine0LocatedInFolder: path),
let result = Machine(
model: .machine0, path: path.appendingPathComponent("Machine0.machine", isDirectory: true)
)
else {
XCTFail("Failed to create machine and model.")
return
}
XCTAssertEqual(result, machine)
XCTAssertEqual(Machine(model: .machine0), Machine.machine0)
}

/// Test the model generation is correct.
func testModelGeneration() {
guard
let machine = Machine(machine0LocatedInFolder: URL(fileURLWithPath: pathRaw, isDirectory: true))
else {
XCTFail("Failed to create machine.")
return
}
let machine = Machine.machine0
let model = MachineModel.machine0
let result = MachineModel(
machine: machine,
Expand Down
2 changes: 1 addition & 1 deletion Tests/MachineGeneratorTests/MachineTester.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ class MachineTester: XCTestCase {
override func setUp() {
let createDir: ()? = try? manager
.createDirectory(at: machine0Path, withIntermediateDirectories: true)
let machine = Machine.machine0
guard
createDir != nil,
let machine = Machine(machine0LocatedInFolder: machinesFolder),
let data = try? encoder.encode(machine),
let modelData = try? encoder.encode(MachineModel.machine0)
else {
Expand Down
90 changes: 72 additions & 18 deletions Tests/MachineGeneratorTests/VHDLGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,12 @@

import Foundation
@testable import MachineGenerator
import SwiftUtils
import VHDLKripkeStructureGenerator
import VHDLMachines
import VHDLParsing
import XCTest

#if os(Linux)
import IO
#endif

/// Test class for ``VHDLGenerator``.
final class VHDLGeneratorTests: MachineTester {

Expand All @@ -73,13 +70,10 @@ final class VHDLGeneratorTests: MachineTester {

/// Test the main method generates the `VHDL` file correctly.
func testRunGeneratesVHDL() throws {
guard let machine = Machine(machine0LocatedInFolder: self.machine0Path) else {
XCTFail("Failed to create machine.")
return
}
let machine = Machine.machine0
VHDLGenerator.main([pathRaw])
let vhdlPath = machine0Path.appendingPathComponent("build/vhdl/Machine0.vhd", isDirectory: false)
guard let representation = MachineRepresentation(machine: machine) else {
guard let representation = MachineRepresentation(machine: machine, name: .machine0) else {
XCTFail("Failed to create VHDL for machine.")
return
}
Expand All @@ -89,10 +83,8 @@ final class VHDLGeneratorTests: MachineTester {

/// Test that the main method throws the correct error for an invalid machine.
func testRunThrowsErrorForInvalidMachine() throws {
guard
var machine = Machine(machine0LocatedInFolder: self.machine0Path),
let onEntry = VariableName(rawValue: "OnEntry")
else {
var machine = Machine.machine0
guard let onEntry = VariableName(rawValue: "OnEntry") else {
XCTFail("Failed to create machine.")
return
}
Expand All @@ -109,15 +101,77 @@ final class VHDLGeneratorTests: MachineTester {
}
}

/// Test the correct error is thrown for paths without the correct extension.
func testPathWithoutExtension() throws {
let invalidPath = self.machinesFolder.appendingPathComponent("invalid", isDirectory: true)
try self.manager.copyItem(at: self.machine0Path, to: invalidPath)
defer { _ = try? self.manager.removeItem(at: invalidPath) }
var command = try VHDLGenerator.parse([invalidPath.path])
XCTAssertThrowsError(try command.run()) {
guard let error = $0 as? GenerationError else {
XCTFail("Thrown incorrect error.")
return
}
XCTAssertEqual(
error,
.invalidFormat(
message: "The machine specified is invalid. " +
"Please make sure you specify a machine with the .machine extension and valid name."
)
)
}
}

/// Test correct errors are thrown for paths with only the machine extension.
func testEmptyMachinePath() throws {
let path = self.machinesFolder.appendingPathComponent(".machine", isDirectory: true)
try self.manager.copyItem(at: self.machine0Path, to: path)
defer { _ = try? self.manager.removeItem(at: path) }
var command = try VHDLGenerator.parse([path.path])
XCTAssertThrowsError(try command.run()) {
print($0.localizedDescription)
fflush(stdout)
guard let error = $0 as? GenerationError else {
XCTFail("Thrown incorrect error.")
return
}
XCTAssertEqual(
error,
.invalidFormat(
message: "The machine specified is invalid. " +
"Please make sure you specify a machine with the .machine extension and valid name."
)
)
}
}

/// Test that VHDL keyword in machine name throw an error.
func testVHDLNameInPath() throws {
let path = self.machinesFolder.appendingPathComponent("signal.machine", isDirectory: true)
try self.manager.copyItem(at: self.machine0Path, to: path)
defer { _ = try? self.manager.removeItem(at: path) }
var command = try VHDLGenerator.parse([path.path])
XCTAssertThrowsError(try command.run()) {
guard let error = $0 as? GenerationError else {
XCTFail("Thrown incorrect error.")
return
}
XCTAssertEqual(
error,
.invalidFormat(
message: "The machine specified is invalid. " +
"Please make sure you specify a machine with the .machine extension and valid name."
)
)
}
}

/// Test the VHDL generator creates the correct Kripke structure.
func testRunGeneratesKripkeStructure() throws {
guard let machine = Machine(machine0LocatedInFolder: self.machine0Path) else {
XCTFail("Failed to create machine.")
return
}
let machine = Machine.machine0
VHDLGenerator.main(["--include-kripke-structure", pathRaw])
guard
let representation = MachineRepresentation(machine: machine),
let representation = MachineRepresentation(machine: machine, name: .machine0),
let files = generator.generateAll(representation: representation)
else {
XCTFail("Failed to create VHDL for machine.")
Expand Down
6 changes: 0 additions & 6 deletions Tests/VHDLMachineTransformationsTests/MachineModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ final class MachineModelTests: XCTestCase {
/// The expected machine.
lazy var machine = Machine(
actions: [VariableName(rawValue: "OnEntry")!, VariableName(rawValue: "OnExit")!],
name: VariableName(rawValue: "Machine0")!,
path: URL(fileURLWithPath: "/tmp/Machine0.machine", isDirectory: true),
includes: [
.library(value: VariableName(rawValue: "IEEE")!),
.include(
Expand All @@ -135,7 +133,6 @@ final class MachineModelTests: XCTestCase {
externalSignals: [PortSignal(type: .stdLogic, name: VariableName(rawValue: "y")!, mode: .output)],
clocks: clocks,
drivingClock: 0,
dependentMachines: [:],
machineSignals: [LocalSignal(type: .stdLogic, name: VariableName(rawValue: "x")!)],
isParameterised: false,
parameterSignals: [],
Expand Down Expand Up @@ -190,8 +187,6 @@ final class MachineModelTests: XCTestCase {
override func setUp() {
machine = Machine(
actions: [VariableName(rawValue: "OnEntry")!, VariableName(rawValue: "OnExit")!],
name: VariableName(rawValue: "Machine0")!,
path: URL(fileURLWithPath: "/tmp/Machine0.machine", isDirectory: true),
includes: [
.library(value: VariableName(rawValue: "IEEE")!),
.include(
Expand All @@ -203,7 +198,6 @@ final class MachineModelTests: XCTestCase {
externalSignals: [PortSignal(type: .stdLogic, name: VariableName(rawValue: "y")!, mode: .output)],
clocks: clocks,
drivingClock: 0,
dependentMachines: [:],
machineSignals: [LocalSignal(type: .stdLogic, name: VariableName(rawValue: "x")!)],
isParameterised: false,
parameterSignals: [],
Expand Down
Loading