Much better working grammar, more fixed tests

This commit is contained in:
2023-08-03 00:53:43 -05:00
parent e8940feea7
commit 65858b18d9
4 changed files with 262 additions and 226 deletions
+73 -47
View File
@@ -80,7 +80,7 @@ pub struct Assignment {
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub enum Lvalue { pub enum Lvalue {
ArrayRef(ArrayRef), ArrayRef(String, Expr),
Name(String), Name(String),
} }
@@ -150,62 +150,78 @@ pub enum Prefix {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Suffix { pub enum Suffix {
Subscript(Expr), Subscript(BoxExpr),
Arglist(Vec<Rvalue>), Arglist(Vec<Rvalue>),
Member(String), Member(String),
} }
#[derive(PartialEq, Clone, Debug)] #[derive(Debug, PartialEq, Clone)]
pub struct Expr { pub enum Expr {
pub lhs: Val, Val(Val),
pub op: Option<Operator>, Prefix(Prefix, BoxExpr),
pub rhs: Option<BoxExpr>, Suffix(BoxExpr, Suffix),
Infix(BoxExpr, Operator, BoxExpr)
} }
// #[derive(PartialEq, Clone, Debug)]
// pub struct Expr {
// pub lhs: Val,
// pub prefix: Vec<Prefix>,
// pub suffix: Vec<Suffix>,
// pub op: Option<Operator>,
// pub rhs: Option<BoxExpr>,
// }
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub enum Val { pub enum Val {
Number(i32, Vec<Prefix>, Vec<Suffix>), Number(i32),
Name(String, Vec<Prefix>, Vec<Suffix>), Name(String),
Expr(BoxExpr, Vec<Prefix>, Vec<Suffix>), Expr(BoxExpr),
} }
impl Val { // impl Val {
pub fn is_simple(&self) -> bool { // pub fn is_simple(&self) -> bool {
if let Self::Expr(expr, pre, suf) = &self { // if let Self::Expr(expr, pre, suf) = &self {
pre.is_empty() && suf.is_empty() && expr.0.op.is_none() // pre.is_empty() && suf.is_empty() && expr.0.op.is_none()
} else { // } else {
false // false
} // }
} // }
//
pub fn inner_expr(self) -> Option<Expr> { // pub fn inner_expr(self) -> Option<Expr> {
if let Self::Expr(expr, _, _) = self { // if let Self::Expr(expr, _, _) = self {
Some(expr.into()) // Some(expr.into())
} else { None } // } else { None }
} // }
} // }
impl From<i32> for Val { impl From<i32> for Val {
fn from(val: i32) -> Self { fn from(val: i32) -> Self {
Self::Number(val, vec![], vec![]) Self::Number(val)
} }
} }
impl From<&str> for Val { impl From<&str> for Val {
fn from(s: &str) -> Self { fn from(s: &str) -> Self {
Self::Name(String::from(s), vec![], vec![]) Self::Name(String::from(s))
} }
} }
impl From<Expr> for Val { impl From<Expr> for Val {
fn from(value: Expr) -> Self { fn from(value: Expr) -> Self {
Self::Expr(value.into(), vec![], vec![]) Self::Expr(value.into())
} }
} }
impl From<BoxExpr> for Val { impl From<BoxExpr> for Val {
fn from(value: BoxExpr) -> Self { fn from(value: BoxExpr) -> Self {
Self::Expr(value, vec![], vec![]) Self::Expr(value)
}
}
impl From<Val> for BoxExpr {
fn from(value: Val) -> Self {
Self::from(Expr::from(value))
} }
} }
@@ -215,11 +231,7 @@ pub struct BoxExpr(pub Box<Expr>);
impl From<Val> for Expr { impl From<Val> for Expr {
fn from(value: Val) -> Self { fn from(value: Val) -> Self {
Self { Self::Val(value)
lhs: value,
op: None,
rhs: None
}
} }
} }
@@ -230,29 +242,43 @@ impl From<i32> for BoxExpr {
} }
impl From<Expr> for BoxExpr { impl From<Expr> for BoxExpr {
fn from(val: Expr) -> Self { fn from(expr: Expr) -> Self {
BoxExpr(Box::from(val)) BoxExpr(Box::from(expr))
}
}
impl From<&str> for BoxExpr {
fn from(value: &str) -> Self {
Expr::Val(Val::Name(String::from(value))).into()
} }
} }
impl From<BoxExpr> for Expr { impl From<BoxExpr> for Expr {
fn from(val: BoxExpr) -> Self { fn from(expr: BoxExpr) -> Self {
*(val.0) *(expr.0)
} }
} }
impl From<i32> for Expr { impl From<i32> for Expr {
fn from(value: i32) -> Self { fn from(value: i32) -> Self {
Self { Self::Val(Val::Number(value))
lhs: Val::Number(value, vec![], vec![]),
op: None,
rhs: None
}
} }
} }
#[derive(PartialEq, Clone, Debug)] impl From<&str> for Expr {
pub struct ArrayRef { fn from(value: &str) -> Self {
pub name: String, Self::Val(Val::Name(String::from(value)))
pub subscript: BoxExpr, }
}
impl From<i32> for Rvalue {
fn from(value: i32) -> Self {
Self::Expr(value.into())
}
}
impl From<&str> for Rvalue {
fn from(value: &str) -> Self {
Self::String(value.into())
}
} }
+1 -1
View File
@@ -311,7 +311,7 @@ impl Compilable for Lvalue {
let global_scope = &state.global_scope; let global_scope = &state.global_scope;
let mut sig = sig.expect("lvalue outside a function"); let mut sig = sig.expect("lvalue outside a function");
match self { match self {
Lvalue::ArrayRef(_) => todo!("Arrays are not implemented yet"), Lvalue::ArrayRef(_, _) => todo!("Arrays are not implemented yet"),
Lvalue::Name(name) => { Lvalue::Name(name) => {
if let Some(var) = lookup(&name, global_scope, &sig.local_scope) { if let Some(var) = lookup(&name, global_scope, &sig.local_scope) {
match var { match var {
+185 -175
View File
@@ -1,4 +1,4 @@
use pest::pratt_parser::PrattParser; use pest::pratt_parser::{Op, PrattParser};
use pest::Parser; use pest::Parser;
#[derive(Parser)] #[derive(Parser)]
@@ -23,6 +23,7 @@ lazy_static::lazy_static! {
.op(Op::infix(add, Left) | Op::infix(sub, Left)) .op(Op::infix(add, Left) | Op::infix(sub, Left))
.op(Op::infix(mul, Left) | Op::infix(div, Left) | Op::infix(modulus, Left)) .op(Op::infix(mul, Left) | Op::infix(div, Left) | Op::infix(modulus, Left))
.op(Op::prefix(Rule::prefix)) .op(Op::prefix(Rule::prefix))
.op(Op::postfix(Rule::suffix))
}; };
} }
@@ -324,12 +325,13 @@ impl AstNode for Assignment {
const RULE: Rule = Rule::assignment; const RULE: Rule = Rule::assignment;
fn from_pair(pair: Pair) -> Self { fn from_pair(pair: Pair) -> Self {
let mut pairs = pair.into_inner(); let mut pairs = pair.into_inner();
let lvalue_pair = pairs.next().unwrap().first(); let lvalue_pair = pairs.next().unwrap();
let lvalue = match lvalue_pair.as_rule() { let lvalue = Lvalue::from_pair(lvalue_pair);
//Rule::arrayref => Lvalue::ArrayRef(ArrayRef::from_pair(lvalue_pair)), // let lvalue = match lvalue_pair.as_rule() {
Rule::name => Lvalue::Name(String::from(lvalue_pair.as_str())), // Rule::subscript => Lvalue::ArrayRef(Subscript::from_pair(lvalue_pair)),
_ => unreachable!(), // Rule::name => Lvalue::Name(String::from(lvalue_pair.as_str())),
}; // _ => unreachable!(),
// };
let rvalue = Rvalue::from_pair(pairs.next().unwrap()); let rvalue = Rvalue::from_pair(pairs.next().unwrap());
Self { lvalue, rvalue } Self { lvalue, rvalue }
} }
@@ -337,6 +339,18 @@ impl AstNode for Assignment {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
impl AstNode for Lvalue {
const RULE: Rule = Rule::lvalue;
fn from_pair(pair: Pair) -> Self {
let mut pairs = pair.into_inner();
let name = pairs.next().unwrap().as_str();
pairs.next().map_or(Lvalue::Name(String::from(name)),
|subscript| Lvalue::ArrayRef(String::from(name), Expr::from_pair(subscript.first())))
}
}
///////////////////////////////////////////////////////////////////////////////////////////
impl AstNode for Rvalue { impl AstNode for Rvalue {
const RULE: Rule = Rule::rvalue; const RULE: Rule = Rule::rvalue;
@@ -428,20 +442,35 @@ impl AstNode for RepeatLoop {
impl AstNode for Expr { impl AstNode for Expr {
const RULE: Rule = Rule::expr; const RULE: Rule = Rule::expr;
fn from_pair(pair: Pair) -> Self { fn from_pair(pair: Pair) -> Self {
// The way this works is, the rule has to be of the form:
// expr = { prefix* ~ val ~ suffix* ~ (operator ~ prefix* ~ val ~ suffix*)* }
// Each of these map methods turns a thing into an expr. Which means expr HAS
// to be an enum with the different possible forms these things can take:
// - if it's a val, it goes into map_primary and returns an Expr::Val
// - If it's a prefix or suffix, it goes into map_prefix or map_postfix, and
// returns an Expr::Prefix or Expr::Suffix
// - Operators go into map_infix along with two exprs for the left and right
// sides
// The output of all this is an Expr, containing a tree of other Exprs of
// various forms.
PRATT_PARSER PRATT_PARSER
.map_primary(|val| { .map_primary(|val| {
Expr { let val = Val::from_pair(val);
lhs: Val::from_pair(val), // If the val is a parenthesized expr, just unwrap it
op: None, if let Val::Expr(expr) = val {
rhs: None *expr.0
} } else {
Expr::Val(val)
}
}) })
.map_infix(|lhs, op, rhs| .map_infix(|lhs, op, rhs|
Expr { Expr::Infix(lhs.into(), Operator::from_pair(op), rhs.into())
lhs: Val::from(lhs), )
op: Some(Operator::from_pair(op)), .map_prefix(|prefix, expr|
rhs: Some(rhs.into()), Expr::Prefix(Prefix::from_pair(prefix), expr.into())
}) )
.map_postfix(|expr, suffix|
Expr::Suffix(expr.into(), Suffix::from_pair(suffix)))
.parse(pair.into_inner()) .parse(pair.into_inner())
} }
} }
@@ -503,7 +532,7 @@ impl AstNode for Suffix {
fn from_pair(pair: Pair) -> Self { fn from_pair(pair: Pair) -> Self {
let first = pair.first(); let first = pair.first();
match first.as_rule() { match first.as_rule() {
Rule::subscript => Subscript(Expr::from_pair(first.first())), Rule::subscript => Subscript(Expr::from_pair(first.first()).into()),
Rule::member => Self::Member(first.first_as_string()), Rule::member => Self::Member(first.first_as_string()),
Rule::arglist => Arglist(first.into_inner().map(Rvalue::from_pair).collect()), Rule::arglist => Arglist(first.into_inner().map(Rvalue::from_pair).collect()),
_ => unreachable!() _ => unreachable!()
@@ -533,23 +562,11 @@ impl AstNode for Val {
const RULE: Rule = Rule::val; const RULE: Rule = Rule::val;
fn from_pair(pair: Pair) -> Self { fn from_pair(pair: Pair) -> Self {
let mut suffix = vec![]; let val = pair.first();
let mut prefix = vec![];
let mut val = None;
for child in pair.into_inner() {
match child.as_rule() {
Rule::prefix => prefix.push(Prefix::from_pair(child)),
Rule::suffix => suffix.push(Suffix::from_pair(child)),
Rule::number | Rule::name | Rule::expr => val = Some(child),
_ => unreachable!()
}
}
let val = val.unwrap();
match val.as_rule() { match val.as_rule() {
Rule::number => Self::Number(val.into_number(), prefix, suffix), Rule::number => Self::Number(val.into_number()),
Rule::name => Self::Name(String::from(val.as_str()), prefix, suffix), Rule::name => Self::Name(String::from(val.as_str())),
Rule::expr => Self::Expr(Expr::from_pair(val).into(), prefix, suffix), Rule::expr => Self::Expr(Expr::from_pair(val).into()),
_ => unreachable!() _ => unreachable!()
} }
} }
@@ -565,23 +582,7 @@ mod test {
fn parse_vals() { fn parse_vals() {
// Basic vals with no extras: // Basic vals with no extras:
assert_eq!(Val::from_str("10"), Ok(10.into())); assert_eq!(Val::from_str("10"), Ok(10.into()));
assert_eq!(Val::from_str("blah"), Ok(Val::Name("blah".into(), vec![], vec![]))); assert_eq!(Val::from_str("blah"), Ok(Val::Name("blah".into())));
// Simple prefix
assert_eq!(Val::from_str("-5"),
Ok(Val::Number(5, vec![Prefix::Neg], vec![])));
// Multiple prefixes
assert_eq!(Val::from_str("!&foo"),
Ok(Val::Name("foo".into(), vec![Prefix::Not, Prefix::Address], vec![])));
// Simple suffix
assert_eq!(Val::from_str("foo[10]"),
Ok(Val::Name("foo".into(), vec![], vec![Suffix::Subscript(10.into())])));
// Multi-suffix
assert_eq!(Val::from_str("foo[10].bar"),
Ok(Val::Name("foo".into(), vec![], vec![Suffix::Subscript(10.into()), Suffix::Member("bar".into())])));
} }
#[test] #[test]
@@ -642,7 +643,7 @@ mod test {
Const::from_str("const a = -7;"), Const::from_str("const a = -7;"),
Ok(Const { Ok(Const {
name: "a".into(), name: "a".into(),
value: Some(Val::Number(7, vec![Prefix::Neg], vec![]).into()), value: Some(Expr::Prefix(Prefix::Neg, 7.into()).into()),
string: None, string: None,
}) })
); );
@@ -755,108 +756,127 @@ mod test {
}], }],
body: Block(vec![]), body: Block(vec![]),
}; };
assert_eq!(
Function::from_str("fn foo<inline, org=0x400>(a) {}"),
Ok(func.clone())
);
assert_eq!(
Function::from_str("fn foo<org=0x400, inline>(a) {}"),
Ok(func)
);
} }
#[test] #[test]
fn parse_exprs() { fn parse_exprs() {
use Operator::*; use Operator::*;
use Prefix::*;
use Suffix::*;
use Expr::Infix;
// A very, very basic expression // A very, very basic expression
// For the rest of these we'll .into() stuff for brevity assert_eq!(
//Some(assert_eq!(Expr::from_str("23"), Ok(Expr { lhs: Val::Number(23, vec![], vec![]), op: None, rhs: None }))); Expr::from_str("23"),
Ok(Expr::Val(Val::Number(23)))
);
// Two vals with an operator // Two vals with an operator
assert_eq!( assert_eq!(
Expr::from_str("23 + 5"), Expr::from_str("23 + 5"),
Ok(Expr { lhs: 23.into(), op: Some(Add), rhs: Some(5.into()) }) Ok(Infix(23.into(), Add, 5.into()))
); );
// Multiple terms at the same precedence level // Multiple terms at the same precedence level
assert_eq!( assert_eq!(
Expr::from_str("1 + 2 + 3"), Expr::from_str("1 + 2 + 3"),
Ok(Expr { lhs: 1.into(), op: Some(Add), rhs: Some(Expr { lhs: 2.into(), op: Some(Add), rhs: Some(3.into()) }.into()) }) Ok(Infix(Infix(1.into(), Add, 2.into()).into(), Add, 3.into()))
); );
/* // Simple prefix
// Higher precedence levels assert_eq!(Expr::from_str("-5"),
assert_eq!( Ok(Expr::Prefix(Neg, 5.into())));
Node::from_str("1 + 2 * 3"),
Ok(Expr(1.into(), Add, Expr(2.into(), Mul, 3.into()).into()))
);
assert_eq!(Node::from_str("2 * 3"), Ok(Expr(2.into(), Mul, 3.into()))); // Multiple prefixes
assert_eq!(
Expr::from_str("!&foo"),
Ok(Expr::Prefix(Not,
Expr::Prefix(Address, "foo".into()).into())));
assert_eq!( // Simple suffix
Node::from_str("2 * 3 + 4"), assert_eq!(
Ok(Expr(Expr(2.into(), Mul, 3.into()).into(), Add, 4.into())) Expr::from_str("foo[10]"),
); Ok(Expr::Suffix("foo".into(), Subscript(10.into())))
);
// Various operators // Multi-suffix
assert_eq!( assert_eq!(
Node::from_str("1 || 2 && 3"), Expr::from_str("foo[10].bar"),
Ok(Expr(1.into(), Or, Expr(2.into(), And, 3.into()).into())) Ok(Expr::Suffix(
); Expr::Suffix("foo".into(), Subscript(10.into())).into(),
Member("bar".into())))
);
assert_eq!( // Higher precedence levels
Node::from_str("2 && &blah"), assert_eq!(
Ok(Expr(2.into(), And, Address("blah".into()).into())) Expr::from_str("1 + 2 * 3"),
); Ok(Infix(1.into(), Add, Infix(2.into(), Mul, 3.into()).into()))
);
assert_eq!( assert_eq!(
Node::from_str("2 & &blah"), Expr::from_str("2 * 3"),
Ok(Expr(2.into(), BitAnd, Address("blah".into()).into())) Ok(Infix(2.into(), Mul, 3.into()))
); );
assert_eq!( assert_eq!(
Node::from_str("1 | 2 ^ 3"), Expr::from_str("2 * 3 + 4"),
Ok(Expr(1.into(), BitOr, Expr(2.into(), Xor, 3.into()).into())) Ok(Infix(Infix(2.into(), Mul, 3.into()).into(), Add, 4.into()))
); );
assert_eq!( // Various operators
Node::from_str("x == y > z"), assert_eq!(
Ok(Expr( Expr::from_str("1 || 2 && 3"),
Name("x".into()).into(), Ok(Infix(1.into(), Or, Infix(2.into(), And, 3.into()).into()))
Eq, );
Expr(Name("y".into()).into(), Gt, Name("z".into()).into()).into()
))
);
assert_eq!( assert_eq!(
Node::from_str("1 << 6"), Expr::from_str("2 && &blah"),
Ok(Expr(1.into(), Lshift, 6.into())) Ok(Infix(2.into(), And, Expr::Prefix(Address, "blah".into()).into()))
); );
assert_eq!( assert_eq!(
Node::from_str("!a - -3"), Expr::from_str("2 & &blah"),
Ok(Expr( Ok(Infix(2.into(), BitAnd, Expr::Prefix(Address, "blah".into()).into()))
Prefix(crate::ast::Prefix::Not, Name("a".into()).into()).into(), );
Sub,
Prefix(crate::ast::Prefix::Neg, 3.into()).into()
))
);
assert_eq!( assert_eq!(
Node::from_str("-(4 * 5)"), Expr::from_str("1 | 2 ^ 3"),
Ok(Prefix( Ok(Infix(1.into(), BitOr, Infix(2.into(), Xor, 3.into()).into()))
crate::ast::Prefix::Neg, );
Expr(4.into(), Mul, 5.into()).into()
))
);
// Parens assert_eq!(
assert_eq!( Expr::from_str("x == y > z"),
Node::from_str("(1 + 2) * 3"), Ok(Infix("x".into(), Eq, Infix("y".into(), Gt, "z".into()).into()))
Ok(Expr(Expr(1.into(), Add, 2.into()).into(), Mul, 3.into())) );
);
*/ assert_eq!(
Expr::from_str("1 << 6"),
Ok(Infix(1.into(), Lshift, 6.into()))
);
assert_eq!(
Expr::from_str("!a - -3"),
Ok(Infix(Expr::Prefix(Not, "a".into()).into(), Sub, Expr::Prefix(Neg, 3.into()).into()))
);
assert_eq!(
Expr::from_str("-(4 * 5)"),
Ok(Expr::Prefix(Neg,
Infix(4.into(), Mul, 5.into()).into()
))
);
// Parens
assert_eq!(
Expr::from_str("(1 + 2) * 3"),
Ok(
Infix(
Infix(1.into(), Add, 2.into()).into(),
Mul,
3.into()
)
)
);
} }
#[test] #[test]
@@ -894,55 +914,48 @@ mod test {
} }
#[test] #[test]
fn parse_addresses() { fn parse_calls() {
// assert_eq!(Node::from_str("&foo"), Ok(Node::Address("foo".into()))); use Val::Number;
}
// #[test] let blah = Expr::Suffix(
// fn parse_calls() { "blah".into(),
// use Val::Number; Arglist(vec![])
// );
// let blah = Call {
// name: "blah".into(), // Can Node parse a call?
// args: vec![], assert_eq!(Expr::from_str("blah()"), Ok(blah.clone()));
// };
// // Can Statement parse a call?
// // Can Node parse a call? assert_eq!(Statement::from_str("blah();"), Ok(Statement::Expr(blah)));
// assert_eq!(Node::from_str("blah()"), Ok(Node::Call(blah.clone())));
// // Calls with args
// // Can Statement parse a call? assert_eq!(
// assert_eq!(Statement::from_str("blah();"), Ok(Statement::Call(blah))); Expr::from_str("blah(1, 2)"),
// Ok(Expr::Suffix(
// // Calls with args "blah".into(),
// // assert_eq!( Arglist(vec![1.into(), 2.into()])))
// // Call::from_str("blah(1, 2)"), );
// // Ok(Call {
// // name: "blah".into(), //Calls with strings
// // args: vec![Rvalue::Expr(Number(1)), Rvalue::Expr(Number(2))] assert_eq!(
// // }) Expr::from_str("blah(\"foo\", 2)"),
// // ); Ok(Expr::Suffix(
// "blah".into(),
// // Calls with strings Arglist(vec!["foo".into(), 2.into()])))
// // assert_eq!( );
// // Call::from_str("blah(\"foo\", 2)"), }
// // Ok(Call {
// // name: "blah".into(),
// // args: vec![Rvalue::String("foo".into()), Rvalue::Expr(Number(2))]
// // })
// // );
// }
#[test] #[test]
fn parse_return() { fn parse_return() {
// assert_eq!( assert_eq!(
// Statement::from_str("return;"), Statement::from_str("return;"),
// Ok(Statement::Return(Return(None))) Ok(Statement::Return(Return(None)))
// ); );
//
// assert_eq!( assert_eq!(
// Statement::from_str("return 17;"), Statement::from_str("return 17;"),
// Ok(Statement::Return(Return(Some(Node::Number(17))))) Ok(Statement::Return(Return(Some(17.into()))))
// ); );
} }
#[test] #[test]
@@ -958,10 +971,7 @@ mod test {
assert_eq!( assert_eq!(
Assignment::from_str("foo[45] = 7"), Assignment::from_str("foo[45] = 7"),
Ok(Assignment { Ok(Assignment {
lvalue: Lvalue::ArrayRef(ArrayRef { lvalue: Lvalue::ArrayRef("foo".into(), 45.into()),
name: "foo".into(),
subscript: 45.into(),
}),
rvalue: Rvalue::Expr(7.into()), rvalue: Rvalue::Expr(7.into()),
}) })
); );
+3 -3
View File
@@ -64,15 +64,15 @@ operator = _{
| ne | ne
} }
expr = { val ~ (operator ~ val)* } expr = { prefix* ~ val ~ suffix* ~ (operator ~ prefix* ~ val ~ suffix*)* }
val = { prefix* ~ (number | name | "(" ~ expr ~ ")") ~ suffix* } val = { number | name | "(" ~ expr ~ ")" }
suffix = { subscript | arglist | member } suffix = { subscript | arglist | member }
subscript = { "[" ~ expr ~ "]" } subscript = { "[" ~ expr ~ "]" }
arglist = { "(" ~ (rvalue ~ ("," ~ rvalue)*)? ~ ")" } arglist = { "(" ~ (rvalue ~ ("," ~ rvalue)*)? ~ ")" }
member = { "." ~ name } member = { "." ~ name }
statement = { asm | ((return_stmt | assignment | expr | var_decl) ~ ";") | conditional | while_loop | repeat_loop } statement = { asm | ((return_stmt | var_decl | assignment | expr) ~ ";") | conditional | while_loop | repeat_loop }
asm = { "asm" ~ asm_args? ~ "{" ~ asm_body ~ "}" } asm = { "asm" ~ asm_args? ~ "{" ~ asm_body ~ "}" }
asm_args = { "(" ~ expr ~ ("," ~ expr)* ~ ")" } asm_args = { "(" ~ expr ~ ("," ~ expr)* ~ ")" }