diff --git a/Cargo.lock b/Cargo.lock index 24b26cc..17acdf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,7 @@ version = "0.1.0" dependencies = [ "codespan-reporting", "derive_more", + "itertools", "logos", "proc-macro2", "quote", @@ -174,6 +175,12 @@ dependencies = [ "syn 1.0.105", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "fnv" version = "1.0.7" @@ -229,6 +236,15 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "libc" version = "0.2.138" diff --git a/programs/aidl/Cargo.toml b/programs/aidl/Cargo.toml index 75503f4..265768f 100644 --- a/programs/aidl/Cargo.toml +++ b/programs/aidl/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] codespan-reporting = "0.11.1" derive_more = "0.99.17" +itertools = "0.10.5" logos = "0" proc-macro2 = "1.0.56" quote = "1.0.26" diff --git a/programs/aidl/SPEC.md b/programs/aidl/SPEC.md index ae2279a..667a6c1 100644 --- a/programs/aidl/SPEC.md +++ b/programs/aidl/SPEC.md @@ -4,7 +4,7 @@ The example implementation will be in rust IDL | Rust __________ -boolean | bool +Boolean | bool I8 | i8 I16 | i16 I32 | i32 @@ -15,8 +15,8 @@ U32 | u32 U64 | u64 F32 | f32 F64 | f64 -Constant X Y Z | const X: Y = Z; -Type | type +Constant X = Make Y { Z } | const X: Y = Y { Z }; +Alias | type Vector | Vec -Array[X;Y] | [X;Y] -Function X accepts(YX) returns(ZX) | fn X(YX) -> ZX +Array | [X; Y] +Function X Takes(YX) Returns(ZX) | fn X(YX) -> ZX diff --git a/programs/aidl/assets/core.idl b/programs/aidl/assets/core.idl index 330598f..58b42e6 100644 --- a/programs/aidl/assets/core.idl +++ b/programs/aidl/assets/core.idl @@ -1,11 +1,5 @@ -Alias Byte = U8; -Alias Int = U32; -Alias String = Vector; - -Enumeration Boolean { - False = 0, - True = 1, -} +Alias Byte = u8; +Alias Int = u32; Enumeration Nothing {} @@ -14,6 +8,11 @@ Enumeration Option { Some(T) } +Enumeration Result { + Ok(T), + Err(E) +} + Structure Version { major: Byte, minor: Byte, diff --git a/programs/aidl/assets/core.rs b/programs/aidl/assets/core.rs new file mode 100644 index 0000000..cfa749c --- /dev/null +++ b/programs/aidl/assets/core.rs @@ -0,0 +1,21 @@ +#![crate_name = "aidl_core"] +#![crate_type = "rlib"] +#![no_implicit_prelude] + +extern crate core as rust_core; +extern crate alloc as rust_alloc; + +pub use self::rust_core::{option::Option, result::Result}; +pub use self::rust_alloc::{vec::Vec as Vector, string::String}; + +pub type Nothing = (); + +pub type Byte = u8; +pub type Int = u32; + +#[derive(Debug, Clone, Copy)] +pub struct Version { + pub major: Byte, + pub minor: Byte, + pub patch: Byte +} diff --git a/programs/aidl/assets/libaidl_core.rlib b/programs/aidl/assets/libaidl_core.rlib new file mode 100644 index 0000000..a746cf8 Binary files /dev/null and b/programs/aidl/assets/libaidl_core.rlib differ diff --git a/programs/aidl/assets/libaidl_vfs.rlib b/programs/aidl/assets/libaidl_vfs.rlib new file mode 100644 index 0000000..3bfee97 Binary files /dev/null and b/programs/aidl/assets/libaidl_vfs.rlib differ diff --git a/programs/aidl/assets/moves.idl b/programs/aidl/assets/moves.idl new file mode 100644 index 0000000..c4cc5b9 --- /dev/null +++ b/programs/aidl/assets/moves.idl @@ -0,0 +1,6 @@ +Interface Thing { + Function moves Takes(Move Self) Returns(Self); + + Function immut_ref Takes(Reference Self); + Function mut_ref Takes(Mutable Reference Self); +} diff --git a/programs/aidl/assets/vfs.idl b/programs/aidl/assets/vfs.idl index 10d0a51..80602bf 100644 --- a/programs/aidl/assets/vfs.idl +++ b/programs/aidl/assets/vfs.idl @@ -1,24 +1,26 @@ +Module vfs; // core provides lots of useful types like String and Byte -Use core; +Use core.Version; +Use core.Vector; +Use core.String; Constant VERSION = Make Version { - major: 1, - minor: 0, - patch: 0, + major: 1, + minor: 0, + patch: 0, }; Alias Path = String; Structure File { - name: String, - data: Vector, + name: String, + data: Vector, } Interface File { - Function new Takes(Path) Returns(None); + Function create Takes(Path) Returns(Nothing); - // Open in this iteration assumes the file exists - Function open Takes(Path) Returns(File); + Function open Takes(Path) Returns(File); - Function close Takes(File) Returns(None); + Function close Takes(File) Returns(Nothing); } diff --git a/programs/aidl/assets/vfs.rs b/programs/aidl/assets/vfs.rs new file mode 100644 index 0000000..285f0c5 --- /dev/null +++ b/programs/aidl/assets/vfs.rs @@ -0,0 +1,26 @@ +#![crate_name = "aidl_vfs"] +#![crate_type = "rlib"] +#![no_implicit_prelude] +extern crate aidl_core; + +use aidl_core::{Vector, Version, String}; + +pub const VERSION: Version = Version { + major: 1, + minor: 0, + patch: 0, +}; + +pub type Path = String; + +pub struct FFile { + pub name: String +} + +pub trait File { + fn fields(&self) -> &FFile; + fn fields_mut(&mut self) -> &mut FFile; + fn into_fields(self) -> FFile where Self: Sized; + + fn new(path: Path) -> Self; +} diff --git a/programs/aidl/assets/why.idl b/programs/aidl/assets/why.idl index e39c329..bc0a408 100644 --- a/programs/aidl/assets/why.idl +++ b/programs/aidl/assets/why.idl @@ -5,25 +5,24 @@ Constant Hi = "WHY???/\n"; Alias Yo = Byte; Constant Version = Make Version { - major: 1 - minor: 0 - patch: 0 + major: 1, minor: 0, patch: 0 }; Interface Iface { - Function hello Takes(Int Boolean) Returns(Int); + Function hello Takes(Int, Boolean,) Returns(Int); } Function a_free_function Returns(Boolean); Structure Hello { - world: Boolean prompt: Option + world: Boolean, + prompt: Option, } Enumeration Reality { - Dead(Boolean Boolean), + Dead(Boolean, Boolean), Alive { - health: Int - dying: Boolean - } + health: Int, + dying: Boolean, + }, } diff --git a/programs/aidl/src/ast.rs b/programs/aidl/src/ast.rs index 3ffa60d..e198e2e 100644 --- a/programs/aidl/src/ast.rs +++ b/programs/aidl/src/ast.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; /// - items #[derive(Debug)] pub struct IDLModule { + pub name: String, // why: only allow use before other items // parser will error if use is present in any other place pub uses: Vec, @@ -121,7 +122,7 @@ pub enum EnumerationContent { #[derive(Debug)] pub struct UseDecl { - pub module: ModulePath, + pub path: (String, Option) } #[derive(Debug)] diff --git a/programs/aidl/src/codegen.rs b/programs/aidl/src/codegen.rs index 6baf426..4392027 100644 --- a/programs/aidl/src/codegen.rs +++ b/programs/aidl/src/codegen.rs @@ -1,10 +1,130 @@ -use std::path::Path; +use std::default::default; -use crate::ast::IDLModule; +use crate::{ast::{IDLModule, ItemInterface, TypeArguments, Type}, unwrap_match}; -use proc_macro2::{TokenStream, Ident, Span}; -use quote::quote; +use itertools::Itertools; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{quote, ToTokens}; +use syn::{Attribute, ItemExternCrate, ItemTrait, ItemUse, LitStr, Meta, Path, UsePath, Generics, punctuated::Punctuated, TypeArray, LitInt}; + +fn attr_inner(meta: Meta) -> Attribute { + Attribute { + pound_token: default(), + style: syn::AttrStyle::Inner(default()), + bracket_token: default(), + meta, + } +} + +fn attr_just(name: &'static str) -> Attribute { + attr_inner(Meta::Path(Path::from(Ident::new(name, Span::call_site())))) +} + +fn attr_inner_eq(name: &'static str, expr: &str) -> Attribute { + attr_inner(Meta::NameValue(syn::MetaNameValue { + path: Path::from(Ident::new(name, Span::call_site())), + eq_token: default(), + value: syn::Expr::Lit(syn::ExprLit { + attrs: vec![], + lit: syn::Lit::Str(LitStr::new(expr, Span::call_site())), + }), + })) +} + +fn extern_crate(name: &str) -> ItemExternCrate { + ItemExternCrate { + attrs: vec![], + vis: syn::Visibility::Inherited, + extern_token: default(), + crate_token: default(), + ident: Ident::new(name, Span::call_site()), + rename: None, + semi_token: default(), + } +} + +fn make_use(a: &str, b: &str) -> ItemUse { + ItemUse { + attrs: vec![], + vis: syn::Visibility::Inherited, + use_token: default(), + tree: syn::UseTree::Path(UsePath { + tree: Box::new(syn::UseTree::Name(syn::UseName { + ident: Ident::new(b, Span::call_site()), + })), + ident: Ident::new(a, Span::call_site()), + colon2_token: default(), + }), + semi_token: default(), + leading_colon: None, + } +} + +fn _gen_type(ty: Type) -> syn::Type { + fn make_array(mut args: Vec>) -> TypeArray { + let box arity = args.pop().unwrap(); + let box real = args.pop().unwrap(); + + drop(args); + + TypeArray { bracket_token: default(), elem: Box::new(gen_type(real)), semi_token: default(), len: syn::Expr::Lit(syn::ExprLit { attrs: vec![], lit: syn::Lit::Int(LitInt::new(&arity.name, Span::call_site())) }) } + } + match ty.name.as_str() { + "Array" => syn::Type::Array(make_array(unwrap_match!(ty.arguments, TypeArguments::AngleBracketed(angle) => angle))), + name => syn::Type::Path(syn::TypePath { qself: None, path: Path::from(Ident::new(name, Span::call_site())) }) + } +} + +// fn gen_iface(interface: ItemInterface) -> ItemTrait { +// ItemTrait { +// attrs: default(), +// vis: syn::Visibility::Public(default()), +// unsafety: None, +// auto_token: None, +// restriction: None, +// trait_token: default(), +// ident: Ident::new(&interface.name, Span::call_site()), +// generics: , +// colon_token: (), +// supertraits: (), +// brace_token: (), +// items: (), +// } +// } pub fn generate(module: IDLModule) -> TokenStream { - quote! {} + let name = String::from("aidl_") + &module.name; + let attrs: TokenStream = [ + attr_inner_eq("crate_name", &name), + attr_inner_eq("crate_type", "rlib"), + attr_just("no_implicit_prelude"), + ] + .into_iter() + .map(ToTokens::into_token_stream) + .collect(); + let uses: Vec<_> = module + .uses + .into_iter() + .map(|a| a.path) + .map(|(a, b)| (String::from("aidl_") + &a, b)) // aidl_core.Something + .collect(); + let extern_crates: TokenStream = uses + .iter() + .map(|(a, _)| a.as_str()) + .unique() + .map(extern_crate) + .map(ToTokens::into_token_stream) + .collect(); + let use_defs: TokenStream = uses + .iter() + .filter_map(|(ref a, ref b)| b.as_ref().map(|b| make_use(a.as_str(), b.as_str()))) + .map(ToTokens::into_token_stream) + .collect(); + + quote! { + #attrs + + #extern_crates + #use_defs + } } diff --git a/programs/aidl/src/lexer.rs b/programs/aidl/src/lexer.rs index 1fa8e9e..2d45b80 100644 --- a/programs/aidl/src/lexer.rs +++ b/programs/aidl/src/lexer.rs @@ -83,6 +83,9 @@ pub enum Token { #[derive(Logos, Debug, Clone, PartialEq, Eq, derive_more::Display)] pub enum Ident { + #[token("Module")] + #[display(fmt = "Module")] + Module, #[token("Interface")] #[display(fmt = "Interface")] Interface, diff --git a/programs/aidl/src/main.rs b/programs/aidl/src/main.rs index 0000eaf..765db7d 100644 --- a/programs/aidl/src/main.rs +++ b/programs/aidl/src/main.rs @@ -1,29 +1,30 @@ #![feature(result_option_inspect)] +#![feature(box_patterns)] +#![feature(default_free_fn)] #![allow(non_snake_case)] use std::{fmt::Display, path::Path, process::exit}; +use ast::IDLModule; +use codegen::generate; use codespan_reporting::{ diagnostic::{Diagnostic, Label, Severity}, files::SimpleFile, term::{ emit, - termcolor::{ColorSpec, StandardStream, StandardStreamLock}, - Config, Styles, + termcolor::{StandardStream, StandardStreamLock}, + Config, }, }; use lexer::{NumberSuffix, Token}; -use logos::Logos; use parser::TokenIterator; use crate::lexer::Spanned; mod ast; +mod codegen; mod lexer; mod parser; -mod codegen; - -//const TEST: &str = include_str!("../assets/why.idl"); fn precheck>( writer: &mut StandardStreamLock<'_>, @@ -68,10 +69,10 @@ fn precheck>( Diagnostic::warning() .with_message("Potentially invalid use of an uppercased number type") .with_labels(vec![Label::primary((), span.0)]) - .with_notes(vec![format!( - "Replace {ident} with {}", - ident.to_lowercase() - ), "Code generation might fail".into()]), + .with_notes(vec![ + format!("Replace {ident} with {}", ident.to_lowercase()), + "Code generation might fail".into(), + ]), ); } _ => {} @@ -98,6 +99,9 @@ fn precheck>( fn main() { let mut args = std::env::args(); args.next().unwrap(); + + let mut ast: Option = None; + if let Some(file) = args.next() { let path = Path::new(&file); let codespan_file = codespan_reporting::files::SimpleFile::new( @@ -113,7 +117,10 @@ fn main() { precheck(&mut writer.lock(), &config, &codespan_file); match parser::parse(codespan_file.source()) { - Ok(ast) => println!("{:#?}", ast), + Ok(ast_) => { + println!("{:#?}", ast_); + ast = Some(ast_); + } Err(e) => { let msg = e.to_string(); let label = match e { @@ -145,6 +152,9 @@ fn main() { } else { eprintln!("No file given. Aborting."); } + + let rust = generate(ast.unwrap()); + println!("{}", rust); } #[macro_export] diff --git a/programs/aidl/src/parser/enumeration.rs b/programs/aidl/src/parser/enumeration.rs index c6d728c..65f0c33 100644 --- a/programs/aidl/src/parser/enumeration.rs +++ b/programs/aidl/src/parser/enumeration.rs @@ -41,8 +41,12 @@ impl<'a> Parser<'a> { match self.tokens.peek()?.0 { Token::Ident(Ident::Other(_)) => { tuple.push(self.ask_type()?.0); - if let Token::Comma = self.tokens.peek()?.0 { - self.eat(); + match self.tokens.peek()?.0 { + Token::Comma => { + self.eat(); + } + Token::RightParen => {} + _ => return Err(self.expected("a comma or closing parentheses")), }; } Token::RightParen => { @@ -74,8 +78,12 @@ impl<'a> Parser<'a> { )?; structure.insert(field_name, self.ask_type()?.0); - if let Token::Comma = self.tokens.peek()?.0 { - self.eat(); + match self.tokens.peek()?.0 { + Token::Comma => { + self.eat(); + } + Token::RightCurly => {} + _ => return Err(self.expected("a comma or closing curly braces")), }; } Token::RightCurly => { @@ -101,8 +109,12 @@ impl<'a> Parser<'a> { } } - if let Spanned(Token::Comma, _) = self.tokens.peek()? { - self.eat(); + match self.tokens.peek()?.0 { + Token::Comma => { + self.eat(); + } + Token::RightCurly => {} + _ => return Err(self.expected("a comma or closing curly braces")), } variants.push(EnumerationVariant { diff --git a/programs/aidl/src/parser/expr.rs b/programs/aidl/src/parser/expr.rs index ff2632c..b63bd2c 100644 --- a/programs/aidl/src/parser/expr.rs +++ b/programs/aidl/src/parser/expr.rs @@ -83,9 +83,11 @@ impl<'a> Parser<'a> { self.get_real(|token| matches!(token, Token::Colon), "a colon")?; let Spanned(value, _) = self.ask_expr()?; params.insert(ident, value); - if let Token::Comma = self.tokens.peek()?.0 { - self.eat(); - }; + match self.tokens.peek()?.0 { + Token::Comma => self.eat(), + Token::RightCurly => {}, + _ => return Err(self.expected("a comma or a closing curly brace")) + } } Token::RightCurly => break, _ => return Err(self.expected("an identifier or a closing curly brace (`}`)")), diff --git a/programs/aidl/src/parser/interface.rs b/programs/aidl/src/parser/interface.rs index 651ad18..8f3582d 100644 --- a/programs/aidl/src/parser/interface.rs +++ b/programs/aidl/src/parser/interface.rs @@ -61,8 +61,12 @@ impl<'a> Parser<'a> { match peeked { Token::Ident(_) => { takes.push(self.ask_type()?.0); - if let Token::Comma = self.tokens.peek()?.0 { - self.eat(); + match self.tokens.peek()?.0 { + Token::Comma => { + self.eat(); + } + Token::RightParen => {} + _ => return Err(self.expected("a comma or closing parentheses")), }; } Token::RightParen => { @@ -78,7 +82,7 @@ impl<'a> Parser<'a> { Token::Ident(Ident::Returns) => { self.get_real( |token| matches!(token, Token::LeftParen), - "Opening parentheses", + "opening parentheses", )?; let Spanned(returns_, _) = self.ask_type()?; @@ -87,7 +91,7 @@ impl<'a> Parser<'a> { self.get_real( |token| matches!(token, Token::RightParen), - "Closing parentheses", + "closing parentheses", )?; self.semi()?; diff --git a/programs/aidl/src/parser/mod.rs b/programs/aidl/src/parser/mod.rs index a1f0b0a..373316e 100644 --- a/programs/aidl/src/parser/mod.rs +++ b/programs/aidl/src/parser/mod.rs @@ -7,7 +7,7 @@ mod types; use logos::{Lexer, Logos}; use crate::{ - ast::{IDLModule, Item, ItemAlias, ItemConstant, ModulePath, UseDecl}, + ast::{IDLModule, Item, ItemAlias, ItemConstant, UseDecl}, lexer::{Ident, Span, Spanned, Token}, }; use std::iter::Iterator; @@ -118,41 +118,6 @@ impl<'a> Parser<'a> { ) } - fn ask_modpath( - &mut self, - end: impl Fn(&Token) -> bool, - ) -> Result, ParserError> { - let mut segments = vec![]; - let mut in_path_seg = false; - let mut waiting_next_seg = true; - let mut span = Span::ZERO; - - loop { - match self.tokens.next()? { - Spanned(Token::Ident(Ident::Other(ident)), span_span) - if !in_path_seg && waiting_next_seg => - { - span += span_span; - segments.push(ident); - in_path_seg = true; - waiting_next_seg = false; - } - Spanned(Token::Dot, span_span) if in_path_seg && !waiting_next_seg => { - span += span_span; - waiting_next_seg = true; - in_path_seg = false; - } - v if end(&v.0) && (in_path_seg || !waiting_next_seg) => { - span += v.1; - break; - } - _ => return Err(self.expected("a path segment")), - } - } - - Ok(Spanned(ModulePath { segments }, span)) - } - fn ask_alias(&mut self) -> Result, ParserError> { let Spanned(_, kSp) = self.get_real( |token| matches!(token, Token::Ident(Ident::Alias)), @@ -203,19 +168,33 @@ impl<'a> Parser<'a> { } fn ask_use(&mut self) -> Result, ParserError> { - let Spanned(_, kSp) = { + let Spanned(_, span) = { match self.tokens.peek()? { Spanned(Token::Ident(Ident::Use), _) => Ok(self.tokens.next()?), _ => Err(ParserError::PleaseStopParsingUse), } }?; - let Spanned(module, nSp) = self.ask_modpath(|token| matches!(token, Token::Semicolon))?; + let mut path = (self.ask_ident()?.0, None); - Ok(Spanned::new(UseDecl { module }, [kSp, nSp])) + if let Token::Dot = self.tokens.peek()?.0 { + self.eat(); + path.1 = Some(self.ask_ident()?.0); + } + + Ok(Spanned::new(UseDecl { path }, [span, self.semi()?])) } pub fn parse(mut self) -> Result { Ok(IDLModule { + name: { + self.get_real( + |token| matches!(token, Token::Ident(Ident::Module)), + "the `Module` keyword", + )?; + let name = self.ask_ident()?.0; + self.semi()?; + name + }, uses: { let mut real = vec![]; loop { diff --git a/programs/aidl/src/parser/structure.rs b/programs/aidl/src/parser/structure.rs index 14a82fa..0b36536 100644 --- a/programs/aidl/src/parser/structure.rs +++ b/programs/aidl/src/parser/structure.rs @@ -29,8 +29,12 @@ impl<'a> Parser<'a> { self.get_real(|token| matches!(token, Token::Colon), "a colon")?; let Spanned(value, _) = self.ask_type()?; fields.insert(ident, value); - if let Token::Comma = self.tokens.peek()?.0 { - self.eat(); + match self.tokens.peek()?.0 { + Token::Comma => { + self.eat(); + } + Token::RightCurly => {} + _ => return Err(self.expected("a comma or closing curly braces")), }; } Token::RightCurly => break, diff --git a/programs/aidl/src/parser/types.rs b/programs/aidl/src/parser/types.rs index 5afbefa..da1cf8a 100644 --- a/programs/aidl/src/parser/types.rs +++ b/programs/aidl/src/parser/types.rs @@ -33,14 +33,22 @@ impl<'a> Parser<'a> { args.push(Box::new(self.ask_type()?.0)); - if let Spanned(Token::Comma, _) = self.tokens.peek()? { - self.eat(); // skip comma, allow trailing comma + match self.tokens.peek()?.0 { + Token::Comma => self.eat(), + Token::RightArrow => {} + _ => return Err(self.expected("a comma or closing angle brackets")), }; loop { match self.tokens.peek()? { Spanned(Token::Ident(_) | Token::NumberLiteral(_), _) => { - args.push(Box::new(self.ask_type()?.0)) + args.push(Box::new(self.ask_type()?.0)); + + match self.tokens.peek()?.0 { + Token::Comma => self.eat(), + Token::RightArrow => {} + _ => return Err(self.expected("a comma or closing angle brackets")), + } } Spanned(Token::RightArrow, _) => { self.eat();