Compare commits
6 Commits
d28ccd79e4
...
49eef16c19
| Author | SHA1 | Date | |
|---|---|---|---|
| 49eef16c19 | |||
| fccaf1aa92 | |||
| 73b4f54bbe | |||
| 0e2b13be93 | |||
| f0f7a660a6 | |||
| a0c6b7730c |
@@ -57,4 +57,25 @@ jobs:
|
|||||||
- run: tree-sitter generate
|
- run: tree-sitter generate
|
||||||
working-directory: ./tree-sitter-p4
|
working-directory: ./tree-sitter-p4
|
||||||
- run: ./ci/format.sh
|
- run: ./ci/format.sh
|
||||||
|
cli-tests:
|
||||||
|
name: Cli Tests
|
||||||
|
runs-on: ubuntu-build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
- uses: tree-sitter/setup-action@v2
|
||||||
|
with:
|
||||||
|
tree-sitter-ref: "master"
|
||||||
|
- uses: swift-actions/setup-swift@v3
|
||||||
|
with:
|
||||||
|
swift-version: "main-snapshot"
|
||||||
|
skip-verify-signature: true
|
||||||
|
# Because our tree-sitter code is in a subdirectory,
|
||||||
|
# and working-directory does not apply to uses, we
|
||||||
|
# are forced to specify calls to the CI ourselves.
|
||||||
|
# See https://github.com/orgs/community/discussions/25742
|
||||||
|
- run: tree-sitter generate
|
||||||
|
working-directory: ./tree-sitter-p4
|
||||||
|
- run: swift build
|
||||||
|
- run: ./ci/tests/test.sh
|
||||||
|
|
||||||
|
|||||||
+55
-5
@@ -17,13 +17,63 @@
|
|||||||
|
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
import Common
|
import Common
|
||||||
|
import P4Compiler
|
||||||
|
import SystemPackage
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct Cli: ParsableCommand {
|
struct Cli: ParsableCommand {
|
||||||
public func run() throws {
|
@Flag(help: "Disable ANSI-stylized output.") var plain: Int
|
||||||
let formatter = FormatterPlain()
|
|
||||||
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "Testing")
|
static let configuration = CommandConfiguration(
|
||||||
let e1 = ErrorWithLocation(sourceLocation: SourceLocation(10, 5), withError: "Oh no")
|
abstract: "P4CE compiler, interpreter and debugger.",
|
||||||
print(e.append(error: e1).format(formatter))
|
subcommands: [Compile.self])
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CliOptions: ParsableArguments {
|
||||||
|
@ArgumentParser.Argument(help: "File to compile.") // Have to be explicit because Common has an Argument, too!
|
||||||
|
var path: String
|
||||||
|
|
||||||
|
@Option(name: [.customShort("I")], help: "Search paths.")
|
||||||
|
var search: [String] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Cli {
|
||||||
|
struct Compile: ParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(abstract: "Compile P4CE code.")
|
||||||
|
|
||||||
|
@ParentCommand var parent: Cli
|
||||||
|
|
||||||
|
@OptionGroup var options: CliOptions
|
||||||
|
|
||||||
|
mutating func run() {
|
||||||
|
let sm = SourceManager(options.search.map { FilePath($0) })
|
||||||
|
let prep = SourceCodePreprocessor(sm)
|
||||||
|
let file = FilePath(options.path)
|
||||||
|
|
||||||
|
let maybe_source = prep.preprocess(file)
|
||||||
|
guard case .Ok(let source) = maybe_source else {
|
||||||
|
let formatter = FormatterAnsi()
|
||||||
|
print(ErrorWithLabel("Preprocessor Error", maybe_source.error()!).format(formatter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_program = Program.Compile(source.getSource())
|
||||||
|
guard case .Ok(_) = maybe_program else {
|
||||||
|
let formatter = FormatterAnsi()
|
||||||
|
print(ErrorWithLabel("Compiler Error", maybe_source.error()!).format(formatter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let success_formatter: any Formattable =
|
||||||
|
if parent.plain != 0 {
|
||||||
|
FormatterPlain()
|
||||||
|
} else {
|
||||||
|
FormatterAnsi()
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
success_formatter.formatWithStyle(
|
||||||
|
"Success", Style(StyleColor.Green, [StyleFormat.Underline])))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public struct ErrorWithLabel: Errorable {
|
|||||||
public func format(_ formatter: any Formattable) -> String {
|
public func format(_ formatter: any Formattable) -> String {
|
||||||
let green = Style(StyleColor.Green)
|
let green = Style(StyleColor.Green)
|
||||||
let formatted_label = formatter.formatWithStyle(self.label, green)
|
let formatted_label = formatter.formatWithStyle(self.label, green)
|
||||||
return formatted_label + self.error.format(formatter)
|
return formatted_label + ": " + self.error.format(formatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(_ label: String, _ error: any Errorable) {
|
public init(_ label: String, _ error: any Errorable) {
|
||||||
|
|||||||
@@ -75,6 +75,20 @@ public struct FormatterPlain: Formattable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct FormatterDelimited: Formattable {
|
||||||
|
let start: String
|
||||||
|
let end: String
|
||||||
|
|
||||||
|
public init(_ start: String, _ end: String) {
|
||||||
|
self.start = start
|
||||||
|
self.end = end
|
||||||
|
}
|
||||||
|
|
||||||
|
public func formatWithStyle(_ value: String, _ style: Style) -> String {
|
||||||
|
return self.start + value + self.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct FormatterAnsi: Formattable {
|
public struct FormatterAnsi: Formattable {
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|||||||
@@ -40,14 +40,49 @@ public struct SourceLocation: Equatable, CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represent a set of directories containing P4 code that can be accessed with relative paths.
|
/// Represent search paths for P4 code that can be accessed with relative paths.
|
||||||
public struct SourceManager {
|
public struct SourceManager {
|
||||||
let paths: [FilePath]
|
let paths: [FilePath]
|
||||||
public init(_ paths: [FilePath]) {
|
|
||||||
self.paths = paths
|
/// Create a `SourceManager`
|
||||||
|
///
|
||||||
|
/// Any relative `FilePath`s in `paths` will be absolutized
|
||||||
|
/// if a `FileManager` is given.
|
||||||
|
///
|
||||||
|
/// parameters:
|
||||||
|
/// - paths: The include paths searched for files with relative paths.
|
||||||
|
/// - fm: An optional instance of a `FileManager` that will be used to
|
||||||
|
/// convert relative paths in `paths` to absolute paths.
|
||||||
|
public init(_ paths: [FilePath], _ fm: FileManager? = .none) {
|
||||||
|
|
||||||
|
// If the user gives a file manager, we will convert relative paths
|
||||||
|
// to absolute paths. Otherwise, we do not.
|
||||||
|
guard let fm else {
|
||||||
|
self.paths = paths
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is a file manager, so we should try to absolutize any
|
||||||
|
// relative paths
|
||||||
|
self.paths = paths.map {
|
||||||
|
if !$0.isAbsolute {
|
||||||
|
return FilePath(fm.currentDirectoryPath + "/" + $0.string).lexicallyNormalized()
|
||||||
|
}
|
||||||
|
return $0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `FilePath` of `file` in search paths.
|
||||||
|
///
|
||||||
|
/// Only if `file` is relative will the search paths be searched.
|
||||||
|
///
|
||||||
|
/// parameters:
|
||||||
|
/// - file: A file to look for in the search paths.
|
||||||
public func firstExisting(_ file: FilePath) -> FilePath? {
|
public func firstExisting(_ file: FilePath) -> FilePath? {
|
||||||
|
if file.isAbsolute {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
let fm = FileManager()
|
let fm = FileManager()
|
||||||
for path in self.paths {
|
for path in self.paths {
|
||||||
let combined = path.pushing(file)
|
let combined = path.pushing(file)
|
||||||
@@ -59,7 +94,10 @@ public struct SourceManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represent preprocessed P4 code and retain information about source filenames.
|
/// Represent preprocessed P4 code
|
||||||
|
///
|
||||||
|
/// The preprocessed code has metadata to recover the paths of any
|
||||||
|
/// code generated by a preprocessor directive.
|
||||||
public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
||||||
let location: SourceLocation
|
let location: SourceLocation
|
||||||
let path: FilePath
|
let path: FilePath
|
||||||
@@ -91,6 +129,22 @@ public struct FileSourceLocation: Equatable, CustomStringConvertible {
|
|||||||
return "\(location)"
|
return "\(location)"
|
||||||
}).joined(separator: ",") + ")"
|
}).joined(separator: ",") + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func pathForLocation(_ location: Int) -> FilePath? {
|
||||||
|
|
||||||
|
let queried_location = SourceLocation(location, 1)
|
||||||
|
if !self.location.contains(queried_location) {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
|
for nested in self.nested {
|
||||||
|
if nested.location.contains(queried_location) {
|
||||||
|
return nested.pathForLocation(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.getPath()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represent preprocessed P4 code.
|
/// Represent preprocessed P4 code.
|
||||||
@@ -105,17 +159,64 @@ public struct SourceCode {
|
|||||||
self.locations = locations
|
self.locations = locations
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getSource() -> String {
|
|
||||||
return self.code
|
|
||||||
}
|
|
||||||
|
|
||||||
public func getManager() -> SourceManager {
|
public func getManager() -> SourceManager {
|
||||||
return self.manager
|
return self.manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func do_annotate(
|
||||||
|
_ contents: String, _ manager: SourceManager, _ locations: FileSourceLocation,
|
||||||
|
_ formatter: Formattable, _ style: Style
|
||||||
|
) -> String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
// Keep track of the start of any gap between nested locations.
|
||||||
|
var gap_start = contents.startIndex
|
||||||
|
|
||||||
|
// contents are devoid of any preceding source code, but the locations do not know that. So,
|
||||||
|
// when we use a range from locations we must adjust appropriately.
|
||||||
|
let offset = locations.location.range.lowerBound
|
||||||
|
|
||||||
|
for nested in locations.getNestedLocations() {
|
||||||
|
let nested_start = contents.index(
|
||||||
|
contents.startIndex, offsetBy: nested.location.range.lowerBound - offset)
|
||||||
|
let nested_end = contents.index(
|
||||||
|
contents.startIndex, offsetBy: nested.location.range.upperBound - offset)
|
||||||
|
|
||||||
|
// Add in any gap.
|
||||||
|
result += contents[gap_start..<nested_start]
|
||||||
|
|
||||||
|
// Handle this range (recursively)
|
||||||
|
result += do_annotate(
|
||||||
|
"\(contents[nested_start ..< nested_end])", manager, nested, formatter, style)
|
||||||
|
|
||||||
|
// Adjust where the next gap will start.
|
||||||
|
gap_start = nested_end
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there anything left?
|
||||||
|
let remainder = contents[gap_start...]
|
||||||
|
|
||||||
|
return formatter.formatWithStyle(result + remainder, style)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getSource(
|
||||||
|
annotated: Bool = false, formatter: Formattable = FormatterDelimited("<", ">"),
|
||||||
|
initialStyle: Style = Style(StyleColor.Red)
|
||||||
|
) -> String {
|
||||||
|
if annotated {
|
||||||
|
return SourceCode.do_annotate(
|
||||||
|
self.code, self.manager, self.locations, formatter, initialStyle)
|
||||||
|
}
|
||||||
|
return self.code
|
||||||
|
}
|
||||||
|
|
||||||
public func getLocations() -> FileSourceLocation {
|
public func getLocations() -> FileSourceLocation {
|
||||||
return self.locations
|
return self.locations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func pathForLocation(_ location: Int) -> FilePath? {
|
||||||
|
return self.locations.pathForLocation(location)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func do_preprocess(
|
func do_preprocess(
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Success
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
state start {
|
||||||
|
Testing ts;
|
||||||
|
ts.yesno = true;
|
||||||
|
ts.count = 5;
|
||||||
|
transition select (ts.count == 5) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
parser main_parser() {
|
||||||
|
#include <annotate-parser-state.p4>
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bool yesno;
|
||||||
|
int count;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
struct Testing {
|
||||||
|
#include <annotate-struct-body.p4>
|
||||||
|
};
|
||||||
|
#include <annotate-parser.p4>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
state start {
|
||||||
|
Testing ts;
|
||||||
|
ts.yesno = true;
|
||||||
|
ts.count = 5;
|
||||||
|
transition select (ts.count == 5) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
parser main_parser() {
|
||||||
|
#include <file-loc-parser-state.p4>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
struct Testing {
|
||||||
|
bool yesno;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
#include <file-loc-parser.p4>
|
||||||
@@ -32,7 +32,8 @@ import TreeSitterP4
|
|||||||
let sm = SourceManager(["./TestData/Sources/"])
|
let sm = SourceManager(["./TestData/Sources/"])
|
||||||
let prep = SourceCodePreprocessor(sm)
|
let prep = SourceCodePreprocessor(sm)
|
||||||
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple.p4")
|
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple.p4")
|
||||||
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string).lexicallyNormalized()
|
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string)
|
||||||
|
.lexicallyNormalized()
|
||||||
|
|
||||||
let source = try! (#UseOkResult(prep.preprocess(file)))
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
||||||
@@ -44,7 +45,8 @@ import TreeSitterP4
|
|||||||
let sm = SourceManager(["./TestData/Sources/"])
|
let sm = SourceManager(["./TestData/Sources/"])
|
||||||
let prep = SourceCodePreprocessor(sm)
|
let prep = SourceCodePreprocessor(sm)
|
||||||
let file = FilePath.init(stringLiteral: "simple.p4")
|
let file = FilePath.init(stringLiteral: "simple.p4")
|
||||||
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/TestData/Sources/simple.p4")
|
let expected_file = FilePath.init(
|
||||||
|
FileManager().currentDirectoryPath + "/TestData/Sources/simple.p4")
|
||||||
|
|
||||||
let source = try! (#UseOkResult(prep.preprocess(file)))
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
let program = try! #UseOkResult(Program.Compile(source.getSource()))
|
||||||
@@ -56,7 +58,8 @@ import TreeSitterP4
|
|||||||
let sm = SourceManager(["./TestData/Sources/"])
|
let sm = SourceManager(["./TestData/Sources/"])
|
||||||
let prep = SourceCodePreprocessor(sm)
|
let prep = SourceCodePreprocessor(sm)
|
||||||
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-split.p4")
|
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-split.p4")
|
||||||
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string).lexicallyNormalized()
|
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string)
|
||||||
|
.lexicallyNormalized()
|
||||||
|
|
||||||
#expect(#RequireOkResult(prep.preprocess(file)))
|
#expect(#RequireOkResult(prep.preprocess(file)))
|
||||||
|
|
||||||
@@ -70,7 +73,8 @@ import TreeSitterP4
|
|||||||
let sm = SourceManager(["./TestData/Sources/"])
|
let sm = SourceManager(["./TestData/Sources/"])
|
||||||
let prep = SourceCodePreprocessor(sm)
|
let prep = SourceCodePreprocessor(sm)
|
||||||
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-split-oneline.p4")
|
let file = FilePath.init(stringLiteral: "./TestData/Sources/simple-split-oneline.p4")
|
||||||
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string).lexicallyNormalized()
|
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string)
|
||||||
|
.lexicallyNormalized()
|
||||||
|
|
||||||
#expect(#RequireOkResult(prep.preprocess(file)))
|
#expect(#RequireOkResult(prep.preprocess(file)))
|
||||||
|
|
||||||
@@ -156,6 +160,56 @@ import TreeSitterP4
|
|||||||
== SourceLocation(48..<166))
|
== SourceLocation(48..<166))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func test_preprocessor_nested_includes_annotated_source() async throws {
|
||||||
|
let sm = SourceManager(["./TestData/Sources/"])
|
||||||
|
let prep = SourceCodePreprocessor(sm)
|
||||||
|
let file = FilePath.init(stringLiteral: "./TestData/Sources/annotate.p4")
|
||||||
|
|
||||||
|
let expected = """
|
||||||
|
<struct Testing {
|
||||||
|
< bool yesno;
|
||||||
|
int count;>
|
||||||
|
};
|
||||||
|
<parser main_parser() {
|
||||||
|
< state start {
|
||||||
|
Testing ts;
|
||||||
|
ts.yesno = true;
|
||||||
|
ts.count = 5;
|
||||||
|
transition select (ts.count == 5) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}>>
|
||||||
|
"""
|
||||||
|
|
||||||
|
#expect(#RequireOkResult(prep.preprocess(file)))
|
||||||
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
|
#expect(source.getSource(annotated: true) == expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_preprocessor_nested_includes_get_file_location() async throws {
|
||||||
|
let sm = SourceManager(["./TestData/Sources/"], FileManager()) // Add a FileManager to get absolute paths.
|
||||||
|
let prep = SourceCodePreprocessor(sm)
|
||||||
|
let file = FilePath.init(stringLiteral: "./TestData/Sources/file-loc.p4")
|
||||||
|
|
||||||
|
let source = try! (#UseOkResult(prep.preprocess(file)))
|
||||||
|
|
||||||
|
let expected_file = FilePath.init(FileManager().currentDirectoryPath + "/" + file.string)
|
||||||
|
.lexicallyNormalized()
|
||||||
|
let expected_nested_file = sm.firstExisting("file-loc-parser.p4")!.lexicallyNormalized()
|
||||||
|
let expected_nested_nested_file = sm.firstExisting("file-loc-parser-state.p4")!
|
||||||
|
.lexicallyNormalized()
|
||||||
|
|
||||||
|
let found_file = try! #require(source.pathForLocation(0))
|
||||||
|
let found_nested_file = try! #require(source.pathForLocation(55))
|
||||||
|
let found_nested_nested_file = try! #require(source.pathForLocation(78))
|
||||||
|
|
||||||
|
#expect(found_file == expected_file)
|
||||||
|
#expect(found_nested_file == expected_nested_file)
|
||||||
|
#expect(found_nested_nested_file == expected_nested_nested_file)
|
||||||
|
}
|
||||||
|
|
||||||
@Test func test_source_location_contains() async throws {
|
@Test func test_source_location_contains() async throws {
|
||||||
let outer = SourceLocation(0..<500)
|
let outer = SourceLocation(0..<500)
|
||||||
@@ -179,8 +233,8 @@ import TreeSitterP4
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test func test_source_location_contains2_a() async throws {
|
@Test func test_source_location_contains2_a() async throws {
|
||||||
let outer = SourceLocation(0,500)
|
let outer = SourceLocation(0, 500)
|
||||||
let not_inner = SourceLocation(0,501)
|
let not_inner = SourceLocation(0, 501)
|
||||||
|
|
||||||
#expect(!outer.contains(not_inner))
|
#expect(!outer.contains(not_inner))
|
||||||
}
|
}
|
||||||
@@ -193,8 +247,8 @@ import TreeSitterP4
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test func test_source_location_contains3_a() async throws {
|
@Test func test_source_location_contains3_a() async throws {
|
||||||
let outer = SourceLocation(200,300)
|
let outer = SourceLocation(200, 300)
|
||||||
let inner = SourceLocation(200,299)
|
let inner = SourceLocation(200, 299)
|
||||||
|
|
||||||
#expect(outer.contains(inner))
|
#expect(outer.contains(inner))
|
||||||
}
|
}
|
||||||
@@ -208,7 +262,7 @@ import TreeSitterP4
|
|||||||
|
|
||||||
@Test func test_source_location_contains5() async throws {
|
@Test func test_source_location_contains5() async throws {
|
||||||
let outer = SourceLocation(200..<300)
|
let outer = SourceLocation(200..<300)
|
||||||
let not_inner = SourceLocation(200,101)
|
let not_inner = SourceLocation(200, 101)
|
||||||
|
|
||||||
#expect(!outer.contains(not_inner))
|
#expect(!outer.contains(not_inner))
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
actual_output=`swift run p4ce --plain compile simple.p4 -I TestData/Sources/`
|
||||||
|
expected_output=`cat TestData/Cli/simple.golden`
|
||||||
|
if [ "${expected_output}" != "${actual_output}" ]; then
|
||||||
|
echo "Expected: ${expected_output}"
|
||||||
|
echo "Actual: ${actual_output}"
|
||||||
|
echo "Cli Tests: Error"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Cli Tests: Success"
|
||||||
|
exit 0
|
||||||
|
|
||||||
Reference in New Issue
Block a user