This commit is contained in:
nothendev 2023-05-06 18:57:45 +03:00
parent 59aad853a0
commit d97b3e3636
21 changed files with 319 additions and 106 deletions

16
Cargo.lock generated
View file

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

View file

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

View file

@ -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<X> | Vec<X>
Array[X;Y] | [X;Y]
Function X accepts(YX) returns(ZX) | fn X(YX) -> ZX
Array<X, Y> | [X; Y]
Function X Takes(YX) Returns(ZX) | fn X(YX) -> ZX

View file

@ -1,11 +1,5 @@
Alias Byte = U8;
Alias Int = U32;
Alias String = Vector<Byte>;
Enumeration Boolean {
False = 0,
True = 1,
}
Alias Byte = u8;
Alias Int = u32;
Enumeration Nothing {}
@ -14,6 +8,11 @@ Enumeration Option<T> {
Some(T)
}
Enumeration Result<T, E> {
Ok(T),
Err(E)
}
Structure Version {
major: 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
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<Byte>,
name: String,
data: Vector<Byte>,
}
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;
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<String>
world: Boolean,
prompt: Option<String>,
}
Enumeration Reality {
Dead(Boolean Boolean),
Dead(Boolean, Boolean),
Alive {
health: Int
dying: Boolean
}
health: Int,
dying: Boolean,
},
}

View file

@ -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<UseDecl>,
@ -121,7 +122,7 @@ pub enum EnumerationContent {
#[derive(Debug)]
pub struct UseDecl {
pub module: ModulePath,
pub path: (String, Option<String>)
}
#[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 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<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 {
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)]
pub enum Ident {
#[token("Module")]
#[display(fmt = "Module")]
Module,
#[token("Interface")]
#[display(fmt = "Interface")]
Interface,

View file

@ -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<N: Display + Clone, S: AsRef<str>>(
writer: &mut StandardStreamLock<'_>,
@ -68,10 +69,10 @@ fn precheck<N: Display + Clone, S: AsRef<str>>(
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<N: Display + Clone, S: AsRef<str>>(
fn main() {
let mut args = std::env::args();
args.next().unwrap();
let mut ast: Option<IDLModule> = 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]

View file

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

View file

@ -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 (`}`)")),

View file

@ -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()?;

View file

@ -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<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> {
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<Spanned<UseDecl>, 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<IDLModule, ParserError> {
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 {

View file

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

View file

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