From 53197b51e5edbb1b195070523dbfd8d9ba05d847 Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch Date: Thu, 15 Jun 2023 09:42:06 +0200 Subject: Add a Swift backend for LibertyXDR --- tools/lxdrgen-swift.awk | 276 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 tools/lxdrgen-swift.awk (limited to 'tools/lxdrgen-swift.awk') diff --git a/tools/lxdrgen-swift.awk b/tools/lxdrgen-swift.awk new file mode 100644 index 0000000..28554c9 --- /dev/null +++ b/tools/lxdrgen-swift.awk @@ -0,0 +1,276 @@ +# lxdrgen-swift.awk: Swift backend for lxdrgen.awk. +# +# Copyright (c) 2023, Přemysl Eric Janouch +# SPDX-License-Identifier: 0BSD + +function define_internal(name, swifttype) { + Types[name] = "internal" + CodegenSwiftType[name] = swifttype + CodegenDeserialize[name] = "%s.read()" +} + +function define_sint(size, shortname, swifttype) { + shortname = "i" size + swifttype = "Int" size + define_internal(shortname, swifttype) +} + +function define_uint(size, shortname, swifttype) { + shortname = "u" size + swifttype = "UInt" size + define_internal(shortname, swifttype) +} + +function codegen_begin() { + define_sint("8") + define_sint("16") + define_sint("32") + define_sint("64") + define_uint("8") + define_uint("16") + define_uint("32") + define_uint("64") + define_internal("bool", "Bool") + define_internal("string", "String") + + print "// Code generated from " FILENAME ". DO NOT EDIT." + print "import Foundation" + print "" + print "public struct RelayReader {" + print "\tpublic var data: Data" + print "" + print "\tpublic enum ReadError: Error {" + print "\t\tcase unexpectedEOF" + print "\t\tcase invalidEncoding" + print "\t\tcase overflow" + print "\t\tcase unexpectedValue" + print "\t}" + print "" + print "\tpublic mutating func read() throws -> T {" + print "\t\tlet size = MemoryLayout.size" + print "\t\tguard data.count >= size else {" + print "\t\t\tthrow ReadError.unexpectedEOF" + print "\t\t}" + print "\t\tvar acc: T = 0" + print "\t\tdata.prefix(size).forEach { acc = acc << 8 | T($0) }" + print "\t\tdata = data.dropFirst(size)" + print "\t\treturn acc" + print "\t}" + print "" + print "\tpublic mutating func read() throws -> Bool {" + print "\t\ttry read() != UInt8(0)" + print "\t}" + print "" + print "\tpublic mutating func read() throws -> String {" + print "\t\tlet size: UInt32 = try self.read()" + print "\t\tguard let count = Int(exactly: size) else {" + print "\t\t\tthrow ReadError.overflow" + print "\t\t}" + print "\t\tguard data.count >= count else {" + print "\t\t\tthrow ReadError.unexpectedEOF" + print "\t\t}" + print "\t\tdefer {" + print "\t\t\tdata = data.dropFirst(count)" + print "\t\t}" + print "\t\tif let s = String(data: data.prefix(count), encoding: .utf8) {" + print "\t\t\treturn s" + print "\t\t} else {" + print "\t\t\tthrow ReadError.invalidEncoding" + print "\t\t}" + print "\t}" + print "" + print "\tpublic mutating func read<" \ + "T: RawRepresentable>() throws -> T {" + print "\t\tguard let value = T(rawValue: try read()) else {" + print "\t\t\tthrow ReadError.unexpectedValue" + print "\t\t}" + print "\t\treturn value" + print "\t}" + print "" + print "\tpublic mutating func read(" + print "\t\t\t_ read: (inout Self) throws -> T) throws -> [T] {" + print "\t\tlet size: UInt32 = try self.read()" + print "\t\tguard let count = Int(exactly: size) else {" + print "\t\t\tthrow ReadError.overflow" + print "\t\t}" + print "\t\tvar array = [T]()" + print "\t\tarray.reserveCapacity(count)" + print "\t\tfor _ in 0..(_ number: T) {" + print "\t\tvar n = number.byteSwapped" + print "\t\tfor _ in 0...size {" + print "\t\t\tdata.append(UInt8(truncatingIfNeeded: n))" + print "\t\t\tn >>= 8" + print "\t\t}" + print "\t}" + print "" + print "\tpublic mutating func append(_ bool: Bool) {" + print "\t\tappend(UInt8(bool ? 1 : 0))" + print "\t}" + print "" + print "\tpublic mutating func append(_ string: String) {" + print "\t\tlet bytes = string.data(using: .utf8)!" + print "\t\tappend(UInt32(bytes.count))" + print "\t\tdata.append(bytes)" + print "\t}" + print "" + print "\tpublic mutating func append<" \ + "T: RawRepresentable>(_ value: T) {" + print "\t\tappend(value.rawValue)" + print "\t}" + print "" + print "\tpublic mutating func append(" + print "\t\t\t_ array: Array, _ write: (inout Self, T) -> ()) {" + print "\t\tappend(UInt32(array.count))" + print "\t\tfor i in 0..(_ value: T) {" + print "\t\tvalue.encode(to: &self)" + print "\t}" + print "}" + print "" + print "public protocol RelayEncodable { " \ + "func encode(to: inout RelayWriter) }" +} + +function codegen_constant(name, value) { + print "" + print "public let " decapitalize(PrefixCamel snaketocamel(name)) " = " value +} + +function codegen_enum_value(name, subname, value, cg) { + append(cg, "fields", + "\tcase " decapitalize(snaketocamel(subname)) " = " value "\n") +} + +function codegen_enum(name, cg, swifttype) { + swifttype = PrefixCamel name + print "" + print "public enum " swifttype ": Int8 {" + print cg["fields"] "}" + + CodegenSwiftType[name] = swifttype + CodegenDeserialize[name] = "%s.read()" + for (i in cg) + delete cg[i] +} + +function codegen_struct_field(d, cg, camel) { + camel = decapitalize(snaketocamel(d["name"])) + if (!d["isarray"]) { + append(cg, "fields", + "\tpublic var " camel ": " CodegenSwiftType[d["type"]] "\n") + append(cg, "deserialize", + "\t\tself." camel " = try " \ + sprintf(CodegenDeserialize[d["type"]], "from") "\n") + append(cg, "serialize", + "\t\tto.append(self." camel ")\n") + return + } + + append(cg, "fields", + "\tpublic var " camel ": [" CodegenSwiftType[d["type"]] "]\n") + append(cg, "deserialize", + "\t\tself." camel " = try from.read() { r in try " \ + sprintf(CodegenDeserialize[d["type"]], "r") " }\n") + append(cg, "serialize", + "\t\tto.append(self." camel ") { (w, value) in w.append(value) }\n") +} + +function codegen_struct_tag(d, cg, camel) { + camel = decapitalize(snaketocamel(d["name"])) + append(cg, "serialize", + "\t\tto.append(self." camel ")\n") +} + +function codegen_struct(name, cg, swifttype) { + swifttype = PrefixCamel name + print "" + print "public struct " swifttype " {\n" cg["fields"] "}" + print "" + print "extension " swifttype ": " PrefixCamel "Encodable {" + print "\tpublic init(from: inout RelayReader) throws {" + print cg["deserialize"] "\t}" + print "" + print "\tpublic func encode(to: inout RelayWriter) {" + print cg["serialize"] "\t}" + print "}" + + CodegenSwiftType[name] = swifttype + CodegenDeserialize[name] = "%s.read()" + for (i in cg) + delete cg[i] +} + +function codegen_union_tag(d, cg) { + cg["tagtype"] = d["type"] + cg["tagname"] = decapitalize(snaketocamel(d["name"])) +} + +function codegen_union_struct(name, casename, cg, scg, swifttype) { + # And thus not all generated structs are present in Types. + swifttype = PrefixCamel name snaketocamel(casename) + casename = decapitalize(snaketocamel(casename)) + print "" + print "public struct " swifttype ": " PrefixCamel name " {" + print "\tpublic var " cg["tagname"] \ + ": " CodegenSwiftType[cg["tagtype"]] " { ." casename " }" + print scg["fields"] "}" + print "" + print "extension " swifttype ": " PrefixCamel "Encodable {" + print "\tfileprivate init(from: inout RelayReader) throws {" + print scg["deserialize"] "\t}" + print "" + print "\tpublic func encode(to: inout RelayWriter) {" + print scg["serialize"] "\t}" + print "}" + + append(cg, "cases", "\tcase ." casename ":\n" \ + "\t\treturn try " swifttype "(from: &from)\n") + + CodegenSwiftType[name] = swifttype + CodegenDeserialize[name] = "%s.read()" + for (i in scg) + delete scg[i] +} + +function codegen_union(name, cg, swifttype, init) { + # Classes don't have automatic member-wise initializers, + # thus using structs and protocols. + swifttype = PrefixCamel name + print "" + print "public protocol " swifttype ": " PrefixCamel "Encodable {" + print "\tvar " cg["tagname"] ": " CodegenSwiftType[cg["tagtype"]] " { get }" + print "}" + + init = decapitalize(swifttype) + print "" + print "public func " init \ + "(from: inout RelayReader) throws -> " swifttype " {" + print "\tlet " cg["tagname"] ": " CodegenSwiftType[cg["tagtype"]] \ + " = try from.read()" + print "\tswitch " cg["tagname"] " {" + # TODO: Only generate the default if there are remaining values, + # so that swiftc doesn't produce warnings. + print cg["cases"] "\tdefault:" + print "\t\tthrow RelayReader.ReadError.unexpectedValue" + print"\t}" + print "}" + + CodegenSwiftType[name] = swifttype + CodegenDeserialize[name] = init "(from: &%s)" + for (i in cg) + delete cg[i] +} -- cgit v1.2.3-54-g00ecf