diff --git a/Sources/Common/Execution.swift b/Sources/Common/Execution.swift index a7f27cc..33de911 100644 --- a/Sources/Common/Execution.swift +++ b/Sources/Common/Execution.swift @@ -17,10 +17,17 @@ open class ProgramExecution: CustomStringConvertible { public var scopes: ValueScopes = ValueScopes() + let initialValues: ValueScopes? var error: Error? var debug: DebugLevel = DebugLevel.Error - public init() {} + public init() { + initialValues = .none + } + + public init(withGlobalValues values: ValueScopes) { + initialValues = values + } open var description: String { return "Runtime:\nScopes: \(scopes)" @@ -80,6 +87,10 @@ open class ProgramExecution: CustomStringConvertible { new_pe.scopes = new_scopes return new_pe } + + public func initial_values() -> ValueScopes? { + return self.initialValues + } } public typealias ValueScope = Scope diff --git a/Sources/Common/ProgramTypes.swift b/Sources/Common/ProgramTypes.swift index f506da6..d033367 100644 --- a/Sources/Common/ProgramTypes.swift +++ b/Sources/Common/ProgramTypes.swift @@ -198,8 +198,12 @@ public class P4IntValue: P4Value { public init(withValue value: Int) { self.value = value } + + public func access() -> Int { + return self.value + } + public func eq(rhs: P4Value) -> Bool { - print("Int value equal.") guard let int_rhs = rhs as? P4IntValue else { return false } @@ -251,3 +255,49 @@ public class P4StringValue: P4Value { public class Packet { 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 + + public init(withValue value: Array) { + 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" + } +} diff --git a/Sources/Common/Scope.swift b/Sources/Common/Scope.swift index ac0e2df..2fd0f0b 100644 --- a/Sources/Common/Scope.swift +++ b/Sources/Common/Scope.swift @@ -16,10 +16,10 @@ // along with this program. If not, see . public struct Scope: CustomStringConvertible, Sequence { - public typealias Element = T - public typealias Iterator = Dictionary.Values.Iterator + public typealias Element = (key: Identifier, value: T) + public typealias Iterator = Dictionary.Iterator public func makeIterator() -> Iterator { - self.symbols.values.makeIterator() + self.symbols.makeIterator() } var symbols: [Identifier: T] = Dictionary() @@ -123,8 +123,8 @@ public struct Scopes: CustomStringConvertible, Sequence { return .Error(Error(withMessage: "Cannot find \(identifier) in lexical scope.")) } - public typealias Element = T - public typealias Iterator = Dictionary.Values.Iterator + public typealias Element = (key: Identifier, value: T) + public typealias Iterator = Dictionary.Iterator public func makeIterator() -> Iterator { scopes.last?.makeIterator() ?? Scope().makeIterator() } diff --git a/Sources/P4Compiler/Expression.swift b/Sources/P4Compiler/Expression.swift index 8f329a3..768e688 100644 --- a/Sources/P4Compiler/Expression.swift +++ b/Sources/P4Compiler/Expression.swift @@ -110,7 +110,7 @@ struct Expression { let localElementsParsers: [CompilableExpression.Type] = [ P4BooleanValue.self, P4StringValue.self, P4IntValue.self, TypedIdentifier.self, - BinaryOperatorExpression.self, + BinaryOperatorExpression.self, ArrayAccessExpression.self ] for le_parser in localElementsParsers { @@ -311,3 +311,67 @@ extension BinaryOperatorExpression: CompilableExpression { 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: 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: 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: 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)) + } +} \ No newline at end of file diff --git a/Sources/P4Compiler/Program.swift b/Sources/P4Compiler/Program.swift index 4391e1b..bc41f3c 100644 --- a/Sources/P4Compiler/Program.swift +++ b/Sources/P4Compiler/Program.swift @@ -24,6 +24,10 @@ import TreeSitterP4 public struct Program { public static func Compile(_ source: String) -> Result { + return Program.Compile(source, withGlobalTypes: .none) + } + + public static func Compile(_ source: String, withGlobalTypes globalTypes: LexicalScopes?) -> Result { let maybe_parser = ConfigureP4Parser() guard case .Ok(let p) = maybe_parser else { @@ -45,6 +49,11 @@ public struct Program { 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 if declaration_node.nodeType != "declaration" { return @@ -185,7 +194,9 @@ public struct 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) } } diff --git a/Sources/P4Lang/Expressions.swift b/Sources/P4Lang/Expressions.swift index b9b1691..b880fe6 100644 --- a/Sources/P4Lang/Expressions.swift +++ b/Sources/P4Lang/Expressions.swift @@ -72,3 +72,13 @@ public struct BinaryOperatorExpression { 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 + } +} \ No newline at end of file diff --git a/Sources/P4Runtime/Expressions.swift b/Sources/P4Runtime/Expressions.swift index 878ec26..69b92a8 100644 --- a/Sources/P4Runtime/Expressions.swift +++ b/Sources/P4Runtime/Expressions.swift @@ -113,3 +113,32 @@ extension BinaryOperatorExpression: EvaluatableExpression { return self.evaluator.1 } } + +extension ArrayAccessExpression: EvaluatableExpression { + public func evaluate(execution: Common.ProgramExecution) -> Common.Result { + 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() + } +} \ No newline at end of file diff --git a/Sources/P4Runtime/Parser.swift b/Sources/P4Runtime/Parser.swift index 1d5feb6..93cf585 100644 --- a/Sources/P4Runtime/Parser.swift +++ b/Sources/P4Runtime/Parser.swift @@ -120,6 +120,13 @@ extension Parser: ParserExecution { execution = execution.declare(identifier: accept.state().state, withValue: accept) 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! for state in self.states.states { execution = execution.declare(identifier: state.state, withValue: state) diff --git a/Sources/P4Runtime/Runtime.swift b/Sources/P4Runtime/Runtime.swift index 3eaed29..1f041b4 100644 --- a/Sources/P4Runtime/Runtime.swift +++ b/Sources/P4Runtime/Runtime.swift @@ -22,16 +22,27 @@ import P4Lang public struct ParserRuntime: CustomStringConvertible { public var parser: Parser + let initialValues: ValueScopes? + init(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 public static func create(program: P4Lang.Program) -> Result { + return ParserRuntime.create(program: program, withInitialValues: .none) + } + public static func create(program: P4Lang.Program, withInitialValues initial: ValueScopes?) -> Result { return switch program.starting_parser() { case .Ok(let parser): - .Ok(P4Runtime.ParserRuntime(parser: parser)) + .Ok(P4Runtime.ParserRuntime(parser: parser, withInitialValues: initial)) case .Error(let error): .Error(error) } } @@ -39,7 +50,13 @@ public struct ParserRuntime: CustomStringConvertible { /// Run the P4 parser on a given packet 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() { return .Error(error) } diff --git a/Tests/p4rseTests/ValueTypeParserTests.swift b/Tests/p4rseTests/ValueTypeParserTests.swift index 2cfcee0..e261e7c 100644 --- a/Tests/p4rseTests/ValueTypeParserTests.swift +++ b/Tests/p4rseTests/ValueTypeParserTests.swift @@ -229,3 +229,59 @@ import TreeSitterP4 // false #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) +}