Compare commits
5 Commits
022dc94fde
...
8f9fbb86bf
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f9fbb86bf | |||
| 16a798cc39 | |||
| 017d5670c0 | |||
| d60465e669 | |||
| b3ca30541a |
@@ -56,25 +56,4 @@ 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
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
6.2.4
|
main-snapshot
|
||||||
|
|||||||
+31
-1
@@ -27,7 +27,7 @@ struct Cli: ParsableCommand {
|
|||||||
|
|
||||||
static let configuration = CommandConfiguration(
|
static let configuration = CommandConfiguration(
|
||||||
abstract: "P4CE compiler, interpreter and debugger.",
|
abstract: "P4CE compiler, interpreter and debugger.",
|
||||||
subcommands: [Compile.self, CodeGen.self])
|
subcommands: [Preprocess.self, Compile.self, CodeGen.self])
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CliOptions: ParsableArguments {
|
struct CliOptions: ParsableArguments {
|
||||||
@@ -38,6 +38,36 @@ struct CliOptions: ParsableArguments {
|
|||||||
var search: [String] = []
|
var search: [String] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Cli {
|
||||||
|
struct Preprocess: 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 formatter: any Formattable =
|
||||||
|
if parent.plain != 0 {
|
||||||
|
FormatterPlain()
|
||||||
|
} else {
|
||||||
|
FormatterAnsi()
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_source = prep.preprocess(file)
|
||||||
|
guard case .Ok(let source) = maybe_source else {
|
||||||
|
print(ErrorWithLabel("Preprocessor Error", maybe_source.error()!).format(formatter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
print(source.getSource())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
extension Cli {
|
extension Cli {
|
||||||
struct Compile: ParsableCommand {
|
struct Compile: ParsableCommand {
|
||||||
static let configuration = CommandConfiguration(abstract: "Compile P4CE code.")
|
static let configuration = CommandConfiguration(abstract: "Compile P4CE code.")
|
||||||
|
|||||||
+103
-37
@@ -291,6 +291,77 @@ public struct MustOr: CodeItemMacro {
|
|||||||
|
|
||||||
public struct CliTestDeclarationMacro: PeerMacro, Sendable {
|
public struct CliTestDeclarationMacro: PeerMacro, Sendable {
|
||||||
|
|
||||||
|
// NOTE: Taken from swift-testing.
|
||||||
|
|
||||||
|
/// Get an expression initializing an instance of ``SourceLocation`` from an
|
||||||
|
/// arbitrary syntax node.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - node: The syntax node for which an instance of ``SourceLocation`` is
|
||||||
|
/// needed.
|
||||||
|
/// - context: The macro context in which the expression is being parsed.
|
||||||
|
///
|
||||||
|
/// - Returns: An expression value that initializes an instance of
|
||||||
|
/// ``SourceLocation`` for `node`.
|
||||||
|
static func createSourceLocationExpr(
|
||||||
|
of node: some SyntaxProtocol, context: some MacroExpansionContext
|
||||||
|
) -> ExprSyntax {
|
||||||
|
if node.isProtocol((any FreestandingMacroExpansionSyntax).self) {
|
||||||
|
// Freestanding macro expressions can just use __here()
|
||||||
|
// directly and do not need to talk to the macro context to get source
|
||||||
|
// location info.
|
||||||
|
return "Testing.SourceLocation.__here()"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the equivalent source location in both `#fileID` and `#filePath` modes.
|
||||||
|
guard let fileIDSourceLoc: AbstractSourceLocation = context.location(of: node),
|
||||||
|
let filePathSourceLoc: AbstractSourceLocation = context.location(
|
||||||
|
of: node, at: .afterLeadingTrivia, filePathMode: .filePath)
|
||||||
|
else {
|
||||||
|
return "Testing.SourceLocation.__here()"
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
"Testing.SourceLocation(__uncheckedFileID: \(fileIDSourceLoc.file), filePath: \(filePathSourceLoc.file), line: \(fileIDSourceLoc.line), column: \(fileIDSourceLoc.column))"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an expression initializing an instance of `__SourceBounds` from two
|
||||||
|
/// arbitrary syntax nodesvalues.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - lowerBoundNode: The syntax node representing the lower bound. The start
|
||||||
|
/// of this node (after leading trivia) is used.
|
||||||
|
/// - upperBoundNode: The syntax node representing the upper bound. The end of
|
||||||
|
/// this node (before trailing trivia) is used.
|
||||||
|
/// - context: The macro context in which the expression is being parsed.
|
||||||
|
///
|
||||||
|
/// - Returns: An expression value that initializes an instance of
|
||||||
|
/// `__SourceBounds`.
|
||||||
|
///
|
||||||
|
/// The resulting source bounds instance represents (approximately):
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// lowerBoundNode.positionAfterSkippingLeadingTrivia ..< upperBoundNode.endPositionBeforeTrailingTrivia
|
||||||
|
/// ```
|
||||||
|
static func createSourceBoundsExpr(
|
||||||
|
from lowerBoundNode: some SyntaxProtocol, to upperBoundNode: some SyntaxProtocol,
|
||||||
|
in context: some MacroExpansionContext
|
||||||
|
) -> ExprSyntax {
|
||||||
|
let lowerBoundExpr = createSourceLocationExpr(of: lowerBoundNode, context: context)
|
||||||
|
let upperBoundExpr: ExprSyntax =
|
||||||
|
if let upperBoundSourceLoc = context.location(
|
||||||
|
of: upperBoundNode, at: .beforeTrailingTrivia, filePathMode: .fileID)
|
||||||
|
{
|
||||||
|
"(\(upperBoundSourceLoc.line), \(upperBoundSourceLoc.column))"
|
||||||
|
} else {
|
||||||
|
"(.max, .max)"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
"Testing.__SourceBounds(__uncheckedLowerBound: \(lowerBoundExpr), upperBound: \(upperBoundExpr))"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: End of what was taken from swift-testing
|
||||||
|
|
||||||
private static func doc_shrink(_ from: String) -> String {
|
private static func doc_shrink(_ from: String) -> String {
|
||||||
return from.replacing(Regex(#/^.*\/\/\/[\s]+/#), with: "")
|
return from.replacing(Regex(#/^.*\/\/\/[\s]+/#), with: "")
|
||||||
}
|
}
|
||||||
@@ -301,74 +372,69 @@ public struct CliTestDeclarationMacro: PeerMacro, Sendable {
|
|||||||
in context: some MacroExpansionContext
|
in context: some MacroExpansionContext
|
||||||
) throws -> [DeclSyntax] {
|
) throws -> [DeclSyntax] {
|
||||||
|
|
||||||
|
let test_name = declaration.cast(FunctionDeclSyntax.self).name
|
||||||
|
|
||||||
let cli_test_expected_output = node.leadingTrivia.filter({ $0.isComment }).map({
|
let cli_test_expected_output = node.leadingTrivia.filter({ $0.isComment }).map({
|
||||||
doc_shrink("\($0)")
|
doc_shrink("\($0)")
|
||||||
}).joined(separator: "\\n")
|
}).joined(separator: "\\n")
|
||||||
|
|
||||||
let cli_test_driver_thunk_name = context.makeUniqueName(
|
let cli_test_driver_thunk_name = context.makeUniqueName("_thunk_")
|
||||||
declaration.cast(FunctionDeclSyntax.self).name.text + "_thunk_")
|
|
||||||
|
let (expected_decl, expected_label) =
|
||||||
|
if cli_test_expected_output.isEmpty {
|
||||||
|
("let expected = \"\(test_name)\"", "withExpectedPath:")
|
||||||
|
} else {
|
||||||
|
("let expected = \"\(cli_test_expected_output)\"", "withExpected:")
|
||||||
|
}
|
||||||
|
|
||||||
let cli_test_driver_thunk: DeclSyntax = """
|
let cli_test_driver_thunk: DeclSyntax = """
|
||||||
@Sendable private func \(cli_test_driver_thunk_name)() async throws {
|
@Sendable private func \(cli_test_driver_thunk_name)() async throws {
|
||||||
let expected = "\(raw: cli_test_expected_output)"
|
\(raw: expected_decl)
|
||||||
|
|
||||||
_ = unsafe try await Testing.__requiringUnsafe(
|
_ = unsafe try await Testing.__requiringUnsafe(
|
||||||
Testing.__requiringTry(
|
Testing.__requiringTry(
|
||||||
Testing.__requiringAwait(swiftCliTestRunner(\(declaration.cast(FunctionDeclSyntax.self).name), expected))))
|
Testing.__requiringAwait(swiftCliTestRunner(\(test_name), \(raw: expected_label) expected))))
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
let cli_test_driver_generator_name = context.makeUniqueName(
|
let source_bounds = createSourceBoundsExpr(from: node, to: declaration, in: context)
|
||||||
declaration.cast(FunctionDeclSyntax.self).name.text + "_generator_")
|
let cli_test_driver_generator_name = context.makeUniqueName("_generator_")
|
||||||
let cli_test_driver_generator: DeclSyntax = """
|
let cli_test_driver_generator: DeclSyntax = """
|
||||||
@Sendable private func \(cli_test_driver_generator_name)() async -> Testing.Test {
|
@Sendable private func \(cli_test_driver_generator_name)() async -> Testing.Test {
|
||||||
return .__function(
|
return .__function(
|
||||||
named: "xxxxxxxx()",
|
named: "\(test_name)",
|
||||||
in: nil,
|
in: nil as Swift.Never.Type?,
|
||||||
xcTestCompatibleSelector: Testing.__xcTestCompatibleSelector("xxxxxx:"),
|
xcTestCompatibleSelector: Testing.__xcTestCompatibleSelector("\(test_name)"),
|
||||||
traits: [],
|
traits: [],
|
||||||
sourceLocation: Testing.SourceLocation(
|
sourceBounds: \(source_bounds),
|
||||||
fileID: "Tests/CliTests/Cli.swift",
|
|
||||||
filePath: "/Users/hawkinsw/code/p4ce/Tests/p4rseTests/CliTests/Cli.swift", line: 359,
|
|
||||||
column: 2),
|
|
||||||
parameters: [],
|
parameters: [],
|
||||||
testFunction: \(cli_test_driver_thunk_name)
|
testFunction: \(cli_test_driver_thunk_name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
let cli_test_driver_accessor_name = context.makeUniqueName(
|
#if os(macOS)
|
||||||
declaration.cast(FunctionDeclSyntax.self).name.text + "_accessor_")
|
let section = "__DATA_CONST,__swift5_tests"
|
||||||
let cli_test_driver_accessor: DeclSyntax = """
|
#else
|
||||||
private nonisolated let \(cli_test_driver_accessor_name): Accessor = { outValue, type, _, _ in
|
let section = "swift5_tests"
|
||||||
|
#endif
|
||||||
|
|
||||||
Testing.Test.__store(\(cli_test_driver_generator_name), into: outValue, asTypeAt: type)
|
let cli_test_driver_content_record_name = context.makeUniqueName("testContentRecord")
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
let cli_test_driver_content_record_name = context.makeUniqueName(
|
|
||||||
declaration.cast(FunctionDeclSyntax.self).name.text + "_cr_")
|
|
||||||
let cli_test_driver_cr: DeclSyntax = """
|
let cli_test_driver_cr: DeclSyntax = """
|
||||||
private nonisolated let \(cli_test_driver_content_record_name): TestContentRecord = (
|
@section("\(raw: section)")
|
||||||
|
@used
|
||||||
|
private nonisolated let \(cli_test_driver_content_record_name): Testing.__TestContentRecord = (
|
||||||
0x7465_7374, /* indicate a test */
|
0x7465_7374, /* indicate a test */
|
||||||
0,
|
0,
|
||||||
unsafe \(cli_test_driver_accessor_name),
|
{ outValue, type, _, _ in
|
||||||
|
Testing.Test.__store(\(cli_test_driver_generator_name), into: outValue, asTypeAt: type)
|
||||||
|
},
|
||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
let cli_test_driver_content_container_name = context.makeUniqueName(
|
|
||||||
declaration.cast(FunctionDeclSyntax.self).name.text + "__🟡$_container_")
|
|
||||||
let cli_test_driver_container: DeclSyntax = """
|
|
||||||
struct \(cli_test_driver_content_container_name): Testing.__TestContentRecordContainer {
|
|
||||||
nonisolated static var __testContentRecord: TestContentRecord {
|
|
||||||
unsafe \(cli_test_driver_content_record_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
return [
|
return [
|
||||||
cli_test_driver_thunk, cli_test_driver_generator, cli_test_driver_accessor,
|
cli_test_driver_thunk, cli_test_driver_generator, cli_test_driver_cr,
|
||||||
cli_test_driver_cr, cli_test_driver_container,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
transition select (false) {
|
||||||
|
true: reject;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Preprocessor Error: Could not open simple.p for preprocessing
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Success
|
||||||
+16
-3
@@ -16,8 +16,12 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SystemPackage
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
|
private let TestGoldenPath: FilePath.Component = FilePath.Component("TestData")
|
||||||
|
private let TestGoldenExtension: String = ".golden"
|
||||||
|
|
||||||
typealias Accessor =
|
typealias Accessor =
|
||||||
@convention(c) (
|
@convention(c) (
|
||||||
_ outValue: UnsafeMutableRawPointer,
|
_ outValue: UnsafeMutableRawPointer,
|
||||||
@@ -68,7 +72,6 @@ func swiftRun(withArgs args: [String]) throws -> String? {
|
|||||||
let path = swiftPath()!
|
let path = swiftPath()!
|
||||||
let child = Process()
|
let child = Process()
|
||||||
let so = Pipe()
|
let so = Pipe()
|
||||||
let se = Pipe()
|
|
||||||
|
|
||||||
child.standardOutput = so
|
child.standardOutput = so
|
||||||
child.executableURL = path
|
child.executableURL = path
|
||||||
@@ -90,7 +93,7 @@ func swiftRun(withArgs args: [String]) throws -> String? {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func swiftCliTestRunner(_ arg_gen: () -> [String], _ expected: String) async throws {
|
func swiftCliTestRunner(_ arg_gen: () -> [String], withExpected expected: String) async throws {
|
||||||
let args = arg_gen()
|
let args = arg_gen()
|
||||||
|
|
||||||
let run_output = try! swiftRun(withArgs: args)
|
let run_output = try! swiftRun(withArgs: args)
|
||||||
@@ -98,7 +101,17 @@ func swiftCliTestRunner(_ arg_gen: () -> [String], _ expected: String) async thr
|
|||||||
#expect(run_output == expected)
|
#expect(run_output == expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func swiftCliTestRunner(_ arg_gen: () -> [String], withExpectedPath expected_path: String) async throws {
|
||||||
|
let args = arg_gen()
|
||||||
|
|
||||||
|
let run_output = try! swiftRun(withArgs: args)
|
||||||
|
|
||||||
|
let expected_source_absolute = FilePath(FileManager().currentDirectoryPath).appending(TestGoldenPath).appending(expected_path + TestGoldenExtension)
|
||||||
|
|
||||||
|
let expected_output = try! String(contentsOfFile: expected_source_absolute.string)
|
||||||
|
|
||||||
|
#expect(run_output == expected_output)
|
||||||
|
}
|
||||||
|
|
||||||
@attached(peer) public macro CliTest() =
|
@attached(peer) public macro CliTest() =
|
||||||
#externalMacro(module: "Macros", type: "CliTestDeclarationMacro")
|
#externalMacro(module: "Macros", type: "CliTestDeclarationMacro")
|
||||||
|
|||||||
@@ -23,4 +23,19 @@ import Testing
|
|||||||
@CliTest()
|
@CliTest()
|
||||||
func simple_cli_test() -> [String] {
|
func simple_cli_test() -> [String] {
|
||||||
return ["p4ce", "--plain", "compile", "simple.p4", "-I", "TestData/Sources/"]
|
return ["p4ce", "--plain", "compile", "simple.p4", "-I", "TestData/Sources/"]
|
||||||
|
}
|
||||||
|
|
||||||
|
@CliTest()
|
||||||
|
func simple_cli_testa() -> [String] {
|
||||||
|
return ["p4ce", "--plain", "compile", "simple.p4", "-I", "TestData/Sources/"]
|
||||||
|
}
|
||||||
|
|
||||||
|
@CliTest()
|
||||||
|
func simple_cli_preprocessor_test() -> [String] {
|
||||||
|
return ["p4ce", "--plain", "preprocess", "simple.p4", "-I", "TestData/Sources/"]
|
||||||
|
}
|
||||||
|
|
||||||
|
@CliTest()
|
||||||
|
func simple_cli_preprocessor_test_file_not_found() -> [String] {
|
||||||
|
return ["p4ce", "--plain", "preprocess", "simple.p", "-I", "TestData/Sources/"]
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/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