12fa43d9f9
Implement a generic visitor for components of a P4 program and use it to start P4 code generation (according to the behavioral model). Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
123 lines
3.7 KiB
Swift
123 lines
3.7 KiB
Swift
// p4rse, Copyright 2026, Will Hawkins
|
|
//
|
|
// This file is part of p4rse.
|
|
//
|
|
// This file is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import ArgumentParser
|
|
import Common
|
|
import P4Compiler
|
|
import P4Runtime
|
|
import SystemPackage
|
|
|
|
@main
|
|
struct Cli: ParsableCommand {
|
|
@Flag(help: "Disable ANSI-stylized output.") var plain: Int
|
|
|
|
static let configuration = CommandConfiguration(
|
|
abstract: "P4CE compiler, interpreter and debugger.",
|
|
subcommands: [Compile.self, CodeGen.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 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
|
|
}
|
|
|
|
let maybe_program = Program.Compile(source.getSource())
|
|
guard case .Ok(_) = maybe_program else {
|
|
print(ErrorWithLabel("Compiler Error", maybe_source.error()!).format(formatter))
|
|
return
|
|
}
|
|
|
|
print(
|
|
formatter.formatWithStyle(
|
|
"Success", Style(StyleColor.Green, [StyleFormat.Underline])))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Cli {
|
|
struct CodeGen: ParsableCommand {
|
|
static let configuration = CommandConfiguration(abstract: "Generate 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
|
|
}
|
|
|
|
let maybe_program = Program.Compile(source.getSource())
|
|
guard case .Ok(let program) = maybe_program else {
|
|
print(ErrorWithLabel("Compiler Error", maybe_source.error()!).format(formatter))
|
|
return
|
|
}
|
|
|
|
let maybe_codegen = P4Runtime.CodeGenerator().codeGen(program)
|
|
guard case .Ok(let codegen) = maybe_codegen else {
|
|
let formatter = FormatterAnsi()
|
|
print(ErrorWithLabel("Code Generation Error", maybe_codegen.error()!).format(formatter))
|
|
return
|
|
}
|
|
|
|
print("\(codegen.getGeneratedCode())")
|
|
}
|
|
}
|
|
}
|