Initial Array Access Implementation
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
@@ -17,10 +17,17 @@
|
|||||||
|
|
||||||
open class ProgramExecution: CustomStringConvertible {
|
open class ProgramExecution: CustomStringConvertible {
|
||||||
public var scopes: ValueScopes = ValueScopes()
|
public var scopes: ValueScopes = ValueScopes()
|
||||||
|
let initialValues: ValueScopes?
|
||||||
var error: Error?
|
var error: Error?
|
||||||
var debug: DebugLevel = DebugLevel.Error
|
var debug: DebugLevel = DebugLevel.Error
|
||||||
|
|
||||||
public init() {}
|
public init() {
|
||||||
|
initialValues = .none
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(withGlobalValues values: ValueScopes) {
|
||||||
|
initialValues = values
|
||||||
|
}
|
||||||
|
|
||||||
open var description: String {
|
open var description: String {
|
||||||
return "Runtime:\nScopes: \(scopes)"
|
return "Runtime:\nScopes: \(scopes)"
|
||||||
@@ -80,6 +87,10 @@ open class ProgramExecution: CustomStringConvertible {
|
|||||||
new_pe.scopes = new_scopes
|
new_pe.scopes = new_scopes
|
||||||
return new_pe
|
return new_pe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func initial_values() -> ValueScopes? {
|
||||||
|
return self.initialValues
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public typealias ValueScope = Scope<P4Value>
|
public typealias ValueScope = Scope<P4Value>
|
||||||
|
|||||||
@@ -198,8 +198,12 @@ public class P4IntValue: P4Value {
|
|||||||
public init(withValue value: Int) {
|
public init(withValue value: Int) {
|
||||||
self.value = value
|
self.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func access() -> Int {
|
||||||
|
return self.value
|
||||||
|
}
|
||||||
|
|
||||||
public func eq(rhs: P4Value) -> Bool {
|
public func eq(rhs: P4Value) -> Bool {
|
||||||
print("Int value equal.")
|
|
||||||
guard let int_rhs = rhs as? P4IntValue else {
|
guard let int_rhs = rhs as? P4IntValue else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -251,3 +255,49 @@ public class P4StringValue: P4Value {
|
|||||||
public class Packet {
|
public class Packet {
|
||||||
public init() {}
|
public init() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A P4 array type
|
||||||
|
public struct P4Array: P4Type {
|
||||||
|
|
||||||
|
public static func create() -> any P4Type {
|
||||||
|
return P4Array()
|
||||||
|
}
|
||||||
|
public var description: String {
|
||||||
|
return "Array"
|
||||||
|
}
|
||||||
|
public func eq(rhs: any P4Type) -> Bool {
|
||||||
|
return switch rhs {
|
||||||
|
case is P4Array: true
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An instance of a P4 array
|
||||||
|
public class P4ArrayValue: P4Value {
|
||||||
|
public func type() -> any P4Type {
|
||||||
|
return P4Array()
|
||||||
|
}
|
||||||
|
|
||||||
|
let value: Array<EvaluatableExpression>
|
||||||
|
|
||||||
|
public init(withValue value: Array<EvaluatableExpression>) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public func access(_ index: Int) -> EvaluatableExpression {
|
||||||
|
return self.value[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func eq(rhs: P4Value) -> Bool {
|
||||||
|
guard let _ = rhs as? P4ArrayValue else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// TODO!!
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
"\(self.value) of \(self.type()) type"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
public struct Scope<T>: CustomStringConvertible, Sequence {
|
public struct Scope<T>: CustomStringConvertible, Sequence {
|
||||||
public typealias Element = T
|
public typealias Element = (key: Identifier, value: T)
|
||||||
public typealias Iterator = Dictionary<Identifier, T>.Values.Iterator
|
public typealias Iterator = Dictionary<Identifier, T>.Iterator
|
||||||
public func makeIterator() -> Iterator {
|
public func makeIterator() -> Iterator {
|
||||||
self.symbols.values.makeIterator()
|
self.symbols.makeIterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
var symbols: [Identifier: T] = Dictionary()
|
var symbols: [Identifier: T] = Dictionary()
|
||||||
@@ -123,8 +123,8 @@ public struct Scopes<T>: CustomStringConvertible, Sequence {
|
|||||||
return .Error(Error(withMessage: "Cannot find \(identifier) in lexical scope."))
|
return .Error(Error(withMessage: "Cannot find \(identifier) in lexical scope."))
|
||||||
}
|
}
|
||||||
|
|
||||||
public typealias Element = T
|
public typealias Element = (key: Identifier, value: T)
|
||||||
public typealias Iterator = Dictionary<Identifier, T>.Values.Iterator
|
public typealias Iterator = Dictionary<Identifier, T>.Iterator
|
||||||
public func makeIterator() -> Iterator {
|
public func makeIterator() -> Iterator {
|
||||||
scopes.last?.makeIterator() ?? Scope<T>().makeIterator()
|
scopes.last?.makeIterator() ?? Scope<T>().makeIterator()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ struct Expression {
|
|||||||
|
|
||||||
let localElementsParsers: [CompilableExpression.Type] = [
|
let localElementsParsers: [CompilableExpression.Type] = [
|
||||||
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self,
|
||||||
BinaryOperatorExpression.self,
|
BinaryOperatorExpression.self, ArrayAccessExpression.self
|
||||||
]
|
]
|
||||||
|
|
||||||
for le_parser in localElementsParsers {
|
for le_parser in localElementsParsers {
|
||||||
@@ -311,3 +311,67 @@ extension BinaryOperatorExpression: CompilableExpression {
|
|||||||
withLhs: left_hand_side, withRhs: right_hand_side))
|
withLhs: left_hand_side, withRhs: right_hand_side))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ArrayAccessExpression: CompilableExpression {
|
||||||
|
static func compile(node: SwiftTreeSitter.Node, withContext context: CompilerContext) -> Common.Result<(any Common.EvaluatableExpression)?> {
|
||||||
|
let expression = node.child(at: 0)!
|
||||||
|
|
||||||
|
#SkipUnlessNodeType<Node, EvaluatableExpression?>(
|
||||||
|
node: expression, type: "arrayAccessExpression")
|
||||||
|
|
||||||
|
let array_access_expression_node = expression
|
||||||
|
|
||||||
|
var currentChildIdx = 0
|
||||||
|
var currentChildIdxSafe = 1
|
||||||
|
var currentChild: Node? = .none
|
||||||
|
|
||||||
|
// What is the "name" of the array?
|
||||||
|
if array_access_expression_node.childCount < currentChildIdxSafe {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorOnNode(node: node, withError: "Malformed array access expression"))
|
||||||
|
}
|
||||||
|
currentChild = expression.child(at: currentChildIdx)
|
||||||
|
|
||||||
|
#RequireNodeType<Node, EvaluatableExpression?>(
|
||||||
|
node: currentChild!, type: "expression",
|
||||||
|
nice_type_name: "array identifier expression")
|
||||||
|
let array_access_identifier_node = currentChild!
|
||||||
|
|
||||||
|
// Check for the [
|
||||||
|
currentChildIdx = currentChildIdx + 1
|
||||||
|
currentChildIdxSafe = currentChildIdxSafe + 1
|
||||||
|
if array_access_expression_node.childCount < currentChildIdxSafe {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorOnNode(node: node, withError: "Missing [ for array access expression")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// What is the indexor of the array?
|
||||||
|
currentChildIdx = currentChildIdx + 1
|
||||||
|
currentChildIdxSafe = currentChildIdxSafe + 1
|
||||||
|
if array_access_expression_node.childCount < currentChildIdxSafe {
|
||||||
|
return Result.Error(
|
||||||
|
ErrorOnNode(node: node, withError: "Missing indexor expression for array access expression")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
currentChild = array_access_expression_node.child(at: currentChildIdx)
|
||||||
|
|
||||||
|
#RequireNodeType<Node, EvaluatableExpression?>(
|
||||||
|
node: currentChild!, type: "expression",
|
||||||
|
nice_type_name: "array indexor expression")
|
||||||
|
|
||||||
|
let array_access_indexor_node = currentChild!
|
||||||
|
|
||||||
|
let maybe_array_identifier = Expression.Compile(node: array_access_identifier_node, withContext: context)
|
||||||
|
guard case Result.Ok(let array_identifier) = maybe_array_identifier else {
|
||||||
|
return Result.Error(maybe_array_identifier.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_array_indexor = Expression.Compile(node: array_access_indexor_node, withContext: context)
|
||||||
|
guard case Result.Ok(let array_indexor) = maybe_array_indexor else {
|
||||||
|
return Result.Error(maybe_array_indexor.error()!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok(ArrayAccessExpression(withName: array_identifier, withIndexor: array_indexor))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,10 @@ import TreeSitterP4
|
|||||||
|
|
||||||
public struct Program {
|
public struct Program {
|
||||||
public static func Compile(_ source: String) -> Result<P4Lang.Program> {
|
public static func Compile(_ source: String) -> Result<P4Lang.Program> {
|
||||||
|
return Program.Compile(source, withGlobalTypes: .none)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func Compile(_ source: String, withGlobalTypes globalTypes: LexicalScopes?) -> Result<P4Lang.Program> {
|
||||||
|
|
||||||
let maybe_parser = ConfigureP4Parser()
|
let maybe_parser = ConfigureP4Parser()
|
||||||
guard case .Ok(let p) = maybe_parser else {
|
guard case .Ok(let p) = maybe_parser else {
|
||||||
@@ -45,6 +49,11 @@ public struct Program {
|
|||||||
|
|
||||||
var errors: [Error] = Array()
|
var errors: [Error] = Array()
|
||||||
|
|
||||||
|
// If the caller gave any global types, add them here.
|
||||||
|
if let globalTypes = globalTypes {
|
||||||
|
compilation_context = compilation_context.update(newNames: globalTypes)
|
||||||
|
}
|
||||||
|
|
||||||
result?.rootNode?.enumerateNamedChildren { declaration_node in
|
result?.rootNode?.enumerateNamedChildren { declaration_node in
|
||||||
if declaration_node.nodeType != "declaration" {
|
if declaration_node.nodeType != "declaration" {
|
||||||
return
|
return
|
||||||
@@ -185,7 +194,9 @@ public struct Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Any of the types that are in the top-level scope should go into the program!
|
// Any of the types that are in the top-level scope should go into the program!
|
||||||
program.types = Array(compilation_context.names)
|
program.types = Array(compilation_context.names.map() { (_, v) in
|
||||||
|
v
|
||||||
|
})
|
||||||
return Result.Ok(program)
|
return Result.Ok(program)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,3 +72,13 @@ public struct BinaryOperatorExpression {
|
|||||||
self.right = rhs
|
self.right = rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ArrayAccessExpression {
|
||||||
|
public let indexor: EvaluatableExpression
|
||||||
|
public let name: EvaluatableExpression
|
||||||
|
|
||||||
|
public init(withName name: EvaluatableExpression, withIndexor indexor: EvaluatableExpression) {
|
||||||
|
self.name = name
|
||||||
|
self.indexor = indexor
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,3 +113,32 @@ extension BinaryOperatorExpression: EvaluatableExpression {
|
|||||||
return self.evaluator.1
|
return self.evaluator.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ArrayAccessExpression: EvaluatableExpression {
|
||||||
|
public func evaluate(execution: Common.ProgramExecution) -> Common.Result<any Common.P4Value> {
|
||||||
|
let maybe_name = self.name.evaluate(execution: execution)
|
||||||
|
guard case Result.Ok(let name) = maybe_name else {
|
||||||
|
return maybe_name
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_indexor = self.indexor.evaluate(execution: execution)
|
||||||
|
guard case Result.Ok(let indexor) = maybe_indexor else {
|
||||||
|
return maybe_indexor
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let indexor_int = indexor as? P4IntValue else {
|
||||||
|
return Result.Error(Error(withMessage: "\(indexor) cannot index an array"))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let array = name as? P4ArrayValue else {
|
||||||
|
return Result.Error(Error(withMessage: "\(name) does not name an array"))
|
||||||
|
}
|
||||||
|
let accessed = array.access(indexor_int.access())
|
||||||
|
|
||||||
|
return accessed.evaluate(execution: execution)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func type() -> any Common.P4Type {
|
||||||
|
return P4Int.create()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -120,6 +120,13 @@ extension Parser: ParserExecution {
|
|||||||
execution = execution.declare(identifier: accept.state().state, withValue: accept)
|
execution = execution.declare(identifier: accept.state().state, withValue: accept)
|
||||||
execution = execution.declare(identifier: reject.state().state, withValue: reject)
|
execution = execution.declare(identifier: reject.state().state, withValue: reject)
|
||||||
|
|
||||||
|
// Add initial values to the global scope
|
||||||
|
if let initial = execution.initial_values() {
|
||||||
|
for (name, value) in initial {
|
||||||
|
execution = execution.declare(identifier: name, withValue: value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// First, add every state to the scope!
|
// First, add every state to the scope!
|
||||||
for state in self.states.states {
|
for state in self.states.states {
|
||||||
execution = execution.declare(identifier: state.state, withValue: state)
|
execution = execution.declare(identifier: state.state, withValue: state)
|
||||||
|
|||||||
@@ -22,16 +22,27 @@ import P4Lang
|
|||||||
public struct ParserRuntime: CustomStringConvertible {
|
public struct ParserRuntime: CustomStringConvertible {
|
||||||
public var parser: Parser
|
public var parser: Parser
|
||||||
|
|
||||||
|
let initialValues: ValueScopes?
|
||||||
|
|
||||||
init(parser: Parser) {
|
init(parser: Parser) {
|
||||||
self.parser = parser
|
self.parser = parser
|
||||||
|
self.initialValues = .none
|
||||||
|
}
|
||||||
|
|
||||||
|
init(parser: Parser, withInitialValues initial: ValueScopes?) {
|
||||||
|
self.parser = parser
|
||||||
|
self.initialValues = initial
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a parser runtime from a P4 program
|
/// Create a parser runtime from a P4 program
|
||||||
public static func create(program: P4Lang.Program) -> Result<ParserRuntime> {
|
public static func create(program: P4Lang.Program) -> Result<ParserRuntime> {
|
||||||
|
return ParserRuntime.create(program: program, withInitialValues: .none)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func create(program: P4Lang.Program, withInitialValues initial: ValueScopes?) -> Result<ParserRuntime> {
|
||||||
return switch program.starting_parser() {
|
return switch program.starting_parser() {
|
||||||
case .Ok(let parser):
|
case .Ok(let parser):
|
||||||
.Ok(P4Runtime.ParserRuntime(parser: parser))
|
.Ok(P4Runtime.ParserRuntime(parser: parser, withInitialValues: initial))
|
||||||
case .Error(let error): .Error(error)
|
case .Error(let error): .Error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +50,13 @@ public struct ParserRuntime: CustomStringConvertible {
|
|||||||
/// Run the P4 parser on a given packet
|
/// Run the P4 parser on a given packet
|
||||||
public func run() -> Result<(ParserState, ProgramExecution)> {
|
public func run() -> Result<(ParserState, ProgramExecution)> {
|
||||||
|
|
||||||
let (end_state, execution) = parser.execute(execution: ProgramExecution())
|
let pe = if let initial = initialValues {
|
||||||
|
ProgramExecution(withGlobalValues: initial)
|
||||||
|
} else {
|
||||||
|
ProgramExecution()
|
||||||
|
}
|
||||||
|
|
||||||
|
let (end_state, execution) = parser.execute(execution: pe)
|
||||||
if let error = execution.getError() {
|
if let error = execution.getError() {
|
||||||
return .Error(error)
|
return .Error(error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,3 +229,59 @@ import TreeSitterP4
|
|||||||
// false
|
// false
|
||||||
#expect(state_result == P4Lang.reject)
|
#expect(state_result == P4Lang.reject)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func test_array_access() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
bool where_to = ta[1] == 2;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
var test_types = LexicalScopes().enter()
|
||||||
|
test_types = test_types.declare(identifier: Identifier(name: "ta"), withValue: P4Array.create())
|
||||||
|
var test_values = ValueScopes().enter()
|
||||||
|
test_values = test_values.declare(
|
||||||
|
identifier: Identifier(name: "ta"),
|
||||||
|
withValue: P4ArrayValue(withValue: [
|
||||||
|
P4IntValue(withValue: 1), P4IntValue(withValue: 2), P4IntValue(withValue: 3),
|
||||||
|
]))
|
||||||
|
let program = try #UseOkResult(
|
||||||
|
Program.Compile(simple_parser_declaration, withGlobalTypes: test_types))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
#expect(state_result == P4Lang.accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_array_access2() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
bool where_to = ta[0] == 2;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
var test_types = LexicalScopes().enter()
|
||||||
|
test_types = test_types.declare(identifier: Identifier(name: "ta"), withValue: P4Array.create())
|
||||||
|
var test_values = ValueScopes().enter()
|
||||||
|
test_values = test_values.declare(
|
||||||
|
identifier: Identifier(name: "ta"),
|
||||||
|
withValue: P4ArrayValue(withValue: [
|
||||||
|
P4IntValue(withValue: 1), P4IntValue(withValue: 2), P4IntValue(withValue: 3),
|
||||||
|
]))
|
||||||
|
let program = try #UseOkResult(
|
||||||
|
Program.Compile(simple_parser_declaration, withGlobalTypes: test_types))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program, withInitialValues: test_values))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
|
||||||
|
#expect(state_result == P4Lang.reject)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user