how does Able envision codegen

master
nothendev 2023-05-05 17:15:38 +03:00
parent 3d87e7693c
commit 2226b0d2f7
12 changed files with 243 additions and 44 deletions

View File

@ -7,5 +7,9 @@ edition = "2021"
[dependencies]
codespan-reporting = "0.11.1"
derive_more = "0.99.17"
logos = "0"
proc-macro2 = "1.0.56"
quote = "1.0.26"
syn = "2.0.15"
thiserror = "1"

View File

@ -15,7 +15,7 @@ Enumeration Option<T> {
}
Structure Version {
major: Byte,
minor: Byte,
patch: Byte,
major: Byte,
minor: Byte,
patch: Byte,
}

View File

@ -0,0 +1,19 @@
Type UUID = Array<U8, 16>;
Type Nanoseconds = U32;
Structure Duration {
secs: U64
nanos: Nanoseconds
}
Structure LinkedList<T> {
data: T,
child: Option<LinkedList<T>>,
}
Constant VERSION = Version {
major: 1,
minor: 0,
patch: 0
};

View File

@ -1,13 +0,0 @@
Type UUID = Array[U8; 16];
Type Nanoseconds = U32;
Structure Duration{
secs: U64,
nanos: Nanoseconds,
}
Structure LinkedList{
data: Any,
child: Option<LinkedList>,
}

View File

@ -1,11 +1,11 @@
// core provides lots of useful types like String and Byte
Use core;
Constant VERSION = Version {
Constant VERSION = Make Version {
major: 1,
minor: 0,
patch: 0,
}
};
Alias Path = String;
@ -15,10 +15,10 @@ Structure File {
}
Interface File {
function new accepts(Path) returns(None);
Function new Takes(Path) Returns(None);
// Open in this iteration assumes the file exists
function open accepts(Path) returns(File);
Function open Takes(Path) Returns(File);
function close accepts(File) returns(None);
Function close Takes(File) Returns(None);
}

View File

@ -22,7 +22,7 @@ pub enum Item {
Constant(ItemConstant),
Function(Function),
Structure(ItemStructure),
Enumeration(ItemEnumeration)
Enumeration(ItemEnumeration),
}
#[derive(Debug, Default)]
@ -42,7 +42,7 @@ impl Type {
pub fn infer() -> Self {
Self {
name: String::from(INFER_TYPE),
arguments: TypeArguments::None
arguments: TypeArguments::None,
}
}
}
@ -82,6 +82,7 @@ pub struct ItemInterface {
pub struct ItemStructure {
pub name: String,
pub fields: HashMap<String, Type>,
pub arguments: TypeArguments,
}
#[derive(Debug)]
@ -100,13 +101,13 @@ pub struct ItemConstant {
pub struct ItemEnumeration {
pub name: String,
pub arguments: TypeArguments,
pub variants: Vec<EnumerationVariant>
pub variants: Vec<EnumerationVariant>,
}
#[derive(Debug)]
pub struct EnumerationVariant {
pub name: String,
pub content: EnumerationContent
pub content: EnumerationContent,
}
#[derive(Debug, Default)]
@ -115,7 +116,7 @@ pub enum EnumerationContent {
None,
Tuple(Vec<Type>),
Structure(HashMap<String, Type>),
Value(NumberLiteral)
Value(NumberLiteral),
}
#[derive(Debug)]
@ -143,22 +144,32 @@ pub enum Literal {
Char(char),
}
#[derive(Debug)]
#[derive(Debug, derive_more::Display)]
pub enum NumberLiteral {
#[display(fmt = "{_0}ptr")]
Ptr(usize),
#[display(fmt = "{_0}u8")]
U8(u8),
#[display(fmt = "{_0}i8")]
I8(i8),
#[display(fmt = "{_0}u16")]
U16(u16),
#[display(fmt = "{_0}i16")]
I16(i16),
#[display(fmt = "{_0}u32")]
U32(u32),
#[display(fmt = "{_0}i32")]
I32(i32),
#[display(fmt = "{_0}u64")]
U64(u64),
#[display(fmt = "{_0}i64")]
I64(i64),
#[display(fmt = "{_0}")]
Infer(i64),
}

View File

@ -0,0 +1,10 @@
use std::path::Path;
use crate::ast::IDLModule;
use proc_macro2::{TokenStream, Ident, Span};
use quote::quote;
pub fn generate(module: IDLModule) -> TokenStream {
quote! {}
}

View File

@ -5,119 +5,175 @@ use std::{
use logos::Logos;
#[derive(Logos, Debug, PartialEq)]
#[derive(Logos, Debug, PartialEq, derive_more::Display, Clone)]
#[logos(skip r"[ \t\n\f]+")]
pub enum Token {
#[token("{")]
#[display(fmt = "{{")]
LeftCurly,
#[token("}")]
#[display(fmt = "}}")]
RightCurly,
#[token("(")]
#[display(fmt = "(")]
LeftParen,
#[token(")")]
#[display(fmt = ")")]
RightParen,
#[token(";")]
#[display(fmt = ";")]
Semicolon,
#[token(":")]
#[display(fmt = ":")]
Colon,
#[token("<")]
#[display(fmt = "<")]
LeftArrow,
#[token(">")]
#[display(fmt = ">")]
RightArrow,
#[token(",")]
#[display(fmt = ",")]
Comma,
#[token("=")]
#[display(fmt = "=")]
Equals,
#[token(".")]
#[display(fmt = ".")]
Dot,
// why
#[regex("\"(?s:[^\"\\\\]|\\\\.)*\"", |lex| lex.slice().strip_prefix('"')?.strip_suffix('"').map(ToOwned::to_owned))]
#[display(fmt = "\"{_0}\"")]
StringLiteral(String),
#[regex(r"'.'", |lex| lex.slice().strip_prefix('\'')?.strip_suffix('\'')?.parse().ok())]
#[display(fmt = "{_0}")]
CharLiteral(char),
#[regex(r#"(-)?\d+"#, |lex| lex.slice().parse().ok())]
#[display(fmt = "{_0}")]
NumberLiteral(i64),
#[regex(
r"(ptr|u8|i8|u16|i16|u32|i32|u64|i64|f32|f64)",
"(ptr|u8|i8|u16|i16|u32|i32|u64|i64|f32|f64)",
|lex| NumberSuffix::lexer(lex.slice()).next().and_then(Result::ok)
)]
#[display(fmt = "{_0}")]
NumberSuffix(NumberSuffix),
#[regex(r#"[a-zA-Z_][a-zA-Z\d_]*"#, |lex| Ident::lexer(lex.slice()).next().and_then(Result::ok))]
#[display(fmt = "{_0}")]
Ident(Ident),
#[regex(r"//.*", |lex| lex.slice().parse().ok())]
#[display(fmt = "//{_0}")]
Comment(String),
}
#[derive(Logos, Debug, PartialEq, Eq)]
#[derive(Logos, Debug, Clone, PartialEq, Eq, derive_more::Display)]
pub enum Ident {
#[token("Interface")]
#[display(fmt = "Interface")]
Interface,
#[token("Function")]
#[display(fmt = "Function")]
Function,
#[token("Constant")]
#[display(fmt = "Constant")]
Constant,
#[token("Structure")]
#[display(fmt = "Structure")]
Structure,
#[token("Alias")]
#[display(fmt = "Alias")]
Alias,
#[token("Enumeration")]
#[display(fmt = "Enumeration")]
Enumeration,
#[token("Use")]
#[display(fmt = "Use")]
Use,
#[token("Make")]
#[display(fmt = "Make")]
Make,
#[token("Takes")]
#[display(fmt = "Takes")]
Takes,
#[token("Returns")]
#[display(fmt = "Returns")]
Returns,
#[token("_")]
#[display(fmt = "_")]
Underscore,
#[regex(r"[a-zA-Z_][a-zA-Z\d_]*", |lex| lex.slice().parse().ok())]
#[display(fmt = "{_0}")]
Other(String),
}
#[derive(Logos, Debug, PartialEq, Eq)]
#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
pub enum NumberSuffix {
#[token("Ptr")]
#[token("ptr")]
#[display(fmt = "ptr")]
Ptr,
#[token("u8")]
#[display(fmt = "u8")]
U8,
#[token("i8")]
#[display(fmt = "i8")]
I8,
#[token("u16")]
#[display(fmt = "u16")]
U16,
#[token("i16")]
#[display(fmt = "i16")]
I16,
#[token("u32")]
#[display(fmt = "u32")]
U32,
#[token("i32")]
#[display(fmt = "i32")]
I32,
#[token("u64")]
#[display(fmt = "u64")]
U64,
#[token("i64")]
#[display(fmt = "i64")]
I64,
#[token("f32")]
#[display(fmt = "f32")]
F32,
#[token("f64")]
#[display(fmt = "f64")]
F64,
}
impl NumberSuffix {
pub const ALL_SUFFIXES: [&str; 11] = [
"ptr",
"u8",
"i8",
"u16",
"i16",
"u32",
"i32",
"u64",
"i64",
"f32",
"f64"
];
}
#[derive(Debug, Clone)]
pub struct Span(pub Range<usize>);
impl Span {

View File

@ -1,12 +1,20 @@
#![feature(result_option_inspect)]
#![allow(non_snake_case)]
use std::path::Path;
use std::{fmt::Display, path::Path, process::exit};
use codespan_reporting::{
diagnostic::Label,
term::{termcolor::StandardStream, Config},
diagnostic::{Diagnostic, Label, Severity},
files::SimpleFile,
term::{
emit,
termcolor::{ColorSpec, StandardStream, StandardStreamLock},
Config, Styles,
},
};
use lexer::{NumberSuffix, Token};
use logos::Logos;
use parser::TokenIterator;
use crate::lexer::Spanned;
@ -16,20 +24,95 @@ mod parser;
//const TEST: &str = include_str!("../assets/why.idl");
fn precheck<N: Display + Clone, S: AsRef<str>>(
writer: &mut StandardStreamLock<'_>,
config: &Config,
file: &SimpleFile<N, S>,
) {
let mut lexer = TokenIterator::new(file.source().as_ref());
let mut diagnostics = vec![];
let mut previous = lexer.peek().ok().map(|Spanned(a, b)| Spanned(a.clone(), b));
while let Ok(Spanned(token, span)) = lexer.next() {
let prev = Spanned(token.clone(), span.clone());
match token {
Token::Ident(lexer::Ident::Other(t)) if t == "Type" => {
diagnostics.push(
Diagnostic::error()
.with_labels(vec![Label::primary((), span.0)])
.with_message("`Type` is not supported anymore.")
.with_notes(vec!["use `Alias` instead of `Type`".into()]),
);
}
Token::Ident(lexer::Ident::Other(ident))
if lexer
.peek()
.is_ok_and(|Spanned(a, _)| matches!(a, Token::LeftCurly))
&& previous.is_some_and(|Spanned(ref a, _)| matches!(a, Token::Equals)) =>
{
diagnostics.push(
Diagnostic::error()
.with_message("Unknown expression")
.with_labels(vec![Label::primary((), span.0.clone())])
.with_notes(vec![
format!("add `Make` before the structure name to create a Make expression that will construct the `{ident}` structure"
),
]),
);
}
Token::Ident(lexer::Ident::Other(ident))
if NumberSuffix::ALL_SUFFIXES.contains(&ident.to_lowercase().as_str()) =>
{
diagnostics.push(
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()]),
);
}
_ => {}
}
previous = Some(prev);
}
if !diagnostics.is_empty() {
let mut was_fatal = false;
for diagnostic in diagnostics {
if let Severity::Error | Severity::Bug = &diagnostic.severity {
was_fatal = true;
}
emit(writer, config, file, &diagnostic).unwrap();
}
if was_fatal {
exit(1);
}
}
}
fn main() {
let mut args = std::env::args();
args.next().unwrap();
if let Some(file) = args.next() {
let path = Path::new(&file);
dbg!(path);
let codespan_file = codespan_reporting::files::SimpleFile::new(
&file,
std::fs::read_to_string(path).unwrap(),
);
let writer = StandardStream::stdout(codespan_reporting::term::termcolor::ColorChoice::Auto);
let config = Config::default();
let config = Config {
tab_width: 2,
..Default::default()
};
precheck(&mut writer.lock(), &config, &codespan_file);
match parser::parse(codespan_file.source()) {
Ok(ast) => println!("SUCCESS: \n{:#?}", ast),
Ok(ast) => println!("{:#?}", ast),
Err(e) => {
let msg = e.to_string();
let label = match e {

View File

@ -12,7 +12,7 @@ use crate::{
};
use std::iter::Iterator;
struct TokenIterator<'a> {
pub struct TokenIterator<'a> {
lexer: Lexer<'a, Token>,
peeked: Option<Option<Token>>,
}
@ -102,11 +102,17 @@ impl<'a> Parser<'a> {
fn ask_ident(&mut self) -> Result<Spanned<String>, ParserError> {
Ok(
match self.get_real(
|token| matches!(token, Token::Ident(Ident::Other(_) | Ident::Underscore)),
|token| {
matches!(
token,
Token::Ident(Ident::Other(_) | Ident::Underscore) | Token::NumberSuffix(_)
)
},
"an identifier",
)? {
Spanned(Token::Ident(Ident::Other(ident)), span) => Spanned(ident, span),
Spanned(Token::Ident(Ident::Underscore), span) => Spanned("_".to_owned(), span),
Spanned(Token::NumberSuffix(suffix), span) => Spanned(suffix.to_string(), span),
_ => unreachable!(),
},
)

View File

@ -1,6 +1,9 @@
use std::collections::HashMap;
use crate::{lexer::{Ident, Spanned, Token}, ast::{Type, ItemStructure}};
use crate::{
ast::{ItemStructure, Type},
lexer::{Ident, Spanned, Token},
};
use super::{Parser, ParserError};
@ -11,7 +14,7 @@ impl<'a> Parser<'a> {
"the `Structure` keyword",
)?;
let Spanned(name, _) = self.ask_ident()?;
let Spanned(Type { name, arguments }, _) = self.ask_type()?;
let Spanned(_, _) = self.get_real(
|token| matches!(token, Token::LeftCurly),
"an opening curly brace (`{`)",
@ -36,7 +39,14 @@ impl<'a> Parser<'a> {
}
if let Spanned(Token::RightCurly, end) = self.tokens.next()? {
return Ok(Spanned(ItemStructure { name, fields }, span + end));
return Ok(Spanned(
ItemStructure {
name,
fields,
arguments,
},
span + end,
));
};
Err(self.expected("closing curly braces"))

View File

@ -7,6 +7,17 @@ use super::{Parser, ParserError};
impl<'a> Parser<'a> {
pub fn ask_type(&mut self) -> Result<Spanned<Type>, ParserError> {
if let Spanned(Token::NumberLiteral(_), _) = self.tokens.peek()? {
let Spanned(number, span) = self._ask_number_literal()?;
return Ok(Spanned(
Type {
name: number.to_string(),
arguments: TypeArguments::None,
},
span,
));
};
let Spanned(name, span) = self.ask_ident()?;
if name == INFER_TYPE {
@ -28,7 +39,9 @@ impl<'a> Parser<'a> {
loop {
match self.tokens.peek()? {
Spanned(Token::Ident(_), _) => args.push(Box::new(self.ask_type()?.0)),
Spanned(Token::Ident(_) | Token::NumberLiteral(_), _) => {
args.push(Box::new(self.ask_type()?.0))
}
Spanned(Token::RightArrow, _) => {
self.eat();
break;