Allow Declaring Instances of Structs
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
This commit is contained in:
@@ -64,9 +64,6 @@ public struct Parser {
|
|||||||
"variableDeclaration": VariableDeclarationStatement.self,
|
"variableDeclaration": VariableDeclarationStatement.self,
|
||||||
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
|
"conditionalStatement": ConditionalStatement.self, "blockStatement": BlockStatement.self,
|
||||||
]
|
]
|
||||||
|
|
||||||
// Iterate through statement parsers and give each one a chance.
|
|
||||||
|
|
||||||
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
guard let parser = statementParsers[statement.nodeType ?? ""] else {
|
||||||
return Result.Error(
|
return Result.Error(
|
||||||
ErrorOnNode(
|
ErrorOnNode(
|
||||||
@@ -151,7 +148,7 @@ public struct Parser {
|
|||||||
return Result.Error(ErrorOnNode(node: node, withError: "Did not find expected statements"))
|
return Result.Error(ErrorOnNode(node: node, withError: "Did not find expected statements"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var parse_err: Error? = .none
|
var parse_errs: [Error] = Array()
|
||||||
var current_context = context
|
var current_context = context
|
||||||
var parsed_s: [EvaluatableStatement] = Array()
|
var parsed_s: [EvaluatableStatement] = Array()
|
||||||
|
|
||||||
@@ -163,12 +160,14 @@ public struct Parser {
|
|||||||
current_context = updated_context
|
current_context = updated_context
|
||||||
parsed_s.append(parsed_statement)
|
parsed_s.append(parsed_statement)
|
||||||
case .Error(let e):
|
case .Error(let e):
|
||||||
parse_err = e
|
parse_errs.append(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let parse_err = parse_err {
|
if !parse_errs.isEmpty {
|
||||||
return Result.Error(parse_err)
|
return Result.Error(Error(withMessage: parse_errs.map() { err in
|
||||||
|
return String(err.msg)
|
||||||
|
}.joined(separator: ";")))
|
||||||
}
|
}
|
||||||
return Result.Ok((parsed_s, current_context))
|
return Result.Ok((parsed_s, current_context))
|
||||||
}
|
}
|
||||||
@@ -224,7 +223,7 @@ public struct Parser {
|
|||||||
currentChildIdx += 2
|
currentChildIdx += 2
|
||||||
currentChildIdxSafe += 2
|
currentChildIdxSafe += 2
|
||||||
|
|
||||||
var parse_err: Error? = .none
|
var parse_errs: [Error] = Array()
|
||||||
var current_context = context
|
var current_context = context
|
||||||
var parsed_s: [EvaluatableStatement] = Array()
|
var parsed_s: [EvaluatableStatement] = Array()
|
||||||
|
|
||||||
@@ -240,14 +239,16 @@ public struct Parser {
|
|||||||
parsed_s = state_statements
|
parsed_s = state_statements
|
||||||
current_context = updated_context
|
current_context = updated_context
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
parse_err = error
|
parse_errs.append(error)
|
||||||
}
|
}
|
||||||
currentChildIdx += 1
|
currentChildIdx += 1
|
||||||
currentChildIdxSafe += 1
|
currentChildIdxSafe += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if let parse_err = parse_err {
|
if !parse_errs.isEmpty {
|
||||||
return Result.Error(parse_err)
|
return Result.Error(Error(withMessage: parse_errs.map() { err in
|
||||||
|
return String(err.msg)
|
||||||
|
}.joined(separator: ";")))
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.childCount < currentChildIdxSafe {
|
if node.childCount < currentChildIdxSafe {
|
||||||
|
|||||||
@@ -33,5 +33,5 @@ public protocol CompilableValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public protocol CompilableType {
|
public protocol CompilableType {
|
||||||
static func CompileType(type: String) -> Result<P4Type?>
|
static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Result<P4Type?>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,14 +182,6 @@ extension VariableDeclarationStatement: CompilableStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let maybe_rvalue = node.childCount > 3 ? node.child(at: 3) : .none
|
let maybe_rvalue = node.childCount > 3 ? node.child(at: 3) : .none
|
||||||
guard let rvalue = maybe_rvalue,
|
|
||||||
rvalue.nodeType == "expression"
|
|
||||||
else {
|
|
||||||
return Result.Error(
|
|
||||||
ErrorOnNode(
|
|
||||||
node: node,
|
|
||||||
withError: "Did not find initial value expression for variable declaration statement"))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard
|
guard
|
||||||
case .Ok(let parsed_variablename) = Identifier.Compile(
|
case .Ok(let parsed_variablename) = Identifier.Compile(
|
||||||
@@ -199,38 +191,47 @@ extension VariableDeclarationStatement: CompilableStatement {
|
|||||||
Error(withMessage: "Could not parse variable name"))
|
Error(withMessage: "Could not parse variable name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_parsed_rvalue = Expression.Compile(node: rvalue, withContext: context)
|
guard case .Ok(let declaration_p4_type) = Types.CompileType(type: typeref, withContext: context) else {
|
||||||
|
|
||||||
guard
|
|
||||||
case .Ok(let parsed_rvalue) = maybe_parsed_rvalue
|
|
||||||
else {
|
|
||||||
return .Error(maybe_parsed_rvalue.error()!)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard case .Ok(let declaration_p4_type) = Types.CompileBasicType(type: typeref.text!) else {
|
|
||||||
return Result.Error(
|
return Result.Error(
|
||||||
Error(withMessage: "Could not parse a P4 type from \(typeref.text!)"))
|
Error(withMessage: "Could not parse a P4 type from \(typeref.text!)"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsed_rvalue.type().eq(rhs: declaration_p4_type) {
|
var initializer: EvaluatableExpression = declaration_p4_type.def()
|
||||||
return Result.Ok(
|
// If there is an initializer, it must be an expression.
|
||||||
(
|
if let rvalue = maybe_rvalue {
|
||||||
VariableDeclarationStatement(
|
guard rvalue.nodeType == "expression" else {
|
||||||
identifier: parsed_variablename, withInitializer: parsed_rvalue),
|
return Result.Error(
|
||||||
// Context with updated names to include the newly declared name.
|
ErrorOnNode(
|
||||||
context.update(
|
node: node,
|
||||||
newNames: context.names.declare(
|
withError: "initial value for declaration statement is not an expression"))
|
||||||
identifier: parsed_variablename, withValue: declaration_p4_type))
|
}
|
||||||
))
|
|
||||||
|
|
||||||
} else {
|
let maybe_parsed_rvalue = Expression.Compile(node: rvalue, withContext: context)
|
||||||
return Result.Error(
|
guard
|
||||||
Error(
|
case .Ok(let parsed_rvalue) = maybe_parsed_rvalue
|
||||||
withMessage:
|
else {
|
||||||
"Cannot initialize \(parsed_variablename) (with type \(declaration_p4_type)) from rvalue with type \(parsed_rvalue.type())"
|
return .Error(maybe_parsed_rvalue.error()!)
|
||||||
))
|
}
|
||||||
|
|
||||||
|
if parsed_rvalue.type().eq(rhs: declaration_p4_type) {
|
||||||
|
initializer = parsed_rvalue
|
||||||
|
} else {
|
||||||
|
return Result.Error(
|
||||||
|
Error(
|
||||||
|
withMessage:
|
||||||
|
"Cannot initialize \(parsed_variablename) (with type \(declaration_p4_type)) from rvalue with type \(parsed_rvalue.type())"
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return Result.Ok(
|
||||||
|
(
|
||||||
|
VariableDeclarationStatement(
|
||||||
|
identifier: parsed_variablename, withInitializer: initializer),
|
||||||
|
// Context with updated names to include the newly declared name.
|
||||||
|
context.update(
|
||||||
|
newNames: context.names.declare(
|
||||||
|
identifier: parsed_variablename, withValue: declaration_p4_type))
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,27 +23,42 @@ import TreeSitterExtensions
|
|||||||
import TreeSitterP4
|
import TreeSitterP4
|
||||||
|
|
||||||
extension P4Boolean: CompilableType {
|
extension P4Boolean: CompilableType {
|
||||||
public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> {
|
public static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Common.Result<(any Common.P4Type)?> {
|
||||||
return type == "bool" ? .Ok(P4Boolean()) : .Ok(.none)
|
return type.text == "bool" ? .Ok(P4Boolean()) : .Ok(.none)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension P4Int: CompilableType {
|
extension P4Int: CompilableType {
|
||||||
public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> {
|
public static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Common.Result<(any Common.P4Type)?> {
|
||||||
return type == "int" ? .Ok(P4Int()) : .Ok(.none)
|
return type.text == "int" ? .Ok(P4Int()) : .Ok(.none)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension P4String: CompilableType {
|
extension P4String: CompilableType {
|
||||||
public static func CompileType(type: String) -> Common.Result<(any Common.P4Type)?> {
|
public static func CompileType(type: SwiftTreeSitter.Node, withContext: CompilerContext) -> Common.Result<(any Common.P4Type)?> {
|
||||||
return type == "string" ? .Ok(P4String()) : .Ok(.none)
|
return type.text == "string" ? .Ok(P4String()) : .Ok(.none)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension P4Struct: CompilableType {
|
||||||
|
public static func CompileType(type: SwiftTreeSitter.Node, withContext context: CompilerContext) -> Common.Result<(any Common.P4Type)?> {
|
||||||
|
let maybe_parsed_type_id = Identifier.Compile(node: type, withContext: context)
|
||||||
|
guard case .Ok(let parsed_type_id) = maybe_parsed_type_id else {
|
||||||
|
return .Error(maybe_parsed_type_id.error()!)
|
||||||
|
}
|
||||||
|
if case .Ok(let found_type) = context.types.lookup(identifier: parsed_type_id),
|
||||||
|
let found_struct_type = found_type as? P4Struct {
|
||||||
|
return .Ok(found_struct_type)
|
||||||
|
}
|
||||||
|
return .Ok(.none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct Types {
|
public struct Types {
|
||||||
static func CompileBasicType(type: String) -> Result<P4Type> {
|
static func CompileType(type: SwiftTreeSitter.Node, withContext context: CompilerContext) -> Result<P4Type> {
|
||||||
let type_parsers: [CompilableType.Type] = [P4Boolean.self, P4Int.self, P4String.self]
|
let type_parsers: [CompilableType.Type] = [P4Boolean.self, P4Int.self, P4String.self, P4Struct.self]
|
||||||
for type_parser in type_parsers {
|
for type_parser in type_parsers {
|
||||||
switch type_parser.CompileType(type: type) {
|
switch type_parser.CompileType(type: type, withContext: context) {
|
||||||
case .Ok(.some(let type)): return .Ok(type)
|
case .Ok(.some(let type)): return .Ok(type)
|
||||||
case .Ok(.none): continue
|
case .Ok(.none): continue
|
||||||
case .Error(let e): return .Error(e)
|
case .Error(let e): return .Error(e)
|
||||||
|
|||||||
@@ -62,6 +62,65 @@ import TreeSitterP4
|
|||||||
#expect(state_result == P4Lang.accept)
|
#expect(state_result == P4Lang.accept)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test func test_field_access_declared() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
Testing ts;
|
||||||
|
ts.yesno = true;
|
||||||
|
bool where_to = ts.yesno;
|
||||||
|
transition select (where_to) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
var test_types = TypeTypeScopes().enter()
|
||||||
|
let fields = P4StructFields([
|
||||||
|
P4StructFieldIdentifier(name: "yesno", withType: P4Boolean()),
|
||||||
|
P4StructFieldIdentifier(name: "count", withType: P4Int()),
|
||||||
|
])
|
||||||
|
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
||||||
|
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
||||||
|
|
||||||
|
let program = try #UseOkResult(
|
||||||
|
Program.Compile(simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: test_types))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
#expect(state_result == P4Lang.accept)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test func test_field_access_declared2() async throws {
|
||||||
|
let simple_parser_declaration = """
|
||||||
|
parser main_parser() {
|
||||||
|
state start {
|
||||||
|
Testing ts;
|
||||||
|
ts.yesno = true;
|
||||||
|
ts.count = 5;
|
||||||
|
bool where_to = ts.yesno;
|
||||||
|
transition select (ts.count == 5) {
|
||||||
|
true: accept;
|
||||||
|
false: reject;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
var test_types = TypeTypeScopes().enter()
|
||||||
|
let fields = P4StructFields([
|
||||||
|
P4StructFieldIdentifier(name: "yesno", withType: P4Boolean()),
|
||||||
|
P4StructFieldIdentifier(name: "count", withType: P4Int()),
|
||||||
|
])
|
||||||
|
let struct_type = P4Struct(withName: Identifier(name: "Testing"), andFields: fields)
|
||||||
|
test_types = test_types.declare(identifier: Identifier(name: "Testing"), withValue: struct_type)
|
||||||
|
|
||||||
|
let program = try #UseOkResult(
|
||||||
|
Program.Compile(simple_parser_declaration, withGlobalInstances: .none, withGlobalTypes: test_types))
|
||||||
|
let runtime = try #UseOkResult(P4Runtime.ParserRuntime.create(program: program))
|
||||||
|
let (state_result, _) = try! #UseOkResult(runtime.run())
|
||||||
|
#expect(state_result == P4Lang.accept)
|
||||||
|
}
|
||||||
|
|
||||||
@Test func test_field_access_opp() async throws {
|
@Test func test_field_access_opp() async throws {
|
||||||
let simple_parser_declaration = """
|
let simple_parser_declaration = """
|
||||||
parser main_parser() {
|
parser main_parser() {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default grammar({
|
|||||||
direction: $ => choice($.in, $.out, $.inout),
|
direction: $ => choice($.in, $.out, $.inout),
|
||||||
|
|
||||||
// Common - Types
|
// Common - Types
|
||||||
typeRef: $ => $.baseType,
|
typeRef: $ => choice($.baseType, $.type_identifier),
|
||||||
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */),
|
baseType: $ => choice($.bool, $.error, $.string, $.int, $.bit /* omitting "templated" types" */),
|
||||||
constructorParameters: $ => seq('(', optional($.parameterList), ')'),
|
constructorParameters: $ => seq('(', optional($.parameterList), ')'),
|
||||||
|
|
||||||
@@ -148,11 +148,11 @@ export default grammar({
|
|||||||
varbit: $ => "varbit",
|
varbit: $ => "varbit",
|
||||||
valueset: $ => "valueset",
|
valueset: $ => "valueset",
|
||||||
void: $ => "void",
|
void: $ => "void",
|
||||||
identifier: $ => /[a-z_]+/,
|
identifier: $ => /[A-Za-z_]+/,
|
||||||
type_identifier: $ => /[a-z]+/,
|
type_identifier: $ => /[A-Za-z_]+/,
|
||||||
string_literal: $ => /".*"/,
|
string_literal: $ => /".*"/,
|
||||||
integer: $ => /[0-9]+/,
|
integer: $ => /[0-9]+/,
|
||||||
annotation_literal: $ => /@[a-z_]+/,
|
annotation_literal: $ => /@[A-Za-z_]+/,
|
||||||
double_equal: $=> '==',
|
double_equal: $=> '==',
|
||||||
open_bracket: $=> '[',
|
open_bracket: $=> '[',
|
||||||
close_bracket: $=> ']',
|
close_bracket: $=> ']',
|
||||||
|
|||||||
@@ -186,3 +186,47 @@ parser simple() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
=========================
|
||||||
|
Non Basic Type Declaration (No Initial Value)
|
||||||
|
=========================
|
||||||
|
parser simple() {
|
||||||
|
state start {
|
||||||
|
header_t header;
|
||||||
|
transition accept;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
---
|
||||||
|
(p4program
|
||||||
|
(declaration
|
||||||
|
(parserDeclaration
|
||||||
|
(parserType
|
||||||
|
(parser)
|
||||||
|
(identifier)
|
||||||
|
)
|
||||||
|
(parserStates
|
||||||
|
(parserState
|
||||||
|
(state)
|
||||||
|
(identifier)
|
||||||
|
(parserStatements
|
||||||
|
(parserStatement
|
||||||
|
(variableDeclaration
|
||||||
|
(typeRef
|
||||||
|
(type_identifier)
|
||||||
|
)
|
||||||
|
(identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(parserTransitionStatement
|
||||||
|
(transition)
|
||||||
|
(transitionSelectionExpression
|
||||||
|
(identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user