master
nothendev 2023-05-06 18:57:45 +03:00
parent 1207c14b47
commit f376bc6b30
21 changed files with 319 additions and 106 deletions

16
Cargo.lock generated
View File

@ -37,6 +37,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"codespan-reporting", "codespan-reporting",
"derive_more", "derive_more",
"itertools",
"logos", "logos",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -174,6 +175,12 @@ dependencies = [
"syn 1.0.105", "syn 1.0.105",
] ]
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -229,6 +236,15 @@ dependencies = [
"hashbrown 0.12.3", "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]] [[package]]
name = "libc" name = "libc"
version = "0.2.138" version = "0.2.138"

View File

@ -8,6 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
codespan-reporting = "0.11.1" codespan-reporting = "0.11.1"
derive_more = "0.99.17" derive_more = "0.99.17"
itertools = "0.10.5"
logos = "0" logos = "0"
proc-macro2 = "1.0.56" proc-macro2 = "1.0.56"
quote = "1.0.26" quote = "1.0.26"

View File

@ -4,7 +4,7 @@ The example implementation will be in rust
IDL | Rust IDL | Rust
__________ __________
boolean | bool Boolean | bool
I8 | i8 I8 | i8
I16 | i16 I16 | i16
I32 | i32 I32 | i32
@ -15,8 +15,8 @@ U32 | u32
U64 | u64 U64 | u64
F32 | f32 F32 | f32
F64 | f64 F64 | f64
Constant X Y Z | const X: Y = Z; Constant X = Make Y { Z } | const X: Y = Y { Z };
Type | type Alias | type
Vector<X> | Vec<X> Vector<X> | Vec<X>
Array[X;Y] | [X;Y] Array<X, Y> | [X; Y]
Function X accepts(YX) returns(ZX) | fn X(YX) -> ZX Function X Takes(YX) Returns(ZX) | fn X(YX) -> ZX

View File

@ -1,11 +1,5 @@
Alias Byte = U8; Alias Byte = u8;
Alias Int = U32; Alias Int = u32;
Alias String = Vector<Byte>;
Enumeration Boolean {
False = 0,
True = 1,
}
Enumeration Nothing {} Enumeration Nothing {}
@ -14,6 +8,11 @@ Enumeration Option<T> {
Some(T) Some(T)
} }
Enumeration Result<T, E> {
Ok(T),
Err(E)
}
Structure Version { Structure Version {
major: Byte, major: Byte,
minor: Byte, minor: Byte,

View File

@ -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
}

Binary file not shown.

Binary file not shown.

View File

@ -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);
}

View File

@ -1,24 +1,26 @@
Module vfs;
// core provides lots of useful types like String and Byte // 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 { Constant VERSION = Make Version {
major: 1, major: 1,
minor: 0, minor: 0,
patch: 0, patch: 0,
}; };
Alias Path = String; Alias Path = String;
Structure File { Structure File {
name: String, name: String,
data: Vector<Byte>, data: Vector<Byte>,
} }
Interface File { 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);
} }

View File

@ -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;
}

View File

@ -5,25 +5,24 @@ Constant Hi = "WHY???/\n";
Alias Yo = Byte; Alias Yo = Byte;
Constant Version = Make Version { Constant Version = Make Version {
major: 1 major: 1, minor: 0, patch: 0
minor: 0
patch: 0
}; };
Interface Iface { Interface Iface {
Function hello Takes(Int Boolean) Returns(Int); Function hello Takes(Int, Boolean,) Returns(Int);
} }
Function a_free_function Returns(Boolean); Function a_free_function Returns(Boolean);
Structure Hello { Structure Hello {
world: Boolean prompt: Option<String> world: Boolean,
prompt: Option<String>,
} }
Enumeration Reality { Enumeration Reality {
Dead(Boolean Boolean), Dead(Boolean, Boolean),
Alive { Alive {
health: Int health: Int,
dying: Boolean dying: Boolean,
} },
} }

View File

@ -9,6 +9,7 @@ use std::collections::HashMap;
/// - items /// - items
#[derive(Debug)] #[derive(Debug)]
pub struct IDLModule { pub struct IDLModule {
pub name: String,
// why: only allow use before other items // why: only allow use before other items
// parser will error if use is present in any other place // parser will error if use is present in any other place
pub uses: Vec<UseDecl>, pub uses: Vec<UseDecl>,
@ -121,7 +122,7 @@ pub enum EnumerationContent {
#[derive(Debug)] #[derive(Debug)]
pub struct UseDecl { pub struct UseDecl {
pub module: ModulePath, pub path: (String, Option<String>)
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -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 itertools::Itertools;
use quote::quote; 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<Box<Type>>) -> 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 { 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
}
} }

View File

@ -83,6 +83,9 @@ pub enum Token {
#[derive(Logos, Debug, Clone, PartialEq, Eq, derive_more::Display)] #[derive(Logos, Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum Ident { pub enum Ident {
#[token("Module")]
#[display(fmt = "Module")]
Module,
#[token("Interface")] #[token("Interface")]
#[display(fmt = "Interface")] #[display(fmt = "Interface")]
Interface, Interface,

View File

@ -1,29 +1,30 @@
#![feature(result_option_inspect)] #![feature(result_option_inspect)]
#![feature(box_patterns)]
#![feature(default_free_fn)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
use std::{fmt::Display, path::Path, process::exit}; use std::{fmt::Display, path::Path, process::exit};
use ast::IDLModule;
use codegen::generate;
use codespan_reporting::{ use codespan_reporting::{
diagnostic::{Diagnostic, Label, Severity}, diagnostic::{Diagnostic, Label, Severity},
files::SimpleFile, files::SimpleFile,
term::{ term::{
emit, emit,
termcolor::{ColorSpec, StandardStream, StandardStreamLock}, termcolor::{StandardStream, StandardStreamLock},
Config, Styles, Config,
}, },
}; };
use lexer::{NumberSuffix, Token}; use lexer::{NumberSuffix, Token};
use logos::Logos;
use parser::TokenIterator; use parser::TokenIterator;
use crate::lexer::Spanned; use crate::lexer::Spanned;
mod ast; mod ast;
mod codegen;
mod lexer; mod lexer;
mod parser; mod parser;
mod codegen;
//const TEST: &str = include_str!("../assets/why.idl");
fn precheck<N: Display + Clone, S: AsRef<str>>( fn precheck<N: Display + Clone, S: AsRef<str>>(
writer: &mut StandardStreamLock<'_>, writer: &mut StandardStreamLock<'_>,
@ -68,10 +69,10 @@ fn precheck<N: Display + Clone, S: AsRef<str>>(
Diagnostic::warning() Diagnostic::warning()
.with_message("Potentially invalid use of an uppercased number type") .with_message("Potentially invalid use of an uppercased number type")
.with_labels(vec![Label::primary((), span.0)]) .with_labels(vec![Label::primary((), span.0)])
.with_notes(vec![format!( .with_notes(vec![
"Replace {ident} with {}", format!("Replace {ident} with {}", ident.to_lowercase()),
ident.to_lowercase() "Code generation might fail".into(),
), "Code generation might fail".into()]), ]),
); );
} }
_ => {} _ => {}
@ -98,6 +99,9 @@ fn precheck<N: Display + Clone, S: AsRef<str>>(
fn main() { fn main() {
let mut args = std::env::args(); let mut args = std::env::args();
args.next().unwrap(); args.next().unwrap();
let mut ast: Option<IDLModule> = None;
if let Some(file) = args.next() { if let Some(file) = args.next() {
let path = Path::new(&file); let path = Path::new(&file);
let codespan_file = codespan_reporting::files::SimpleFile::new( let codespan_file = codespan_reporting::files::SimpleFile::new(
@ -113,7 +117,10 @@ fn main() {
precheck(&mut writer.lock(), &config, &codespan_file); precheck(&mut writer.lock(), &config, &codespan_file);
match parser::parse(codespan_file.source()) { match parser::parse(codespan_file.source()) {
Ok(ast) => println!("{:#?}", ast), Ok(ast_) => {
println!("{:#?}", ast_);
ast = Some(ast_);
}
Err(e) => { Err(e) => {
let msg = e.to_string(); let msg = e.to_string();
let label = match e { let label = match e {
@ -145,6 +152,9 @@ fn main() {
} else { } else {
eprintln!("No file given. Aborting."); eprintln!("No file given. Aborting.");
} }
let rust = generate(ast.unwrap());
println!("{}", rust);
} }
#[macro_export] #[macro_export]

View File

@ -41,8 +41,12 @@ impl<'a> Parser<'a> {
match self.tokens.peek()?.0 { match self.tokens.peek()?.0 {
Token::Ident(Ident::Other(_)) => { Token::Ident(Ident::Other(_)) => {
tuple.push(self.ask_type()?.0); tuple.push(self.ask_type()?.0);
if let Token::Comma = self.tokens.peek()?.0 { match self.tokens.peek()?.0 {
self.eat(); Token::Comma => {
self.eat();
}
Token::RightParen => {}
_ => return Err(self.expected("a comma or closing parentheses")),
}; };
} }
Token::RightParen => { Token::RightParen => {
@ -74,8 +78,12 @@ impl<'a> Parser<'a> {
)?; )?;
structure.insert(field_name, self.ask_type()?.0); structure.insert(field_name, self.ask_type()?.0);
if let Token::Comma = self.tokens.peek()?.0 { match self.tokens.peek()?.0 {
self.eat(); Token::Comma => {
self.eat();
}
Token::RightCurly => {}
_ => return Err(self.expected("a comma or closing curly braces")),
}; };
} }
Token::RightCurly => { Token::RightCurly => {
@ -101,8 +109,12 @@ impl<'a> Parser<'a> {
} }
} }
if let Spanned(Token::Comma, _) = self.tokens.peek()? { match self.tokens.peek()?.0 {
self.eat(); Token::Comma => {
self.eat();
}
Token::RightCurly => {}
_ => return Err(self.expected("a comma or closing curly braces")),
} }
variants.push(EnumerationVariant { variants.push(EnumerationVariant {

View File

@ -83,9 +83,11 @@ impl<'a> Parser<'a> {
self.get_real(|token| matches!(token, Token::Colon), "a colon")?; self.get_real(|token| matches!(token, Token::Colon), "a colon")?;
let Spanned(value, _) = self.ask_expr()?; let Spanned(value, _) = self.ask_expr()?;
params.insert(ident, value); params.insert(ident, value);
if let Token::Comma = self.tokens.peek()?.0 { match self.tokens.peek()?.0 {
self.eat(); Token::Comma => self.eat(),
}; Token::RightCurly => {},
_ => return Err(self.expected("a comma or a closing curly brace"))
}
} }
Token::RightCurly => break, Token::RightCurly => break,
_ => return Err(self.expected("an identifier or a closing curly brace (`}`)")), _ => return Err(self.expected("an identifier or a closing curly brace (`}`)")),

View File

@ -61,8 +61,12 @@ impl<'a> Parser<'a> {
match peeked { match peeked {
Token::Ident(_) => { Token::Ident(_) => {
takes.push(self.ask_type()?.0); takes.push(self.ask_type()?.0);
if let Token::Comma = self.tokens.peek()?.0 { match self.tokens.peek()?.0 {
self.eat(); Token::Comma => {
self.eat();
}
Token::RightParen => {}
_ => return Err(self.expected("a comma or closing parentheses")),
}; };
} }
Token::RightParen => { Token::RightParen => {
@ -78,7 +82,7 @@ impl<'a> Parser<'a> {
Token::Ident(Ident::Returns) => { Token::Ident(Ident::Returns) => {
self.get_real( self.get_real(
|token| matches!(token, Token::LeftParen), |token| matches!(token, Token::LeftParen),
"Opening parentheses", "opening parentheses",
)?; )?;
let Spanned(returns_, _) = self.ask_type()?; let Spanned(returns_, _) = self.ask_type()?;
@ -87,7 +91,7 @@ impl<'a> Parser<'a> {
self.get_real( self.get_real(
|token| matches!(token, Token::RightParen), |token| matches!(token, Token::RightParen),
"Closing parentheses", "closing parentheses",
)?; )?;
self.semi()?; self.semi()?;

View File

@ -7,7 +7,7 @@ mod types;
use logos::{Lexer, Logos}; use logos::{Lexer, Logos};
use crate::{ use crate::{
ast::{IDLModule, Item, ItemAlias, ItemConstant, ModulePath, UseDecl}, ast::{IDLModule, Item, ItemAlias, ItemConstant, UseDecl},
lexer::{Ident, Span, Spanned, Token}, lexer::{Ident, Span, Spanned, Token},
}; };
use std::iter::Iterator; use std::iter::Iterator;
@ -118,41 +118,6 @@ impl<'a> Parser<'a> {
) )
} }
fn ask_modpath(
&mut self,
end: impl Fn(&Token) -> bool,
) -> Result<Spanned<ModulePath>, 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<Spanned<ItemAlias>, ParserError> { fn ask_alias(&mut self) -> Result<Spanned<ItemAlias>, ParserError> {
let Spanned(_, kSp) = self.get_real( let Spanned(_, kSp) = self.get_real(
|token| matches!(token, Token::Ident(Ident::Alias)), |token| matches!(token, Token::Ident(Ident::Alias)),
@ -203,19 +168,33 @@ impl<'a> Parser<'a> {
} }
fn ask_use(&mut self) -> Result<Spanned<UseDecl>, ParserError> { fn ask_use(&mut self) -> Result<Spanned<UseDecl>, ParserError> {
let Spanned(_, kSp) = { let Spanned(_, span) = {
match self.tokens.peek()? { match self.tokens.peek()? {
Spanned(Token::Ident(Ident::Use), _) => Ok(self.tokens.next()?), Spanned(Token::Ident(Ident::Use), _) => Ok(self.tokens.next()?),
_ => Err(ParserError::PleaseStopParsingUse), _ => 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<IDLModule, ParserError> { pub fn parse(mut self) -> Result<IDLModule, ParserError> {
Ok(IDLModule { 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: { uses: {
let mut real = vec![]; let mut real = vec![];
loop { loop {

View File

@ -29,8 +29,12 @@ impl<'a> Parser<'a> {
self.get_real(|token| matches!(token, Token::Colon), "a colon")?; self.get_real(|token| matches!(token, Token::Colon), "a colon")?;
let Spanned(value, _) = self.ask_type()?; let Spanned(value, _) = self.ask_type()?;
fields.insert(ident, value); fields.insert(ident, value);
if let Token::Comma = self.tokens.peek()?.0 { match self.tokens.peek()?.0 {
self.eat(); Token::Comma => {
self.eat();
}
Token::RightCurly => {}
_ => return Err(self.expected("a comma or closing curly braces")),
}; };
} }
Token::RightCurly => break, Token::RightCurly => break,

View File

@ -33,14 +33,22 @@ impl<'a> Parser<'a> {
args.push(Box::new(self.ask_type()?.0)); args.push(Box::new(self.ask_type()?.0));
if let Spanned(Token::Comma, _) = self.tokens.peek()? { match self.tokens.peek()?.0 {
self.eat(); // skip comma, allow trailing comma Token::Comma => self.eat(),
Token::RightArrow => {}
_ => return Err(self.expected("a comma or closing angle brackets")),
}; };
loop { loop {
match self.tokens.peek()? { match self.tokens.peek()? {
Spanned(Token::Ident(_) | Token::NumberLiteral(_), _) => { 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, _) => { Spanned(Token::RightArrow, _) => {
self.eat(); self.eat();