Support Calling Parsers With Parameters
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
@@ -96,6 +96,12 @@ public enum Result<OKT>: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public func ok() -> Bool {
|
||||
switch self {
|
||||
case .Ok(_): true
|
||||
case .Error(_): false
|
||||
}
|
||||
}
|
||||
public func error() -> Error? {
|
||||
if case Result.Error(let e) = self {
|
||||
return e
|
||||
|
||||
@@ -171,7 +171,7 @@ extension P4Lang.Parser: CompilableDeclaration {
|
||||
var parser_name: Common.Identifier? = .none
|
||||
|
||||
// Assume that the parameter list is empty!
|
||||
var parameter_list: ParameterList = ParameterList([])
|
||||
var parameter_list = ParameterList()
|
||||
|
||||
do {
|
||||
// Parse the parser type (type_node)
|
||||
@@ -207,31 +207,28 @@ extension P4Lang.Parser: CompilableDeclaration {
|
||||
return .Error(e)
|
||||
}
|
||||
|
||||
// Now, see if there are any parameters
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if currentChildIdxSafe < type_node!.childCount {
|
||||
|
||||
// There is something that _should_ be parameters!
|
||||
|
||||
// skip the '('
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if type_node!.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: currentChild!, withError: "Missing ( before parameter list"))
|
||||
}
|
||||
|
||||
currentChild = type_node?.child(at: currentChildIdx)
|
||||
|
||||
switch ParameterList.Compile(node: currentChild!, withContext: current_context) {
|
||||
case .Ok(let (parsed_parameter_list, updated_context)):
|
||||
parameter_list = parsed_parameter_list
|
||||
current_context = updated_context
|
||||
case .Error(let e):
|
||||
return .Error(e)
|
||||
}
|
||||
if type_node!.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: type_node!, withError: "Missing parser parameters"))
|
||||
}
|
||||
|
||||
currentChild = type_node?.child(at: currentChildIdx)
|
||||
switch ParameterList.Compile(node: currentChild!, withContext: current_context) {
|
||||
case .Ok(let (parsed_parameter_list, updated_context)):
|
||||
parameter_list = parsed_parameter_list
|
||||
current_context = updated_context
|
||||
case .Error(let e):
|
||||
return .Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Now, let's put the parameters into scope.
|
||||
for parameter in parameter_list.parameters {
|
||||
current_context = current_context.update(
|
||||
newInstances: current_context.instances.declare(
|
||||
identifier: parameter.name, withValue: parameter.type))
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
@@ -281,73 +278,101 @@ extension P4Lang.Parser: CompilableDeclaration {
|
||||
}
|
||||
}
|
||||
|
||||
func parameter_list_compiler(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no parameters!
|
||||
return Result.Ok((ParameterList([]), context))
|
||||
}
|
||||
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: node, type: "parameter_list", nice_type_name: "Parameter List")
|
||||
|
||||
var parameters: ParameterList = ParameterList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "parameter_list" {
|
||||
switch parameter_list_compiler(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
parameters = ps
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return Result.Ok((parameters, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return Result.Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one parameter left!
|
||||
switch Parameter.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (vds, updated_context)):
|
||||
return Result.Ok((parameters.addParameter(vds), updated_context))
|
||||
case .Error(let e): return Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
extension ParameterList: Compilable {
|
||||
public typealias T = ParameterList
|
||||
public static func Compile(
|
||||
node: SwiftTreeSitter.Node, withContext context: CompilerContext
|
||||
) -> Common.Result<(ParameterList, CompilerContext)> {
|
||||
|
||||
if node.text == ")" {
|
||||
// There are no parameters!
|
||||
return .Ok((ParameterList([]), context))
|
||||
}
|
||||
|
||||
let parameter_node = node
|
||||
#RequireNodeType<Node, (ParameterList, CompilerContext)>(
|
||||
node: node, type: "parameter_list", nice_type_name: "Parameter List")
|
||||
node: parameter_node, type: "parameters", nice_type_name: "Parameters")
|
||||
|
||||
var currentChildIdx = 0
|
||||
var currentChildIdxSafe = 1
|
||||
var currentChild: Node? = .none
|
||||
var parameters: ParameterList = ParameterList([])
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
// Let's eat the '(' before we start ...
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing '(' in parameter list component"))
|
||||
}
|
||||
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
if currentChild?.nodeType == "parameter_list" {
|
||||
switch ParameterList.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (ps, _)):
|
||||
parameters = ps
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
|
||||
print("Back here!")
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
// We may have moved nodes, check/reset currentChild.
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
if parameter_node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
ErrorOnNode(node: parameter_node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
let currentChild = parameter_node.child(at: currentChildIdx)
|
||||
|
||||
// If this is a ')', we are done.
|
||||
if currentChild?.text == ")" {
|
||||
return .Ok((parameters, context))
|
||||
}
|
||||
|
||||
// If this is a comma, we skip it!
|
||||
if currentChild?.text == "," {
|
||||
currentChildIdx += 1
|
||||
currentChildIdxSafe += 1
|
||||
}
|
||||
|
||||
if node.childCount < currentChildIdxSafe {
|
||||
return .Error(
|
||||
ErrorOnNode(node: node, withError: "Missing parameter list component"))
|
||||
}
|
||||
currentChild = node.child(at: currentChildIdx)
|
||||
|
||||
// Otherwise, there should be one parameter left!
|
||||
switch Parameter.Compile(node: currentChild!, withContext: context) {
|
||||
case .Ok(let (vds, updated_context)):
|
||||
return .Ok((parameters.addParameter(vds), updated_context))
|
||||
case .Error(let e): return .Error(e)
|
||||
}
|
||||
return parameter_list_compiler(node: currentChild!, withContext: context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ public struct Parameter: CustomStringConvertible {
|
||||
public struct ParameterList: CustomStringConvertible {
|
||||
public var parameters: [Parameter]
|
||||
|
||||
public init() {
|
||||
self.parameters = Array()
|
||||
}
|
||||
|
||||
public init(_ parameters: [Parameter]) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
@@ -200,3 +200,34 @@ public struct FieldAccessExpression {
|
||||
self.field = field
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArgumentList {
|
||||
public let arguments: [(Int, EvaluatableExpression)]
|
||||
public init(_ arguments: [EvaluatableExpression]) {
|
||||
self.arguments = zip(1..., arguments).map { (idx, argument) in
|
||||
(idx, argument)
|
||||
}
|
||||
}
|
||||
|
||||
public func compatible(_ parameters: ParameterList) -> Result<()> {
|
||||
if self.arguments.count != parameters.parameters.count {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"\(self.arguments.count) arguments found but \(parameters.parameters.count) required"))
|
||||
}
|
||||
|
||||
for (arg, param) in zip(self.arguments, parameters.parameters) {
|
||||
let arg_index = arg.0
|
||||
let arg_type = arg.1.type()
|
||||
if !arg_type.eq(rhs: param.type) {
|
||||
return .Error(
|
||||
Error(
|
||||
withMessage:
|
||||
"Argument \(arg_index)'s type (\(arg_type)) is incompatible with the parameter type (\(param.type))"
|
||||
))
|
||||
}
|
||||
}
|
||||
return .Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,10 +319,11 @@ public struct Parser: P4Type, P4Value {
|
||||
public var states: ParserStates
|
||||
|
||||
public var name: Identifier
|
||||
public var parameters: ParameterList?
|
||||
public var parameters: ParameterList
|
||||
|
||||
public init(withName name: Identifier) {
|
||||
self.states = ParserStates()
|
||||
self.parameters = ParameterList()
|
||||
self.name = name
|
||||
}
|
||||
|
||||
@@ -349,7 +350,6 @@ public struct Parser: P4Type, P4Value {
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let parameters = self.parameters?.description ?? "N/A"
|
||||
return "Parser \(self.name) with parameters: \(parameters) and states: \(self.states)"
|
||||
}
|
||||
|
||||
|
||||
@@ -117,8 +117,11 @@ extension ParserStateSelectTransition: EvaluatableParserState {
|
||||
}
|
||||
}
|
||||
|
||||
extension Parser: ParserExecution {
|
||||
public func execute(execution: ProgramExecution) -> (InstantiatedParserState, ProgramExecution) {
|
||||
extension Parser: CallableExecution {
|
||||
public typealias T = InstantiatedParserState
|
||||
public func call(
|
||||
execution: Common.ProgramExecution, arguments: P4Lang.ArgumentList
|
||||
) -> (P4Lang.InstantiatedParserState, Common.ProgramExecution) {
|
||||
var execution = execution.enter_scope()
|
||||
|
||||
execution = execution.declare(
|
||||
@@ -146,10 +149,34 @@ extension Parser: ParserExecution {
|
||||
)
|
||||
}
|
||||
|
||||
// Now that we are assured that there is a start state,
|
||||
// let's set the arguments.
|
||||
|
||||
if case .Error(let e) = arguments.compatible(self.parameters) {
|
||||
return (
|
||||
reject, execution.setError(error: Error(withMessage: "Cannot call parser: \(e)"))
|
||||
)
|
||||
}
|
||||
|
||||
for (parameter, argument) in zip(self.parameters.parameters, arguments.arguments) {
|
||||
let arg_idx = argument.0
|
||||
let arg_value = argument.1
|
||||
let maybe_argument_value = arg_value.evaluate(execution: execution)
|
||||
guard case .Ok(let argument_value) = maybe_argument_value else {
|
||||
return (
|
||||
reject,
|
||||
execution.setError(
|
||||
error: Error(withMessage: "Cannot evaluate argument \(arg_idx): \(argument)"))
|
||||
)
|
||||
}
|
||||
execution = execution.declare(identifier: parameter.name, withValue: argument_value)
|
||||
}
|
||||
|
||||
// Evaluate until the state is either accept or reject.
|
||||
while !current_state.done() && !execution.hasError() {
|
||||
(current_state, execution) = current_state.execute(program: execution)
|
||||
}
|
||||
return (AsInstantiatedParserState(current_state.state()), execution)
|
||||
|
||||
return (AsInstantiatedParserState(current_state.state()), execution.exit_scope())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public protocol EvaluatableParserState: P4Value {
|
||||
func state() -> ParserState
|
||||
}
|
||||
|
||||
public protocol ParserExecution {
|
||||
func execute(execution: ProgramExecution) -> (InstantiatedParserState, ProgramExecution)
|
||||
public protocol CallableExecution<T> {
|
||||
associatedtype T
|
||||
func call(execution: ProgramExecution, arguments: ArgumentList) -> (T, ProgramExecution)
|
||||
}
|
||||
|
||||
@@ -49,8 +49,14 @@ public struct ParserRuntime: CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the P4 parser on a given packet
|
||||
/// Run a P4 parser with no arguments
|
||||
public func run() -> Result<(ParserState, ProgramExecution)> {
|
||||
return self.run(withArguments: ArgumentList([]))
|
||||
}
|
||||
|
||||
/// Run the P4 parser on a given packet
|
||||
public func run(withArguments arguments: ArgumentList) -> Result<(ParserState, ProgramExecution)>
|
||||
{
|
||||
|
||||
let pe =
|
||||
if let initial = initialValues {
|
||||
@@ -59,7 +65,7 @@ public struct ParserRuntime: CustomStringConvertible {
|
||||
ProgramExecution()
|
||||
}
|
||||
|
||||
let (end_state, execution) = parser.execute(execution: pe)
|
||||
let (end_state, execution) = parser.call(execution: pe, arguments: arguments)
|
||||
if let error = execution.getError() {
|
||||
return .Error(error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user