Merge pull request #20 from mist-go/attributes

Attributes
This commit is contained in:
2026-05-05 15:11:22 +02:00
committed by GitHub
6 changed files with 253 additions and 54 deletions
+1
View File
@@ -8,6 +8,7 @@ target
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
*.exe
# Generated by cargo mutants
# Contains mutation testing data
+32 -11
View File
@@ -17,10 +17,22 @@ pub enum TypePostfix {
RefMut,
}
#[derive(Debug, Clone, Serialize)]
pub enum Attribute {
/// #[test]
Path(Path),
/// #[name = "value"]
NameValue { path: Path, value: Literal },
/// #[derive(Clone, Copy)]
List { path: Path, items: Vec<Attribute> },
}
#[derive(Debug, Clone, Serialize)]
pub enum TypeExprKind {
Path(StaticPath),
PathParams(StaticPath, Vec<TypeExpr>),
Path(Path),
PathParams(Path, Vec<TypeExpr>),
Tuple(Vec<TypeExpr>),
}
@@ -28,7 +40,7 @@ pub enum TypeExprKind {
pub struct TypeExpr(pub TypeExprKind, pub Vec<TypePostfix>);
#[derive(Debug, Clone, Serialize)]
pub struct StaticPath(pub Vec<String>);
pub struct Path(pub Vec<String>);
#[derive(Debug, Clone, Serialize)]
pub enum BinaryOp {
@@ -46,8 +58,12 @@ pub enum BinaryOp {
}
#[derive(Debug, Clone, Serialize)]
pub enum TopLevel {
Include(StaticPath),
pub struct TopLevel(pub TopLevelKind, pub Vec<Attribute>);
#[derive(Debug, Clone, Serialize)]
pub enum TopLevelKind {
ModAttribute,
Include(Path),
StructDecl {
export: bool,
name: String,
@@ -137,15 +153,20 @@ pub struct ForStmt {
#[derive(Debug, Clone, Serialize)]
pub enum Expression {
Path(StaticPath),
IntLiteral(i64),
FloatLiteral(f64),
BoolLiteral(bool),
StringLiteral(String),
TupleLiteral(Vec<Expression>),
Literal(Literal),
Path(Path),
Fix {
initial: Box<Expression>,
prefixes: Vec<Prefix>,
postfixes: Vec<Postfix>,
},
}
#[derive(Debug, Clone, Serialize)]
pub enum Literal {
String(String),
Int(i64),
Float(f64),
Bool(bool),
Tuple(Vec<Expression>),
}
+22 -4
View File
@@ -19,7 +19,25 @@ var_decl = { type_expr ~ mutable? ~ identifier }
// ================= TOP LEVEL =================
top_level = _{ import | function_decl | struct_decl }
attribute = {
"#" ~ "[" ~ meta ~ "]"
}
mod_attribute = {
"#" ~ "!" ~ "[" ~ meta ~ "]"
}
meta = {
static_path ~ ("=" ~ primary | "(" ~ meta_list? ~ ")")?
}
meta_list = {
meta ~ ("," ~ meta)* ~ ","?
}
attributes = { attribute* }
top_level = { (attributes ~ (import | function_decl | struct_decl)) | mod_attribute }
import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" }
@@ -90,8 +108,8 @@ macro_inner_v = {
tuple = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" }
primary = {
integer
| float
float
| integer
| boolean
| string_lit
| static_path
@@ -110,7 +128,7 @@ postfix = {
field_px = { "." ~ identifier }
call_px = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" }
macro_call_px = { "!" ~ "(" ~ macro_inner_v* ~ ")" }
struct_px = { "{" ~ (struct_field_decl ~ ("," ~ struct_field_decl)*)? ~ "}" }
struct_px = { "{" ~ (struct_field_decl ~ ("," ~ struct_field_decl)*)? ~ ","? ~ "}" }
index_px = { "[" ~ expr ~ "]" }
binary_px = { bin_op ~ expr }
bin_op = { "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" }
+121 -23
View File
@@ -18,8 +18,8 @@ pub fn parse(source: &str) -> Result<Vec<TopLevel>, ParseError> {
let mut statements = vec![];
for pair in pairs.next().unwrap().into_inner() {
if let Ok(stmt) = TopLevel::try_from(pair) {
statements.push(stmt);
if pair.as_rule() != Rule::EOI {
statements.push(TopLevel::from(pair));
}
}
@@ -67,7 +67,7 @@ impl From<pest::iterators::Pair<'_, Rule>> for TypeExprKind {
match rule {
Rule::tuple_type => TypeExprKind::Tuple(inner.map(TypeExpr::from).collect()),
Rule::path_type => {
let path = StaticPath::from(inner.next().unwrap());
let path = Path::from(inner.next().unwrap());
let params = inner.map(TypeExpr::from).collect::<Vec<_>>();
if params.len() == 0 {
@@ -81,12 +81,10 @@ impl From<pest::iterators::Pair<'_, Rule>> for TypeExprKind {
}
}
impl From<pest::iterators::Pair<'_, Rule>> for StaticPath {
impl From<pest::iterators::Pair<'_, Rule>> for Path {
fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self {
match pair.as_rule() {
Rule::static_path => {
StaticPath(pair.into_inner().map(|i| i.as_str().to_string()).collect())
}
Rule::static_path => Path(pair.into_inner().map(|i| i.as_str().to_string()).collect()),
_ => unimplemented!("{pair:#?}"),
}
}
@@ -120,14 +118,91 @@ impl From<pest::iterators::Pair<'_, Rule>> for ParamList {
}
}
impl TryFrom<pest::iterators::Pair<'_, Rule>> for TopLevel {
type Error = ();
fn try_from(pair: pest::iterators::Pair<Rule>) -> Result<Self, ()> {
impl From<pest::iterators::Pair<'_, Rule>> for Attribute {
fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self {
match pair.as_rule() {
Rule::attribute => {
// unwrap #[ ... ]
Attribute::from(pair.into_inner().next().unwrap())
}
Rule::meta => {
let mut inner = pair.into_inner();
// first item is always the path
let path = Path::from(inner.next().unwrap());
// check what comes next
match inner.next() {
None => {
// #[path]
Attribute::Path(path)
}
Some(next) => match next.as_rule() {
Rule::primary => {
// #[path = literal]
Attribute::NameValue {
path,
value: Literal::from(next),
}
}
Rule::meta_list => {
// #[path(...)]
let items = next.into_inner().map(Attribute::from).collect();
Attribute::List { path, items }
}
_ => unreachable!("unexpected rule in meta: {:?}", next.as_rule()),
},
}
}
Rule::meta_list => {
// This case usually won't be hit directly,
// but it's nice to keep it safe if reused
let items = pair.into_inner().map(Attribute::from).collect::<Vec<_>>();
// NOTE: this shouldn't normally construct an Attribute alone
// but you can panic or wrap depending on your design
panic!("meta_list should be handled inside meta: {:?}", items);
}
_ => unreachable!("unexpected rule: {:?}", pair.as_rule()),
}
}
}
impl From<pest::iterators::Pair<'_, Rule>> for TopLevel {
fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self {
let mut inner = pair.into_inner();
let attributes = inner
.next()
.unwrap()
.into_inner()
.map(Attribute::from)
.collect::<Vec<_>>();
TopLevel(
inner
.next()
.map(TopLevelKind::from)
.unwrap_or(TopLevelKind::ModAttribute),
attributes,
)
}
}
impl From<pest::iterators::Pair<'_, Rule>> for TopLevelKind {
fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self {
let rule = pair.as_rule();
let mut inner = pair.into_inner();
match rule {
Rule::import => Ok(TopLevel::Include(StaticPath::from(inner.next().unwrap()))),
Rule::import => TopLevelKind::Include(Path::from(inner.next().unwrap())),
Rule::function_decl => {
let export = if let Some(first) = inner.peek() {
@@ -152,13 +227,13 @@ impl TryFrom<pest::iterators::Pair<'_, Rule>> for TopLevel {
let body = Block::from(inner.next().unwrap());
Ok(TopLevel::FunctionDecl {
TopLevelKind::FunctionDecl {
export,
name,
params,
return_type,
body,
})
}
}
Rule::struct_decl => {
@@ -176,14 +251,12 @@ impl TryFrom<pest::iterators::Pair<'_, Rule>> for TopLevel {
let fields_pair = inner.next().unwrap();
let fields = FieldList::from(fields_pair);
Ok(TopLevel::StructDecl {
TopLevelKind::StructDecl {
export,
name,
fields,
})
}
}
Rule::EOI => Err(()),
_ => unimplemented!("{rule:#?}"),
}
}
@@ -262,6 +335,23 @@ impl From<pest::iterators::Pair<'_, Rule>> for Statement {
}
}
impl From<pest::iterators::Pair<'_, Rule>> for Literal {
fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self {
let rule = pair.as_rule();
let mut inner = pair.clone().into_inner();
match rule {
Rule::primary => Self::from(inner.next().unwrap()),
Rule::integer => Literal::Int(pair.as_str().parse::<i64>().unwrap()),
Rule::float => Literal::Float(pair.as_str().parse::<f64>().unwrap()),
Rule::boolean => Literal::Bool(pair.as_str().parse::<bool>().unwrap()),
Rule::string_lit => Literal::String(inner.as_str().to_string()),
Rule::tuple => Literal::Tuple(inner.map(Expression::from).collect()),
_ => unimplemented!("{rule:#?}"),
}
}
}
impl From<pest::iterators::Pair<'_, Rule>> for Expression {
fn from(pair: pest::iterators::Pair<Rule>) -> Self {
let rule = pair.as_rule();
@@ -292,12 +382,20 @@ impl From<pest::iterators::Pair<'_, Rule>> for Expression {
}
}
Rule::primary => Expression::from(inner.next().unwrap()),
Rule::static_path => Expression::Path(StaticPath::from(pair)),
Rule::integer => Expression::IntLiteral(pair.as_str().parse::<i64>().unwrap()),
Rule::float => Expression::FloatLiteral(pair.as_str().parse::<f64>().unwrap()),
Rule::boolean => Expression::BoolLiteral(pair.as_str().parse::<bool>().unwrap()),
Rule::string_lit => Expression::StringLiteral(inner.as_str().to_string()),
Rule::tuple => Expression::TupleLiteral(inner.map(Expression::from).collect()),
Rule::static_path => Expression::Path(Path::from(pair)),
Rule::integer => {
Expression::Literal(Literal::Int(pair.as_str().parse::<i64>().unwrap()))
}
Rule::float => {
Expression::Literal(Literal::Float(pair.as_str().parse::<f64>().unwrap()))
}
Rule::boolean => {
Expression::Literal(Literal::Bool(pair.as_str().parse::<bool>().unwrap()))
}
Rule::string_lit => Expression::Literal(Literal::String(inner.as_str().to_string())),
Rule::tuple => {
Expression::Literal(Literal::Tuple(inner.map(Expression::from).collect()))
}
_ => unimplemented!("{rule:#?}"),
}
}
+65 -14
View File
@@ -1,6 +1,7 @@
use parser::ast::{
BinaryOp, Block, Expression, IfStmt, Postfix, Prefix, Statement, StaticPath, TopLevel,
TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, WhileStmt,
Attribute, BinaryOp, Block, Expression, IfStmt, Literal, Path, Postfix, Prefix, Statement,
TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl,
VarDeclStmt, WhileStmt,
};
// ---------------------------------------------------------------------------
@@ -105,15 +106,14 @@ impl GetRust for TypeExprKind {
}
}
impl GetRust for Expression {
impl GetRust for Literal {
fn get_rust(&self) -> String {
match self {
Expression::Path(path) => path.get_rust(),
Expression::IntLiteral(n) => n.to_string(),
Expression::FloatLiteral(n) => n.to_string(),
Expression::BoolLiteral(b) => b.to_string(),
Expression::StringLiteral(s) => format!("\"{s}\""),
Expression::TupleLiteral(t) => {
Self::Int(n) => n.to_string(),
Self::Float(n) => format!("{n:?}"),
Self::Bool(b) => b.to_string(),
Self::String(s) => format!("\"{s}\""),
Self::Tuple(t) => {
format!(
"({})",
t.iter()
@@ -122,6 +122,15 @@ impl GetRust for Expression {
.join(", ")
)
}
}
}
}
impl GetRust for Expression {
fn get_rust(&self) -> String {
match self {
Expression::Path(path) => path.get_rust(),
Expression::Literal(literal) => literal.get_rust(),
Expression::Fix {
initial,
prefixes,
@@ -219,12 +228,54 @@ impl ToRust for Block {
impl ToRust for TopLevel {
fn to_rust(&self, cg: &mut RustCodegen) {
match &self.0 {
TopLevelKind::ModAttribute => {
for attr in &self.1 {
cg.addln(&format!("#![{}]", attr.get_rust()));
}
}
_ => {
for attr in &self.1 {
cg.addln(&format!("#[{}]", attr.get_rust()));
}
}
}
self.0.to_rust(cg);
}
}
impl GetRust for Attribute {
fn get_rust(&self) -> String {
match self {
TopLevel::Include(path) => {
Self::Path(path) => path.get_rust(),
Self::NameValue { path, value } => {
format!("{} = {}", path.get_rust(), value.get_rust())
}
Self::List { path, items } => {
format!(
"{}({})",
path.get_rust(),
items
.iter()
.map(Attribute::get_rust)
.collect::<Vec<_>>()
.join(", ")
)
}
}
}
}
impl ToRust for TopLevelKind {
fn to_rust(&self, cg: &mut RustCodegen) {
match self {
Self::ModAttribute => {}
Self::Include(path) => {
cg.addln(&format!("use {};", path.get_rust()));
}
TopLevel::StructDecl {
Self::StructDecl {
export,
name,
fields,
@@ -242,7 +293,7 @@ impl ToRust for TopLevel {
cg.addln("}\n");
}
TopLevel::FunctionDecl {
Self::FunctionDecl {
export,
name,
params,
@@ -359,7 +410,7 @@ impl GetRust for VarDecl {
}
}
impl GetRust for StaticPath {
impl GetRust for Path {
fn get_rust(&self) -> String {
self.0.join("::")
}
@@ -374,7 +425,7 @@ impl GetRust for TypePostfix {
}
}
pub fn get_static_type_path(path: &StaticPath) -> String {
pub fn get_static_type_path(path: &Path) -> String {
let rust_path = path.get_rust();
if rust_path == "void" {
+12 -2
View File
@@ -1,3 +1,13 @@
#![allow(unused_mut)]
#[derive(Debug)]
public struct ApiCall {
i32 x,
i32 y,
}
void main() {
(i32, i32, str*) my_tuple = (10, 20, "hello, world");
}
ApiCall call = ApiCall { x: 10, y: 12 };
println!("{call:?}");
}