From 2cbf871d09c2db98b05be72ed509be8d825e30bc Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Sat, 27 Jul 2024 20:23:03 -0700 Subject: [PATCH] Add codegen for new rust yunq parser. --- rust/lib/yellowstone/src/lib.rs | 81 ++++++++++++++-- rust/lib/yunq-derive/src/lib.rs | 10 ++ rust/lib/yunq/src/message.rs | 24 +++++ yunq/rust/Cargo.lock | 59 ++++++------ yunq/rust/Cargo.toml | 6 +- yunq/rust/src/codegen.rs | 157 ++++++++++++++++++++++---------- yunq/rust/src/main.rs | 14 +-- yunq/rust/src/parser.rs | 6 +- 8 files changed, 261 insertions(+), 96 deletions(-) diff --git a/rust/lib/yellowstone/src/lib.rs b/rust/lib/yellowstone/src/lib.rs index d5e025f..5e255b3 100644 --- a/rust/lib/yellowstone/src/lib.rs +++ b/rust/lib/yellowstone/src/lib.rs @@ -1,7 +1,5 @@ #![no_std] - extern crate alloc; - use alloc::string::String; use alloc::string::ToString; use mammoth::syscall::z_cap_t; @@ -9,22 +7,54 @@ use mammoth::syscall::ZError; use yunq::ByteBuffer; use yunq::YunqMessage; use yunq_derive::YunqMessage; - +#[derive(YunqMessage)] +pub struct RegisterEndpointRequest { + pub endpoint_name: String, + pub endpoint_capability: z_cap_t, +} #[derive(YunqMessage)] pub struct GetEndpointRequest { pub endpoint_name: String, } - #[derive(YunqMessage)] pub struct Endpoint { pub endpoint: z_cap_t, } - +#[derive(YunqMessage)] +pub struct AhciInfo { + pub ahci_region: z_cap_t, + pub region_length: u64, +} +#[derive(YunqMessage)] +pub struct XhciInfo { + pub xhci_region: z_cap_t, + pub region_length: u64, +} +#[derive(YunqMessage)] +pub struct FramebufferInfo { + pub address_phys: u64, + pub width: u64, + pub height: u64, + pub pitch: u64, + pub bpp: u64, + pub memory_model: u64, + pub red_mask_size: u64, + pub red_mask_shift: u64, + pub green_mask_size: u64, + pub green_mask_shift: u64, + pub blue_mask_size: u64, + pub blue_mask_shift: u64, +} +#[derive(YunqMessage)] +pub struct DenaliInfo { + pub denali_endpoint: z_cap_t, + pub device_id: u64, + pub lba_offset: u64, +} pub struct YellowstoneClient { endpoint_cap: z_cap_t, byte_buffer: ByteBuffer<0x1000>, } - impl YellowstoneClient { pub fn new(endpoint_cap: z_cap_t) -> Self { Self { @@ -32,7 +62,44 @@ impl YellowstoneClient { byte_buffer: ByteBuffer::new(), } } - pub fn get_endpoint(&mut self, req: &GetEndpointRequest) -> Result { + pub fn register_endpoint( + &mut self, + req: &RegisterEndpointRequest, + ) -> Result { yunq::client::call_endpoint(req, &mut self.byte_buffer, self.endpoint_cap) } + pub fn get_endpoint( + &mut self, + req: &GetEndpointRequest, + ) -> Result { + yunq::client::call_endpoint(req, &mut self.byte_buffer, self.endpoint_cap) + } + pub fn get_ahci_info(&mut self) -> Result { + yunq::client::call_endpoint( + &yunq::message::Empty {}, + &mut self.byte_buffer, + self.endpoint_cap, + ) + } + pub fn get_xhci_info(&mut self) -> Result { + yunq::client::call_endpoint( + &yunq::message::Empty {}, + &mut self.byte_buffer, + self.endpoint_cap, + ) + } + pub fn get_framebuffer_info(&mut self) -> Result { + yunq::client::call_endpoint( + &yunq::message::Empty {}, + &mut self.byte_buffer, + self.endpoint_cap, + ) + } + pub fn get_denali(&mut self) -> Result { + yunq::client::call_endpoint( + &yunq::message::Empty {}, + &mut self.byte_buffer, + self.endpoint_cap, + ) + } } diff --git a/rust/lib/yunq-derive/src/lib.rs b/rust/lib/yunq-derive/src/lib.rs index 8311244..cf72f4a 100644 --- a/rust/lib/yunq-derive/src/lib.rs +++ b/rust/lib/yunq-derive/src/lib.rs @@ -26,6 +26,12 @@ fn serialize_field(name: &Ident, ind: usize, path: &Path) -> proc_macro2::TokenS buf.write_at(yunq::message::field_offset(offset, #ind), cap_ind as u64)?; } } + } else if path.is_ident("u64") { + quote! { + { + buf.write_at(yunq::message::field_offset(offset, #ind), self.#name as u64)?; + } + } } else { panic!( "Serialization not implemented for: {}", @@ -51,6 +57,10 @@ fn parse_field(name: &Ident, ind: usize, path: &Path) -> proc_macro2::TokenStrea caps[cap_ind as usize] }; } + } else if path.is_ident("u64") { + quote! { + let #name = buf.at::(yunq::message::field_offset(offset, #ind))?; + } } else { panic!("Parsing not implemented for: {}", path.into_token_stream()); } diff --git a/rust/lib/yunq/src/message.rs b/rust/lib/yunq/src/message.rs index e114343..3369cba 100644 --- a/rust/lib/yunq/src/message.rs +++ b/rust/lib/yunq/src/message.rs @@ -26,3 +26,27 @@ pub trait YunqMessage { caps: &mut Vec, ) -> Result; } + +pub struct Empty {} + +impl YunqMessage for Empty { + fn parse( + buf: &ByteBuffer, + offset: usize, + caps: &Vec, + ) -> Result + where + Self: Sized, + { + todo!() + } + + fn serialize( + &self, + buf: &mut ByteBuffer, + offset: usize, + caps: &mut Vec, + ) -> Result { + todo!() + } +} diff --git a/yunq/rust/Cargo.lock b/yunq/rust/Cargo.lock index d5f212d..145ddd8 100644 --- a/yunq/rust/Cargo.lock +++ b/yunq/rust/Cargo.lock @@ -98,25 +98,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] -name = "genco" -version = "0.17.9" +name = "convert_case" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afac3cbb14db69ac9fef9cdb60d8a87e39a7a527f85a81a923436efa40ad42c6" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ - "genco-macros", - "relative-path", - "smallvec", -] - -[[package]] -name = "genco-macros" -version = "0.17.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553630feadf7b76442b0849fd25fdf89b860d933623aec9693fed19af0400c78" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "unicode-segmentation", ] [[package]] @@ -131,6 +118,16 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.85" @@ -149,18 +146,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "relative-path" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "strsim" version = "0.11.1" @@ -169,9 +154,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -184,6 +169,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "utf8parse" version = "0.2.2" @@ -268,5 +259,9 @@ name = "yunq" version = "0.1.0" dependencies = [ "clap", - "genco", + "convert_case", + "prettyplease", + "proc-macro2", + "quote", + "syn", ] diff --git a/yunq/rust/Cargo.toml b/yunq/rust/Cargo.toml index 8c0c88a..2550596 100644 --- a/yunq/rust/Cargo.toml +++ b/yunq/rust/Cargo.toml @@ -5,4 +5,8 @@ edition = "2021" [dependencies] clap = { version = "4.5.7", features = ["derive"] } -genco = { version = "0.17.9" } +convert_case = "0.6.0" +prettyplease = "0.2.20" +proc-macro2 = { version = "1.0" } +quote = { version = "1.0" } +syn = "2.0.72" diff --git a/yunq/rust/src/codegen.rs b/yunq/rust/src/codegen.rs index cf07f24..2b2ca34 100644 --- a/yunq/rust/src/codegen.rs +++ b/yunq/rust/src/codegen.rs @@ -1,53 +1,116 @@ -use crate::parser::{Decl, Field, Interface, Message}; -use genco::fmt; -use genco::fmt::FmtWriter; -use genco::prelude::quote_fn; -use genco::prelude::quote_in; -use genco::prelude::rust; -use genco::prelude::FormatInto; -use genco::prelude::Rust; +use crate::parser::{Decl, Interface, Message, Method}; +use convert_case::{Case, Casing}; +use proc_macro2::Ident; +use proc_macro2::Span; +use proc_macro2::TokenStream; +use quote::quote; -pub fn generate_field_def<'a>(field: &'a Field) -> impl FormatInto + 'a { - quote_fn!( - $(field.name.clone()): $(field.field_type.rust_type()), - ) +fn ident(s: &str) -> Ident { + Ident::new(s, Span::call_site()) } -pub fn generate_message_code<'a>(message: &'a Message) -> impl FormatInto + 'a { - quote_fn!( - struct $(&message.name) {$['\r'] - $(for field in &message.fields => - $[' ']$(generate_field_def(field))$['\r'] - )}$['\n'] - - impl $(&message.name) {$['\r'] - jjj - - }$['\n'] - ) -} - -pub fn generate_code(ast: &Vec) -> Result { - let mut tokens = rust::Tokens::new(); - - for decl in ast { - match decl { - Decl::Message(message) => quote_in!(tokens => $(generate_message_code(message))), - _ => {} +fn generate_message(message: &Message) -> TokenStream { + let name = ident(&message.name); + let field_names = message.fields.iter().map(|f| ident(&f.name)); + let field_types = message + .fields + .iter() + .map(|f| Ident::new(f.field_type.rust_type(), Span::call_site())); + quote! { + #[derive(YunqMessage)] + pub struct #name { + #(pub #field_names: #field_types),* } + } - - let mut w = FmtWriter::new(String::new()); - - let fmt = fmt::Config::from_lang::() - .with_indentation(fmt::Indentation::Space(4)) - .with_newline("\n"); - - let config = rust::Config::default() - // Prettier imports and use. - .with_default_import(rust::ImportMode::Qualified); - - tokens.format_file(&mut w.as_formatter(&fmt), &config)?; - - Ok(w.into_inner()) +} + +fn generate_method(method: &Method) -> TokenStream { + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(resp)) => quote! { + pub fn #name (&mut self, req: & #req) -> Result<#resp, ZError> { + yunq::client::call_endpoint(req, &mut self.byte_buffer, self.endpoint_cap) + } + }, + (Some(req), None) => quote! { + pub fn #name (&mut self, req: & #req) -> Result { + yunq::client::call_endpoint(req, &mut self.byte_buffer, self.endpoint_cap) + } + }, + (None, Some(resp)) => quote! { + pub fn #name (&mut self) -> Result<#resp, ZError> { + yunq::client::call_endpoint(&yunq::message::Empty{}, &mut self.byte_buffer, self.endpoint_cap) + } + }, + _ => unreachable!(), + } +} + +fn generate_interface(interface: &Interface) -> TokenStream { + let client_name = interface.name.clone() + "Client"; + let name = ident(&client_name); + let methods = interface.methods.iter().map(|m| generate_method(&m)); + quote! { + pub struct #name { + endpoint_cap: z_cap_t, + byte_buffer: ByteBuffer<0x1000>, + } + + impl #name { + pub fn new(endpoint_cap: z_cap_t) -> Self { + Self { + endpoint_cap, + byte_buffer: ByteBuffer::new(), + } + } + #(#methods)* + } + + } +} + +pub fn generate_code(ast: &Vec) -> String { + let prelude = quote! { + #![no_std] + + extern crate alloc; + + use alloc::string::String; + use alloc::string::ToString; + use mammoth::syscall::z_cap_t; + use mammoth::syscall::ZError; + use yunq::ByteBuffer; + use yunq::YunqMessage; + use yunq_derive::YunqMessage; + }; + + let message_decls = ast + .iter() + .filter_map(|decl| match decl { + Decl::Message(m) => Some(m), + _ => None, + }) + .map(|message| generate_message(&message)); + + let interface_decls = ast + .iter() + .filter_map(|decl| match decl { + Decl::Interface(i) => Some(i), + _ => None, + }) + .map(|interface| generate_interface(&interface)); + + let output = quote! { + #prelude + + #(#message_decls)* + + #(#interface_decls)* + } + .to_string(); + + prettyplease::unparse(&syn::parse_file(&output).unwrap()) } diff --git a/yunq/rust/src/main.rs b/yunq/rust/src/main.rs index c9fc119..21ece22 100644 --- a/yunq/rust/src/main.rs +++ b/yunq/rust/src/main.rs @@ -3,7 +3,7 @@ mod lexer; mod parser; use std::error::Error; -use std::fs::read_to_string; +use std::fs; use clap::Parser; @@ -13,22 +13,24 @@ struct Args { // The .yunq file to parse #[arg(short, long)] input_path: String, + + // The .rs file to generate + #[arg(short, long)] + output_path: String, } fn main() -> Result<(), Box> { let args = Args::parse(); - let input = read_to_string(args.input_path)?; + let input = fs::read_to_string(args.input_path)?; let tokens = lexer::lex_input(&input)?; let mut ast_parser = parser::Parser::new(&tokens); ast_parser.parse_ast()?; ast_parser.type_check()?; - for decl in ast_parser.ast() { - println!("{:?}", decl); - } + let code = codegen::generate_code(ast_parser.ast()); - println!("{}", codegen::generate_code(ast_parser.ast())?); + fs::write(args.output_path, code)?; Ok(()) } diff --git a/yunq/rust/src/parser.rs b/yunq/rust/src/parser.rs index 85d7263..f116fb9 100644 --- a/yunq/rust/src/parser.rs +++ b/yunq/rust/src/parser.rs @@ -23,7 +23,7 @@ impl Type { Type::I64 => "i64", Type::String => "String", Type::Bytes => "Vec", - Type::Capability => "u64", + Type::Capability => "z_cap_t", Type::Message(s) => s, } } @@ -102,8 +102,8 @@ impl Debug for Method { } pub struct Interface { - name: String, - methods: Vec, + pub name: String, + pub methods: Vec, } pub enum Decl {