New grammar, sort of working
This commit is contained in:
+93
-43
@@ -12,14 +12,14 @@ pub enum Declaration {
|
||||
#[derive(PartialEq, Clone, Debug, Default)]
|
||||
pub struct Varinfo {
|
||||
pub typename: Option<String>,
|
||||
pub size: Option<Node>,
|
||||
pub size: Option<Expr>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Global {
|
||||
pub name: String,
|
||||
pub typename: Option<String>,
|
||||
pub size: Option<Node>,
|
||||
pub size: Option<Expr>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
@@ -32,13 +32,13 @@ pub struct Struct {
|
||||
pub struct Member {
|
||||
pub name: String,
|
||||
pub typename: Option<String>,
|
||||
pub size: Option<Node>,
|
||||
pub size: Option<Expr>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Const {
|
||||
pub name: String,
|
||||
pub value: Option<Node>,
|
||||
pub value: Option<Expr>,
|
||||
pub string: Option<String>,
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ pub struct Block(pub Vec<Statement>);
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub org: Option<i32>,
|
||||
pub typename: Option<String>,
|
||||
pub inline: bool,
|
||||
pub args: Vec<Argname>,
|
||||
pub body: Block,
|
||||
}
|
||||
@@ -65,7 +62,7 @@ pub struct Argname {
|
||||
pub enum Statement {
|
||||
Return(Return),
|
||||
Assignment(Assignment),
|
||||
Call(Call),
|
||||
Expr(Expr),
|
||||
VarDecl(VarDecl),
|
||||
Conditional(Conditional),
|
||||
WhileLoop(WhileLoop),
|
||||
@@ -73,7 +70,7 @@ pub enum Statement {
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Return(pub Option<Node>);
|
||||
pub struct Return(pub Option<Expr>);
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Assignment {
|
||||
@@ -81,12 +78,6 @@ pub struct Assignment {
|
||||
pub rvalue: Rvalue,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Call {
|
||||
pub name: String,
|
||||
pub args: Vec<Rvalue>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum Lvalue {
|
||||
ArrayRef(ArrayRef),
|
||||
@@ -95,7 +86,7 @@ pub enum Lvalue {
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum Rvalue {
|
||||
Expr(Node),
|
||||
Expr(Expr),
|
||||
String(String),
|
||||
}
|
||||
|
||||
@@ -103,26 +94,26 @@ pub enum Rvalue {
|
||||
pub struct VarDecl {
|
||||
pub name: String,
|
||||
pub typename: Option<String>,
|
||||
pub size: Option<Node>,
|
||||
pub initial: Option<Node>,
|
||||
pub size: Option<Expr>,
|
||||
pub initial: Option<Expr>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Conditional {
|
||||
pub condition: Node,
|
||||
pub condition: Expr,
|
||||
pub body: Block,
|
||||
pub alternative: Option<Block>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct WhileLoop {
|
||||
pub condition: Node,
|
||||
pub condition: Expr,
|
||||
pub body: Block,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct RepeatLoop {
|
||||
pub count: Node,
|
||||
pub count: Expr,
|
||||
pub name: Option<String>,
|
||||
pub body: Block,
|
||||
}
|
||||
@@ -154,55 +145,114 @@ pub enum Operator {
|
||||
pub enum Prefix {
|
||||
Neg,
|
||||
Not,
|
||||
Address
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Suffix {
|
||||
Subscript(Expr),
|
||||
Arglist(Vec<Rvalue>),
|
||||
Member(String),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum Node {
|
||||
Number(i32),
|
||||
Call(Call),
|
||||
ArrayRef(ArrayRef),
|
||||
Name(String),
|
||||
Address(String),
|
||||
Expr(BoxNode, Operator, BoxNode),
|
||||
Prefix(Prefix, BoxNode),
|
||||
pub struct Expr {
|
||||
pub lhs: Val,
|
||||
pub op: Option<Operator>,
|
||||
pub rhs: Option<BoxExpr>,
|
||||
}
|
||||
|
||||
impl From<i32> for Node {
|
||||
fn from(val: i32) -> Self {
|
||||
Self::Number(val)
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum Val {
|
||||
Number(i32, Vec<Prefix>, Vec<Suffix>),
|
||||
Name(String, Vec<Prefix>, Vec<Suffix>),
|
||||
Expr(BoxExpr, Vec<Prefix>, Vec<Suffix>),
|
||||
}
|
||||
|
||||
impl Val {
|
||||
pub fn is_simple(&self) -> bool {
|
||||
if let Self::Expr(expr, pre, suf) = &self {
|
||||
pre.is_empty() && suf.is_empty() && expr.0.op.is_none()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner_expr(self) -> Option<Expr> {
|
||||
if let Self::Expr(expr, _, _) = self {
|
||||
Some(expr.into())
|
||||
} else { None }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Node {
|
||||
impl From<i32> for Val {
|
||||
fn from(val: i32) -> Self {
|
||||
Self::Number(val, vec![], vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Val {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::Name(String::from(s))
|
||||
Self::Name(String::from(s), vec![], vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expr> for Val {
|
||||
fn from(value: Expr) -> Self {
|
||||
Self::Expr(value.into(), vec![], vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoxExpr> for Val {
|
||||
fn from(value: BoxExpr) -> Self {
|
||||
Self::Expr(value, vec![], vec![])
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct BoxNode(pub Box<Node>);
|
||||
pub struct BoxExpr(pub Box<Expr>);
|
||||
|
||||
impl From<i32> for BoxNode {
|
||||
impl From<Val> for Expr {
|
||||
fn from(value: Val) -> Self {
|
||||
Self {
|
||||
lhs: value,
|
||||
op: None,
|
||||
rhs: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for BoxExpr {
|
||||
fn from(val: i32) -> Self {
|
||||
BoxNode(Box::from(Node::Number(val)))
|
||||
BoxExpr(Box::from(Expr::from(Val::from(val))))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Node> for BoxNode {
|
||||
fn from(val: Node) -> Self {
|
||||
BoxNode(Box::from(val))
|
||||
impl From<Expr> for BoxExpr {
|
||||
fn from(val: Expr) -> Self {
|
||||
BoxExpr(Box::from(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoxNode> for Node {
|
||||
fn from(val: BoxNode) -> Self {
|
||||
impl From<BoxExpr> for Expr {
|
||||
fn from(val: BoxExpr) -> Self {
|
||||
*(val.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Expr {
|
||||
fn from(value: i32) -> Self {
|
||||
Self {
|
||||
lhs: Val::Number(value, vec![], vec![]),
|
||||
op: None,
|
||||
rhs: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct ArrayRef {
|
||||
pub name: String,
|
||||
pub subscript: BoxNode,
|
||||
pub subscript: BoxExpr,
|
||||
}
|
||||
|
||||
+222
-233
@@ -193,13 +193,6 @@ impl Compilable for Declaration {
|
||||
|
||||
impl Compilable for Function {
|
||||
fn process(self, state: &mut State, _: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||
if self.typename.is_some() {
|
||||
todo!("Structs are not yet supported")
|
||||
}
|
||||
if self.org.is_some() || self.inline {
|
||||
todo!("Attributes are not yet supported")
|
||||
}
|
||||
|
||||
// The signature for this function, which will eventually get added to the state
|
||||
let mut sig = Signature {
|
||||
label: state.gensym(),
|
||||
@@ -227,12 +220,13 @@ impl Compilable for Function {
|
||||
match stmt {
|
||||
Statement::Return(_) => {}
|
||||
Statement::Assignment(assign) => assign.process(state, Some(&mut sig))?,
|
||||
Statement::Call(call) => {
|
||||
call.process(state, Some(&mut sig))?;
|
||||
// Every call leaves a single-word return value on the stack. In an rvalue this
|
||||
// is useful but in a statement it's garbage (because nothing else is about to
|
||||
// pick it up) so, drop it:
|
||||
sig.emit("pop")
|
||||
Statement::Expr(expr) => {
|
||||
todo!();
|
||||
// expr.process(state, Some(&mut sig))?;
|
||||
// // Every expr leaves a single-word return value on the stack. In an rvalue this
|
||||
// // is useful but in a statement it's garbage (because nothing else is about to
|
||||
// // pick it up) so, drop it:
|
||||
// sig.emit("pop")
|
||||
}
|
||||
Statement::VarDecl(vardecl) => vardecl.process(state, Some(&mut sig))?,
|
||||
Statement::Conditional(_) | Statement::WhileLoop(_) | Statement::RepeatLoop(_) => {
|
||||
@@ -268,13 +262,13 @@ impl Compilable for Assignment {
|
||||
|
||||
// For a normal expr, eval and leave on the stack; for a string literal, add it to
|
||||
// the str table and push the label's address
|
||||
match rvalue {
|
||||
Rvalue::Expr(rvalue) => rvalue.process(state, Some(sig))?,
|
||||
Rvalue::String(string) => {
|
||||
let label = state.add_string(&string);
|
||||
sig.emit_arg("push", label);
|
||||
}
|
||||
}
|
||||
// match rvalue {
|
||||
// Rvalue::Expr(rvalue) => rvalue.process(state, Some(sig))?,
|
||||
// Rvalue::String(string) => {
|
||||
// let label = state.add_string(&string);
|
||||
// sig.emit_arg("push", label);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Then process the lvalue and storew
|
||||
lvalue.process(state, Some(sig))?;
|
||||
@@ -285,36 +279,27 @@ impl Compilable for Assignment {
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
impl Compilable for Call {
|
||||
fn process(self, state: &mut State, sig: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||
// todo arity check
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
impl Compilable for VarDecl {
|
||||
fn process(self, state: &mut State, sig: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||
let mut sig = sig.expect("Var declaration outside function");
|
||||
if self.typename.is_some() || self.size.is_some() {
|
||||
todo!("Structs and arrays are not yet supported")
|
||||
}
|
||||
if let Some(initial) = self.initial {
|
||||
// If it's got an initial value, we have to compile that before we add
|
||||
// the name to scope, or else UB will ensue if it refers to itself:
|
||||
initial.process(state, Some(sig))?;
|
||||
// But then add it to scope and assign:
|
||||
sig.add_local(&self.name)?;
|
||||
// We'll just whip up an lvalue real quick...
|
||||
Lvalue::Name(self.name).process(state, Some(sig))?;
|
||||
sig.emit("storew"); // And store the initial value there
|
||||
} else {
|
||||
// Otherwise, just add it to scope and leave garbage in there:
|
||||
sig.add_local(&self.name)?;
|
||||
}
|
||||
Ok(())
|
||||
todo!();
|
||||
// let mut sig = sig.expect("Var declaration outside function");
|
||||
// if self.typename.is_some() || self.size.is_some() {
|
||||
// todo!("Structs and arrays are not yet supported")
|
||||
// }
|
||||
// if let Some(initial) = self.initial {
|
||||
// // If it's got an initial value, we have to compile that before we add
|
||||
// // the name to scope, or else UB will ensue if it refers to itself:
|
||||
// initial.process(state, Some(sig))?;
|
||||
// // But then add it to scope and assign:
|
||||
// sig.add_local(&self.name)?;
|
||||
// // We'll just whip up an lvalue real quick...
|
||||
// Lvalue::Name(self.name).process(state, Some(sig))?;
|
||||
// sig.emit("storew"); // And store the initial value there
|
||||
// } else {
|
||||
// // Otherwise, just add it to scope and leave garbage in there:
|
||||
// sig.add_local(&self.name)?;
|
||||
// }
|
||||
// Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,119 +348,120 @@ impl Compilable for Lvalue {
|
||||
|
||||
/// Evaluate an expression in the context of a local scope. The runtime brother to eval_const.
|
||||
/// This recursively evaluates a Node and leaves its value on the stack.
|
||||
impl Compilable for Node {
|
||||
impl Compilable for Expr {
|
||||
fn process(self, state: &mut State, sig: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||
let mut sig = sig.expect("Non-const expression outside a function");
|
||||
let global_scope = &state.global_scope;
|
||||
|
||||
// First, a sanity check: try and eval_const this. If it's something incredibly basic
|
||||
// that just becomes an i32, then we don't need to do anything else:
|
||||
if let Ok(val) = eval_const(self.clone(), &state.global_scope) {
|
||||
sig.emit_arg("push", val);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Okay, looks like we need something that's in scope. Let's recurse:
|
||||
match self {
|
||||
Node::Number(n) => {
|
||||
// Numbers are just pushed as literals
|
||||
sig.body.push(format!("push {}", n));
|
||||
Ok(())
|
||||
}
|
||||
Node::Call(_) | Node::ArrayRef(_) | Node::Address(_) => todo!(),
|
||||
Node::Name(name) => match lookup(&name, global_scope, &sig.local_scope) {
|
||||
// Names are treated differently depending on what they are
|
||||
Some(Variable::Literal(val)) => {
|
||||
// Names of constants are just that number
|
||||
sig.emit_arg("push", *val);
|
||||
Ok(())
|
||||
}
|
||||
Some(Variable::IndirectLabel(label)) => {
|
||||
// Names pointing at labels are loaded (rvalue; for lvalues they aren't)
|
||||
// Indirect labels are the address of where the value is stored (a var, .db)
|
||||
sig.emit_arg("loadw", label.clone());
|
||||
Ok(())
|
||||
}
|
||||
Some(Variable::DirectLabel(label)) => {
|
||||
// Direct labels are like functions, the label itself is the value, so just
|
||||
// push it:
|
||||
sig.emit_arg("push", label.clone());
|
||||
Ok(())
|
||||
}
|
||||
Some(Variable::Local(offset)) => {
|
||||
// Names of locals are added from the frame pointer
|
||||
let offset = *offset;
|
||||
sig.emit("loadw frame");
|
||||
if offset > 0 {
|
||||
sig.emit_arg("add", offset);
|
||||
sig.emit("loadw");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(CompileError(0, 0, format!("Unknown name {}", name))),
|
||||
},
|
||||
Node::Expr(lhs, op, rhs) => {
|
||||
// Recurse on expressions, handling operators
|
||||
lhs.0.process(state, Some(&mut sig))?;
|
||||
rhs.0.process(state, Some(&mut sig))?;
|
||||
match op {
|
||||
// Basic math
|
||||
Operator::Add => sig.emit("add"),
|
||||
Operator::Sub => sig.emit("sub"),
|
||||
Operator::Mul => sig.emit("mul"),
|
||||
Operator::Div => sig.emit("div"),
|
||||
Operator::Mod => sig.emit("mod"),
|
||||
Operator::And => {
|
||||
// Vulcan "and" is bitwise, so we need to flag-ify both args to make it logical
|
||||
sig.emit("gt 0");
|
||||
sig.emit("swap");
|
||||
sig.emit("gt 0");
|
||||
sig.emit("and");
|
||||
}
|
||||
Operator::Or => {
|
||||
// Same as and, flag-ify both args
|
||||
sig.emit("gt 0");
|
||||
sig.emit("swap");
|
||||
sig.emit("gt 0");
|
||||
sig.emit("or");
|
||||
}
|
||||
Operator::BitAnd => sig.emit("and"),
|
||||
Operator::BitOr => sig.emit("or"),
|
||||
Operator::Xor => sig.emit("xor"),
|
||||
Operator::Lt => sig.emit("alt"),
|
||||
Operator::Le => {
|
||||
// LE and GE are the inverses of GT and LT (arithmetic versions)
|
||||
sig.emit("agt");
|
||||
sig.emit("not");
|
||||
}
|
||||
Operator::Gt => sig.emit("agt"),
|
||||
Operator::Ge => {
|
||||
sig.emit("alt");
|
||||
sig.emit("not");
|
||||
}
|
||||
Operator::Eq => {
|
||||
sig.emit("xor");
|
||||
sig.emit("not");
|
||||
}
|
||||
Operator::Ne => sig.emit("xor"),
|
||||
Operator::Lshift => sig.emit("lshift"),
|
||||
Operator::Rshift => sig.emit("arshift"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Node::Prefix(prefix, node) => {
|
||||
node.0.process(state, Some(sig))?;
|
||||
match prefix {
|
||||
Prefix::Neg => {
|
||||
// To arithmetically negate something, invert and increment (2s complement)
|
||||
sig.emit("xor -1");
|
||||
sig.emit("add 1");
|
||||
}
|
||||
Prefix::Not => sig.emit("not"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
todo!();
|
||||
// let mut sig = sig.expect("Non-const expression outside a function");
|
||||
// let global_scope = &state.global_scope;
|
||||
//
|
||||
// // First, a sanity check: try and eval_const this. If it's something incredibly basic
|
||||
// // that just becomes an i32, then we don't need to do anything else:
|
||||
// if let Ok(val) = eval_const(self.clone(), &state.global_scope) {
|
||||
// sig.emit_arg("push", val);
|
||||
// return Ok(());
|
||||
// }
|
||||
//
|
||||
// // Okay, looks like we need something that's in scope. Let's recurse:
|
||||
// match self.val {
|
||||
// Val::Number(n) => {
|
||||
// // Numbers are just pushed as literals
|
||||
// sig.body.push(format!("push {}", n));
|
||||
// Ok(())
|
||||
// }
|
||||
// Val::Name(name) => match lookup(&name, global_scope, &sig.local_scope) {
|
||||
// // Names are treated differently depending on what they are
|
||||
// Some(Variable::Literal(val)) => {
|
||||
// // Names of constants are just that number
|
||||
// sig.emit_arg("push", *val);
|
||||
// Ok(())
|
||||
// }
|
||||
// Some(Variable::IndirectLabel(label)) => {
|
||||
// // Names pointing at labels are loaded (rvalue; for lvalues they aren't)
|
||||
// // Indirect labels are the address of where the value is stored (a var, .db)
|
||||
// sig.emit_arg("loadw", label.clone());
|
||||
// Ok(())
|
||||
// }
|
||||
// Some(Variable::DirectLabel(label)) => {
|
||||
// // Direct labels are like functions, the label itself is the value, so just
|
||||
// // push it:
|
||||
// sig.emit_arg("push", label.clone());
|
||||
// Ok(())
|
||||
// }
|
||||
// Some(Variable::Local(offset)) => {
|
||||
// // Names of locals are added from the frame pointer
|
||||
// let offset = *offset;
|
||||
// sig.emit("loadw frame");
|
||||
// if offset > 0 {
|
||||
// sig.emit_arg("add", offset);
|
||||
// sig.emit("loadw");
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
// None => Err(CompileError(0, 0, format!("Unknown name {}", name))),
|
||||
// },
|
||||
// Val::Expr(node) => {
|
||||
// todo!();
|
||||
// // // Recurse on expressions, handling operators
|
||||
// // lhs.0.process(state, Some(&mut sig))?;
|
||||
// // rhs.0.process(state, Some(&mut sig))?;
|
||||
// // match op {
|
||||
// // // Basic math
|
||||
// // Operator::Add => sig.emit("add"),
|
||||
// // Operator::Sub => sig.emit("sub"),
|
||||
// // Operator::Mul => sig.emit("mul"),
|
||||
// // Operator::Div => sig.emit("div"),
|
||||
// // Operator::Mod => sig.emit("mod"),
|
||||
// // Operator::And => {
|
||||
// // // Vulcan "and" is bitwise, so we need to flag-ify both args to make it logical
|
||||
// // sig.emit("gt 0");
|
||||
// // sig.emit("swap");
|
||||
// // sig.emit("gt 0");
|
||||
// // sig.emit("and");
|
||||
// // }
|
||||
// // Operator::Or => {
|
||||
// // // Same as and, flag-ify both args
|
||||
// // sig.emit("gt 0");
|
||||
// // sig.emit("swap");
|
||||
// // sig.emit("gt 0");
|
||||
// // sig.emit("or");
|
||||
// // }
|
||||
// // Operator::BitAnd => sig.emit("and"),
|
||||
// // Operator::BitOr => sig.emit("or"),
|
||||
// // Operator::Xor => sig.emit("xor"),
|
||||
// // Operator::Lt => sig.emit("alt"),
|
||||
// // Operator::Le => {
|
||||
// // // LE and GE are the inverses of GT and LT (arithmetic versions)
|
||||
// // sig.emit("agt");
|
||||
// // sig.emit("not");
|
||||
// // }
|
||||
// // Operator::Gt => sig.emit("agt"),
|
||||
// // Operator::Ge => {
|
||||
// // sig.emit("alt");
|
||||
// // sig.emit("not");
|
||||
// // }
|
||||
// // Operator::Eq => {
|
||||
// // sig.emit("xor");
|
||||
// // sig.emit("not");
|
||||
// // }
|
||||
// // Operator::Ne => sig.emit("xor"),
|
||||
// // Operator::Lshift => sig.emit("lshift"),
|
||||
// // Operator::Rshift => sig.emit("arshift"),
|
||||
// // }
|
||||
// // Ok(())
|
||||
// }
|
||||
// // Node::Prefix(prefix, node) => {
|
||||
// // node.0.process(state, Some(sig))?;
|
||||
// // match prefix {
|
||||
// // Prefix::Neg => {
|
||||
// // // To arithmetically negate something, invert and increment (2s complement)
|
||||
// // sig.emit("xor -1");
|
||||
// // sig.emit("add 1");
|
||||
// // }
|
||||
// // Prefix::Not => sig.emit("not"),
|
||||
// // }
|
||||
// // Ok(())
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,17 +480,18 @@ impl Compilable for Global {
|
||||
|
||||
impl Compilable for Const {
|
||||
fn process(self, state: &mut State, _: Option<&mut Signature>) -> Result<(), CompileError> {
|
||||
let var = if self.string.is_some() {
|
||||
// If it's a string, add it to the string table
|
||||
Variable::DirectLabel(state.add_string(&self.string.unwrap()))
|
||||
} else if let Some(expr) = self.value {
|
||||
// Otherwise eval_const it
|
||||
Variable::Literal(eval_const(expr, &state.global_scope)?)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
// Add it to the global namespace
|
||||
state.add_global(&self.name, |_| var.clone())
|
||||
todo!()
|
||||
// let var = if self.string.is_some() {
|
||||
// // If it's a string, add it to the string table
|
||||
// Variable::DirectLabel(state.add_string(&self.string.unwrap()))
|
||||
// } else if let Some(expr) = self.value {
|
||||
// // Otherwise eval_const it
|
||||
// Variable::Literal(eval_const(expr, &state.global_scope)?)
|
||||
// } else {
|
||||
// unreachable!()
|
||||
// };
|
||||
// // Add it to the global namespace
|
||||
// state.add_global(&self.name, |_| var.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,61 +506,63 @@ fn to_flag(val: bool) -> i32 {
|
||||
}
|
||||
|
||||
/// Evaluate a node in a static context, for const definitions and array sizes, that sort of thing.
|
||||
pub fn eval_const(expr: Node, scope: &Scope) -> Result<i32, CompileError> {
|
||||
match expr {
|
||||
Node::Number(n) => Ok(n),
|
||||
Node::Address(_) | Node::ArrayRef(_) | Node::Call(_) => Err(CompileError(
|
||||
0,
|
||||
0,
|
||||
String::from("Constants must be statically defined"),
|
||||
)),
|
||||
|
||||
Node::Name(n) => {
|
||||
if let Some(Variable::Literal(val)) = scope.get(&n) {
|
||||
Ok(*val)
|
||||
} else {
|
||||
Err(CompileError(0, 0, format!("Unknown const {}", n)))
|
||||
}
|
||||
}
|
||||
|
||||
Node::Expr(lhs, op, rhs) => {
|
||||
let lhs = eval_const(lhs.into(), scope)?;
|
||||
let rhs = eval_const(rhs.into(), scope)?;
|
||||
match op {
|
||||
Operator::Add => Ok(lhs + rhs),
|
||||
Operator::Sub => Ok(lhs - rhs),
|
||||
Operator::Mul => Ok(lhs * rhs),
|
||||
Operator::Div => Ok(lhs / rhs),
|
||||
Operator::Mod => Ok(lhs % rhs),
|
||||
Operator::And => Ok(to_flag(lhs != 0 && rhs != 0)),
|
||||
Operator::Or => Ok(to_flag(lhs != 0 || rhs != 0)),
|
||||
Operator::BitAnd => Ok(lhs & rhs),
|
||||
Operator::BitOr => Ok(lhs | rhs),
|
||||
Operator::Xor => Ok(lhs ^ rhs),
|
||||
Operator::Lt => Ok(to_flag(lhs < rhs)),
|
||||
Operator::Le => Ok(to_flag(lhs <= rhs)),
|
||||
Operator::Gt => Ok(to_flag(lhs > rhs)),
|
||||
Operator::Ge => Ok(to_flag(lhs >= rhs)),
|
||||
Operator::Eq => Ok(to_flag(lhs == rhs)),
|
||||
Operator::Ne => Ok(to_flag(lhs != rhs)),
|
||||
Operator::Lshift => Ok(lhs << rhs),
|
||||
Operator::Rshift => Ok(lhs >> rhs),
|
||||
}
|
||||
}
|
||||
Node::Prefix(p, child) => {
|
||||
let val = eval_const(child.into(), scope)?;
|
||||
match p {
|
||||
Prefix::Neg => Ok(-val),
|
||||
Prefix::Not => {
|
||||
if val == 0 {
|
||||
Ok(1)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn eval_const(expr: Expr, scope: &Scope) -> Result<i32, CompileError> {
|
||||
todo!()
|
||||
// match expr.lhs {
|
||||
// Val::Number(n) => Ok(n),
|
||||
// // Node::Address(_) | Node::ArrayRef(_) | Node::Call(_) => Err(CompileError(
|
||||
// // 0,
|
||||
// // 0,
|
||||
// // String::from("Constants must be statically defined"),
|
||||
// // )),
|
||||
//
|
||||
// Val::Name(n) => {
|
||||
// if let Some(Variable::Literal(val)) = scope.get(&n) {
|
||||
// Ok(*val)
|
||||
// } else {
|
||||
// Err(CompileError(0, 0, format!("Unknown const {}", n)))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Val::Expr(node) => {
|
||||
// todo!()
|
||||
// // let lhs = eval_const(lhs.into(), scope)?;
|
||||
// // let rhs = eval_const(rhs.into(), scope)?;
|
||||
// // match op {
|
||||
// // Operator::Add => Ok(lhs + rhs),
|
||||
// // Operator::Sub => Ok(lhs - rhs),
|
||||
// // Operator::Mul => Ok(lhs * rhs),
|
||||
// // Operator::Div => Ok(lhs / rhs),
|
||||
// // Operator::Mod => Ok(lhs % rhs),
|
||||
// // Operator::And => Ok(to_flag(lhs != 0 && rhs != 0)),
|
||||
// // Operator::Or => Ok(to_flag(lhs != 0 || rhs != 0)),
|
||||
// // Operator::BitAnd => Ok(lhs & rhs),
|
||||
// // Operator::BitOr => Ok(lhs | rhs),
|
||||
// // Operator::Xor => Ok(lhs ^ rhs),
|
||||
// // Operator::Lt => Ok(to_flag(lhs < rhs)),
|
||||
// // Operator::Le => Ok(to_flag(lhs <= rhs)),
|
||||
// // Operator::Gt => Ok(to_flag(lhs > rhs)),
|
||||
// // Operator::Ge => Ok(to_flag(lhs >= rhs)),
|
||||
// // Operator::Eq => Ok(to_flag(lhs == rhs)),
|
||||
// // Operator::Ne => Ok(to_flag(lhs != rhs)),
|
||||
// // Operator::Lshift => Ok(lhs << rhs),
|
||||
// // Operator::Rshift => Ok(lhs >> rhs),
|
||||
// // }
|
||||
// }
|
||||
// // Node::Prefix(p, child) => {
|
||||
// // let val = eval_const(child.into(), scope)?;
|
||||
// // match p {
|
||||
// // Prefix::Neg => Ok(-val),
|
||||
// // Prefix::Not => {
|
||||
// // if val == 0 {
|
||||
// // Ok(1)
|
||||
// // } else {
|
||||
// // Ok(0)
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -584,12 +573,12 @@ mod test {
|
||||
#[test]
|
||||
fn test_eval_const() {
|
||||
let empty_scope = Scope::new();
|
||||
let to_node = |s| Node::parse(s).unwrap();
|
||||
let to_expr = |s| Expr::parse(s).unwrap();
|
||||
|
||||
// Basic arithmetic
|
||||
assert_eq!(eval_const(to_node("2 * 3 + 4"), &empty_scope), Ok(10));
|
||||
assert_eq!(eval_const(to_node("1 + -2"), &empty_scope), Ok(-1));
|
||||
assert_eq!(eval_const(to_node("1 << 3"), &empty_scope), Ok(8));
|
||||
assert_eq!(eval_const(to_expr("2 * 3 + 4"), &empty_scope), Ok(10));
|
||||
assert_eq!(eval_const(to_expr("1 + -2"), &empty_scope), Ok(-1));
|
||||
assert_eq!(eval_const(to_expr("1 << 3"), &empty_scope), Ok(8));
|
||||
|
||||
// Names
|
||||
let scope: Scope = [
|
||||
@@ -597,12 +586,12 @@ mod test {
|
||||
("bar".into(), Variable::Literal(5)),
|
||||
]
|
||||
.into();
|
||||
assert_eq!(eval_const(to_node("foo + 5"), &scope), Ok(15));
|
||||
assert_eq!(eval_const(to_node("bar * foo"), &scope), Ok(50));
|
||||
assert_eq!(eval_const(to_expr("foo + 5"), &scope), Ok(15));
|
||||
assert_eq!(eval_const(to_expr("bar * foo"), &scope), Ok(50));
|
||||
|
||||
// Error
|
||||
assert_eq!(
|
||||
eval_const(to_node("nope"), &scope),
|
||||
eval_const(to_expr("nope"), &scope),
|
||||
Err(CompileError(0, 0, String::from("Unknown const nope")))
|
||||
);
|
||||
}
|
||||
|
||||
+299
-289
@@ -2,7 +2,7 @@ use pest::pratt_parser::PrattParser;
|
||||
use pest::Parser;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "forge.pest"]
|
||||
#[grammar = "new.pest"]
|
||||
struct ForgeParser;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -30,6 +30,7 @@ use crate::ast::*;
|
||||
use pest::error::{Error, LineColLocation};
|
||||
use std::iter::Peekable;
|
||||
use std::str::FromStr;
|
||||
use crate::ast::Suffix::{Arglist, Subscript};
|
||||
|
||||
pub(crate) type Pair<'a> = pest::iterators::Pair<'a, Rule>;
|
||||
pub(crate) type Pairs<'i, R = Rule> = pest::iterators::Pairs<'i, R>;
|
||||
@@ -179,7 +180,7 @@ impl AstNode for Varinfo {
|
||||
.map(|t| String::from(t.first().as_str()));
|
||||
let size = children
|
||||
.next_if_rule(Rule::size)
|
||||
.map(|s| Node::from_pair(s.first()));
|
||||
.map(|s| Expr::from_pair(s.first()));
|
||||
Self { typename, size }
|
||||
}
|
||||
}
|
||||
@@ -246,7 +247,7 @@ impl AstNode for Const {
|
||||
Rule::expr => Const {
|
||||
name,
|
||||
string: None,
|
||||
value: Some(Node::from_pair(value)),
|
||||
value: Some(Expr::from_pair(value)),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -260,20 +261,6 @@ impl AstNode for Function {
|
||||
fn from_pair(pair: Pair<'_>) -> Self {
|
||||
let mut inner = pair.into_inner().peekable();
|
||||
let name = String::from(inner.next().unwrap().as_str());
|
||||
let mut inline = false;
|
||||
let mut org = None;
|
||||
let mut typename = None;
|
||||
if let Some(annotations) = inner.next_if_rule(Rule::annotations) {
|
||||
for annotation in annotations.into_inner() {
|
||||
let annotation = annotation.first();
|
||||
match annotation.as_rule() {
|
||||
Rule::inline_annotation => inline = true,
|
||||
Rule::org_annotation => org = Some(annotation.first().into_number()),
|
||||
Rule::type_annotation => typename = Some(annotation.first_as_string()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
let args: Vec<_> = inner
|
||||
.next()
|
||||
.unwrap()
|
||||
@@ -285,9 +272,6 @@ impl AstNode for Function {
|
||||
|
||||
Self {
|
||||
name,
|
||||
org,
|
||||
typename,
|
||||
inline,
|
||||
args,
|
||||
body,
|
||||
}
|
||||
@@ -313,7 +297,7 @@ impl AstNode for Statement {
|
||||
match pair.as_rule() {
|
||||
Rule::return_stmt => Self::Return(Return::from_pair(pair)),
|
||||
Rule::assignment => Self::Assignment(Assignment::from_pair(pair)),
|
||||
Rule::call => Self::Call(Call::from_pair(pair)),
|
||||
Rule::expr => Self::Expr(Expr::from_pair(pair)),
|
||||
Rule::var_decl => Self::VarDecl(VarDecl::from_pair(pair)),
|
||||
Rule::conditional => Self::Conditional(Conditional::from_pair(pair)),
|
||||
Rule::while_loop => Self::WhileLoop(WhileLoop::from_pair(pair)),
|
||||
@@ -330,7 +314,7 @@ impl AstNode for Return {
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
pair.into_inner()
|
||||
.next()
|
||||
.map_or(Self(None), |expr| Self(Some(Node::from_pair(expr))))
|
||||
.map_or(Self(None), |expr| Self(Some(Expr::from_pair(expr))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +326,7 @@ impl AstNode for Assignment {
|
||||
let mut pairs = pair.into_inner();
|
||||
let lvalue_pair = pairs.next().unwrap().first();
|
||||
let lvalue = match lvalue_pair.as_rule() {
|
||||
Rule::arrayref => Lvalue::ArrayRef(ArrayRef::from_pair(lvalue_pair)),
|
||||
//Rule::arrayref => Lvalue::ArrayRef(ArrayRef::from_pair(lvalue_pair)),
|
||||
Rule::name => Lvalue::Name(String::from(lvalue_pair.as_str())),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@@ -360,7 +344,7 @@ impl AstNode for Rvalue {
|
||||
let pair = pair.first();
|
||||
match pair.as_rule() {
|
||||
Rule::string => Self::String(pair.into_quoted_string()),
|
||||
Rule::expr => Self::Expr(Node::from_pair(pair)),
|
||||
Rule::expr => Self::Expr(Expr::from_pair(pair)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -368,26 +352,13 @@ impl AstNode for Rvalue {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Call {
|
||||
const RULE: Rule = Rule::call;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let mut inner = pair.into_inner();
|
||||
let name = String::from(inner.next().unwrap().as_str());
|
||||
let arg_pairs = inner.next().unwrap().into_inner();
|
||||
let args: Vec<Rvalue> = arg_pairs.map(Rvalue::from_pair).collect();
|
||||
Self { name, args }
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for VarDecl {
|
||||
const RULE: Rule = Rule::var_decl;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let mut inner = pair.into_inner();
|
||||
let name = String::from(inner.next().unwrap().as_str());
|
||||
let Varinfo { typename, size } = Varinfo::from_pair(inner.next().unwrap());
|
||||
let initial = inner.next().map(Node::from_pair);
|
||||
let initial = inner.next().map(Expr::from_pair);
|
||||
Self {
|
||||
name,
|
||||
typename,
|
||||
@@ -413,7 +384,7 @@ impl AstNode for Conditional {
|
||||
const RULE: Rule = Rule::conditional;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let mut inner = pair.into_inner();
|
||||
let condition = Node::from_pair(inner.next().unwrap());
|
||||
let condition = Expr::from_pair(inner.next().unwrap());
|
||||
let body = Block::from_pair(inner.next().unwrap());
|
||||
let alternative = inner.next().map(Block::from_pair);
|
||||
Self {
|
||||
@@ -431,7 +402,7 @@ impl AstNode for WhileLoop {
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let mut inner = pair.into_inner();
|
||||
Self {
|
||||
condition: Node::from_pair(inner.next().unwrap()),
|
||||
condition: Expr::from_pair(inner.next().unwrap()),
|
||||
body: Block::from_pair(inner.next().unwrap()),
|
||||
}
|
||||
}
|
||||
@@ -443,7 +414,7 @@ impl AstNode for RepeatLoop {
|
||||
const RULE: Rule = Rule::repeat_loop;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let mut inner = pair.into_inner().peekable();
|
||||
let count = Node::from_pair(inner.next().unwrap());
|
||||
let count = Expr::from_pair(inner.next().unwrap());
|
||||
let name = inner
|
||||
.next_if_rule(Rule::name)
|
||||
.map(|p| String::from(p.as_str()));
|
||||
@@ -454,29 +425,28 @@ impl AstNode for RepeatLoop {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Node {
|
||||
const RULE: Rule = Rule::expr; // Also used for vals / terms / the whole tree
|
||||
impl AstNode for Expr {
|
||||
const RULE: Rule = Rule::expr;
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
PRATT_PARSER
|
||||
.map_primary(|val| {
|
||||
let primary = val.first();
|
||||
match primary.as_rule() {
|
||||
Rule::number => Node::Number(primary.into_number()),
|
||||
Rule::name => Node::Name(String::from(primary.as_str())),
|
||||
Rule::call => Node::Call(Call::from_pair(primary)),
|
||||
Rule::arrayref => Node::ArrayRef(ArrayRef::from_pair(primary)),
|
||||
Rule::address => Node::Address(primary.first_as_string()),
|
||||
Rule::expr => Node::from_pair(primary),
|
||||
rule => unreachable!("Expr::parse expected atom, found {:?}", rule),
|
||||
}
|
||||
Expr {
|
||||
lhs: Val::from_pair(val),
|
||||
op: None,
|
||||
rhs: None
|
||||
}
|
||||
})
|
||||
.map_prefix(|op, rhs| Node::Prefix(Prefix::from_pair(op), rhs.into()))
|
||||
.map_infix(|lhs, op, rhs| Node::Expr(lhs.into(), Operator::from_pair(op), rhs.into()))
|
||||
.map_infix(|lhs, op, rhs|
|
||||
Expr {
|
||||
lhs: Val::from(lhs),
|
||||
op: Some(Operator::from_pair(op)),
|
||||
rhs: Some(rhs.into()),
|
||||
})
|
||||
.parse(pair.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
impl Expr {
|
||||
pub(crate) fn parse(src: &str) -> Result<Self, ParseError> {
|
||||
Self::from_str(src)
|
||||
}
|
||||
@@ -485,7 +455,8 @@ impl Node {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Operator {
|
||||
const RULE: Rule = Rule::operator; // also term_op
|
||||
const RULE: Rule = Rule::operator;
|
||||
// also term_op
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
match pair.as_str() {
|
||||
"+" => Self::Add,
|
||||
@@ -518,6 +489,7 @@ impl AstNode for Prefix {
|
||||
match pair.as_str() {
|
||||
"!" => Self::Not,
|
||||
"-" => Self::Neg,
|
||||
"&" => Self::Address,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -525,16 +497,16 @@ impl AstNode for Prefix {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for ArrayRef {
|
||||
const RULE: Rule = Rule::arrayref;
|
||||
impl AstNode for Suffix {
|
||||
const RULE: Rule = Rule::suffix;
|
||||
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let mut inner = pair.into_inner();
|
||||
let name = String::from(inner.next().unwrap().as_str());
|
||||
let subscript = Node::from_pair(inner.next().unwrap().first());
|
||||
Self {
|
||||
name,
|
||||
subscript: subscript.into(),
|
||||
let first = pair.first();
|
||||
match first.as_rule() {
|
||||
Rule::subscript => Subscript(Expr::from_pair(first.first())),
|
||||
Rule::member => Self::Member(first.first_as_string()),
|
||||
Rule::arglist => Arglist(first.into_inner().map(Rvalue::from_pair).collect()),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -557,10 +529,61 @@ impl AstNode for Program {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl AstNode for Val {
|
||||
const RULE: Rule = Rule::val;
|
||||
|
||||
fn from_pair(pair: Pair) -> Self {
|
||||
let mut suffix = vec![];
|
||||
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() {
|
||||
Rule::number => Self::Number(val.into_number(), prefix, suffix),
|
||||
Rule::name => Self::Name(String::from(val.as_str()), prefix, suffix),
|
||||
Rule::expr => Self::Expr(Expr::from_pair(val).into(), prefix, suffix),
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_vals() {
|
||||
// Basic vals with no extras:
|
||||
assert_eq!(Val::from_str("10"), Ok(10.into()));
|
||||
assert_eq!(Val::from_str("blah"), Ok(Val::Name("blah".into(), vec![], vec![])));
|
||||
|
||||
// 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]
|
||||
fn parse_globals() {
|
||||
assert_eq!(
|
||||
@@ -568,7 +591,7 @@ mod test {
|
||||
Ok(Global {
|
||||
name: "foo".into(),
|
||||
typename: None,
|
||||
size: None
|
||||
size: None,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -576,7 +599,7 @@ mod test {
|
||||
Ok(Global {
|
||||
name: "foo".into(),
|
||||
typename: Some("Thing".into()),
|
||||
size: None
|
||||
size: None,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -584,7 +607,7 @@ mod test {
|
||||
Ok(Global {
|
||||
name: "foo".into(),
|
||||
typename: Some("Thing".into()),
|
||||
size: Some(Node::Number(10))
|
||||
size: Some(10.into()),
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -592,7 +615,7 @@ mod test {
|
||||
Ok(Global {
|
||||
name: "foo".into(),
|
||||
typename: None,
|
||||
size: Some(Node::Number(10))
|
||||
size: Some(10.into()),
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -603,27 +626,24 @@ mod test {
|
||||
Const::from_str("const a = 123;"),
|
||||
Ok(Const {
|
||||
name: "a".into(),
|
||||
value: Some(Node::Number(123)),
|
||||
string: None
|
||||
value: Some(123.into()),
|
||||
string: None,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
Const::from_str("const a = 0xaa;"),
|
||||
Ok(Const {
|
||||
name: "a".into(),
|
||||
value: Some(Node::Number(0xaa)),
|
||||
string: None
|
||||
value: Some(0xaa.into()),
|
||||
string: None,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
Const::from_str("const a = -7;"),
|
||||
Ok(Const {
|
||||
name: "a".into(),
|
||||
value: Some(Node::Prefix(
|
||||
crate::ast::Prefix::Neg,
|
||||
Node::Number(7).into()
|
||||
)),
|
||||
string: None
|
||||
value: Some(Val::Number(7, vec![Prefix::Neg], vec![]).into()),
|
||||
string: None,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -631,7 +651,7 @@ mod test {
|
||||
Ok(Const {
|
||||
name: "a".into(),
|
||||
value: None,
|
||||
string: Some("foo bar".into())
|
||||
string: Some("foo bar".into()),
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -646,14 +666,14 @@ mod test {
|
||||
Member {
|
||||
name: "x".into(),
|
||||
typename: None,
|
||||
size: None
|
||||
size: None,
|
||||
},
|
||||
Member {
|
||||
name: "y".into(),
|
||||
typename: None,
|
||||
size: None
|
||||
size: None,
|
||||
},
|
||||
]
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -664,8 +684,8 @@ mod test {
|
||||
members: vec![Member {
|
||||
name: "bar".into(),
|
||||
typename: None,
|
||||
size: Some(Node::Number(100))
|
||||
},]
|
||||
size: Some(100.into()),
|
||||
}, ],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -676,8 +696,8 @@ mod test {
|
||||
members: vec![Member {
|
||||
name: "bar".into(),
|
||||
typename: Some("Thing".into()),
|
||||
size: Some(Node::Number(100))
|
||||
},]
|
||||
size: Some(100.into()),
|
||||
}, ],
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -688,9 +708,6 @@ mod test {
|
||||
Function::from_str("fn foo() {}"),
|
||||
Ok(Function {
|
||||
name: "foo".into(),
|
||||
inline: false,
|
||||
org: None,
|
||||
typename: None,
|
||||
args: vec![],
|
||||
body: Block(vec![]),
|
||||
})
|
||||
@@ -699,18 +716,15 @@ mod test {
|
||||
Function::from_str("fn foo(a, b) {}"),
|
||||
Ok(Function {
|
||||
name: "foo".into(),
|
||||
inline: false,
|
||||
org: None,
|
||||
typename: None,
|
||||
args: vec![
|
||||
Argname {
|
||||
name: "a".into(),
|
||||
typename: None
|
||||
typename: None,
|
||||
},
|
||||
Argname {
|
||||
name: "b".into(),
|
||||
typename: None
|
||||
}
|
||||
typename: None,
|
||||
},
|
||||
],
|
||||
body: Block(vec![]),
|
||||
})
|
||||
@@ -719,18 +733,15 @@ mod test {
|
||||
Function::from_str("fn foo(a:Blah, b) {}"),
|
||||
Ok(Function {
|
||||
name: "foo".into(),
|
||||
inline: false,
|
||||
org: None,
|
||||
typename: None,
|
||||
args: vec![
|
||||
Argname {
|
||||
name: "a".into(),
|
||||
typename: Some("Blah".into())
|
||||
typename: Some("Blah".into()),
|
||||
},
|
||||
Argname {
|
||||
name: "b".into(),
|
||||
typename: None
|
||||
}
|
||||
typename: None,
|
||||
},
|
||||
],
|
||||
body: Block(vec![]),
|
||||
})
|
||||
@@ -738,9 +749,6 @@ mod test {
|
||||
|
||||
let func = Function {
|
||||
name: "foo".into(),
|
||||
inline: true,
|
||||
org: Some(0x400),
|
||||
typename: None,
|
||||
args: vec![Argname {
|
||||
name: "a".into(),
|
||||
typename: None,
|
||||
@@ -760,179 +768,181 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn parse_exprs() {
|
||||
use Node::*;
|
||||
use Operator::*;
|
||||
// A very, very basic expression
|
||||
assert_eq!(Node::from_str("23"), Ok(Number(23)));
|
||||
// For the rest of these we'll .into() stuff for brevity
|
||||
//Some(assert_eq!(Expr::from_str("23"), Ok(Expr { lhs: Val::Number(23, vec![], vec![]), op: None, rhs: None })));
|
||||
|
||||
// Two vals with an operator
|
||||
assert_eq!(
|
||||
Node::from_str("23 + 5"),
|
||||
Ok(Expr(23.into(), Add, Number(5).into()))
|
||||
Expr::from_str("23 + 5"),
|
||||
Ok(Expr { lhs: 23.into(), op: Some(Add), rhs: Some(5.into()) })
|
||||
);
|
||||
|
||||
// Multiple terms at the same precedence level
|
||||
assert_eq!(
|
||||
Node::from_str("1 + 2 + 3"),
|
||||
Ok(Expr(Expr(1.into(), Add, 2.into()).into(), Add, 3.into()))
|
||||
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()) })
|
||||
);
|
||||
|
||||
// Higher precedence levels
|
||||
assert_eq!(
|
||||
Node::from_str("1 + 2 * 3"),
|
||||
Ok(Expr(1.into(), Add, Expr(2.into(), Mul, 3.into()).into()))
|
||||
);
|
||||
/*
|
||||
// Higher precedence levels
|
||||
assert_eq!(
|
||||
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())));
|
||||
assert_eq!(Node::from_str("2 * 3"), Ok(Expr(2.into(), Mul, 3.into())));
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("2 * 3 + 4"),
|
||||
Ok(Expr(Expr(2.into(), Mul, 3.into()).into(), Add, 4.into()))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("2 * 3 + 4"),
|
||||
Ok(Expr(Expr(2.into(), Mul, 3.into()).into(), Add, 4.into()))
|
||||
);
|
||||
|
||||
// Various operators
|
||||
assert_eq!(
|
||||
Node::from_str("1 || 2 && 3"),
|
||||
Ok(Expr(1.into(), Or, Expr(2.into(), And, 3.into()).into()))
|
||||
);
|
||||
// Various operators
|
||||
assert_eq!(
|
||||
Node::from_str("1 || 2 && 3"),
|
||||
Ok(Expr(1.into(), Or, Expr(2.into(), And, 3.into()).into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("2 && &blah"),
|
||||
Ok(Expr(2.into(), And, Address("blah".into()).into()))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("2 && &blah"),
|
||||
Ok(Expr(2.into(), And, Address("blah".into()).into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("2 & &blah"),
|
||||
Ok(Expr(2.into(), BitAnd, Address("blah".into()).into()))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("2 & &blah"),
|
||||
Ok(Expr(2.into(), BitAnd, Address("blah".into()).into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("1 | 2 ^ 3"),
|
||||
Ok(Expr(1.into(), BitOr, Expr(2.into(), Xor, 3.into()).into()))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("1 | 2 ^ 3"),
|
||||
Ok(Expr(1.into(), BitOr, Expr(2.into(), Xor, 3.into()).into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("x == y > z"),
|
||||
Ok(Expr(
|
||||
Name("x".into()).into(),
|
||||
Eq,
|
||||
Expr(Name("y".into()).into(), Gt, Name("z".into()).into()).into()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("x == y > z"),
|
||||
Ok(Expr(
|
||||
Name("x".into()).into(),
|
||||
Eq,
|
||||
Expr(Name("y".into()).into(), Gt, Name("z".into()).into()).into()
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("1 << 6"),
|
||||
Ok(Expr(1.into(), Lshift, 6.into()))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("1 << 6"),
|
||||
Ok(Expr(1.into(), Lshift, 6.into()))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("!a - -3"),
|
||||
Ok(Expr(
|
||||
Prefix(crate::ast::Prefix::Not, Name("a".into()).into()).into(),
|
||||
Sub,
|
||||
Prefix(crate::ast::Prefix::Neg, 3.into()).into()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("!a - -3"),
|
||||
Ok(Expr(
|
||||
Prefix(crate::ast::Prefix::Not, Name("a".into()).into()).into(),
|
||||
Sub,
|
||||
Prefix(crate::ast::Prefix::Neg, 3.into()).into()
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Node::from_str("-(4 * 5)"),
|
||||
Ok(Prefix(
|
||||
crate::ast::Prefix::Neg,
|
||||
Expr(4.into(), Mul, 5.into()).into()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
Node::from_str("-(4 * 5)"),
|
||||
Ok(Prefix(
|
||||
crate::ast::Prefix::Neg,
|
||||
Expr(4.into(), Mul, 5.into()).into()
|
||||
))
|
||||
);
|
||||
|
||||
// Parens
|
||||
assert_eq!(
|
||||
Node::from_str("(1 + 2) * 3"),
|
||||
Ok(Expr(Expr(1.into(), Add, 2.into()).into(), Mul, 3.into()))
|
||||
);
|
||||
// Parens
|
||||
assert_eq!(
|
||||
Node::from_str("(1 + 2) * 3"),
|
||||
Ok(Expr(Expr(1.into(), Add, 2.into()).into(), Mul, 3.into()))
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_arrayrefs() {
|
||||
use crate::ast::ArrayRef as AR;
|
||||
use Node::*;
|
||||
use Operator::*;
|
||||
|
||||
// Normal numbers
|
||||
assert_eq!(
|
||||
AR::from_str("foo[7]"),
|
||||
Ok(AR {
|
||||
name: "foo".into(),
|
||||
subscript: Number(7).into()
|
||||
})
|
||||
);
|
||||
|
||||
// Full exprs (this is the last one of these; the full expr test above covers it
|
||||
assert_eq!(
|
||||
AR::from_str("foo[7+x]"),
|
||||
Ok(AR {
|
||||
name: "foo".into(),
|
||||
subscript: Node::from_str("7+x").unwrap().into()
|
||||
})
|
||||
);
|
||||
// use crate::ast::ArrayRef as AR;
|
||||
// use Val::*;
|
||||
// use Operator::*;
|
||||
//
|
||||
// // Normal numbers
|
||||
// assert_eq!(
|
||||
// AR::from_str("foo[7]"),
|
||||
// Ok(AR {
|
||||
// name: "foo".into(),
|
||||
// subscript: Number(7).into()
|
||||
// })
|
||||
// );
|
||||
//
|
||||
// // Full exprs (this is the last one of these; the full expr test above covers it
|
||||
// assert_eq!(
|
||||
// AR::from_str("foo[7+x]"),
|
||||
// Ok(AR {
|
||||
// name: "foo".into(),
|
||||
// subscript: Node::from_str("7+x").unwrap().into()
|
||||
// })
|
||||
// );
|
||||
|
||||
// Exprs that are actually arrayrefs
|
||||
assert_eq!(
|
||||
Node::from_str("foo[7]"),
|
||||
Ok(ArrayRef(AR {
|
||||
name: "foo".into(),
|
||||
subscript: Number(7).into()
|
||||
}))
|
||||
);
|
||||
// assert_eq!(
|
||||
// Node::from_str("foo[7]"),
|
||||
// Ok(ArrayRef(AR {
|
||||
// name: "foo".into(),
|
||||
// subscript: Number(7).into()
|
||||
// }))
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_addresses() {
|
||||
assert_eq!(Node::from_str("&foo"), Ok(Node::Address("foo".into())));
|
||||
// assert_eq!(Node::from_str("&foo"), Ok(Node::Address("foo".into())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_calls() {
|
||||
use Node::Number;
|
||||
|
||||
let blah = Call {
|
||||
name: "blah".into(),
|
||||
args: vec![],
|
||||
};
|
||||
|
||||
// Can Node parse a call?
|
||||
assert_eq!(Node::from_str("blah()"), Ok(Node::Call(blah.clone())));
|
||||
|
||||
// Can Statement parse a call?
|
||||
assert_eq!(Statement::from_str("blah();"), Ok(Statement::Call(blah)));
|
||||
|
||||
// Calls with args
|
||||
assert_eq!(
|
||||
Call::from_str("blah(1, 2)"),
|
||||
Ok(Call {
|
||||
name: "blah".into(),
|
||||
args: vec![Rvalue::Expr(Number(1)), Rvalue::Expr(Number(2))]
|
||||
})
|
||||
);
|
||||
|
||||
// Calls with strings
|
||||
assert_eq!(
|
||||
Call::from_str("blah(\"foo\", 2)"),
|
||||
Ok(Call {
|
||||
name: "blah".into(),
|
||||
args: vec![Rvalue::String("foo".into()), Rvalue::Expr(Number(2))]
|
||||
})
|
||||
);
|
||||
}
|
||||
// #[test]
|
||||
// fn parse_calls() {
|
||||
// use Val::Number;
|
||||
//
|
||||
// let blah = Call {
|
||||
// name: "blah".into(),
|
||||
// args: vec![],
|
||||
// };
|
||||
//
|
||||
// // Can Node parse a call?
|
||||
// assert_eq!(Node::from_str("blah()"), Ok(Node::Call(blah.clone())));
|
||||
//
|
||||
// // Can Statement parse a call?
|
||||
// assert_eq!(Statement::from_str("blah();"), Ok(Statement::Call(blah)));
|
||||
//
|
||||
// // Calls with args
|
||||
// // assert_eq!(
|
||||
// // Call::from_str("blah(1, 2)"),
|
||||
// // Ok(Call {
|
||||
// // name: "blah".into(),
|
||||
// // args: vec![Rvalue::Expr(Number(1)), Rvalue::Expr(Number(2))]
|
||||
// // })
|
||||
// // );
|
||||
//
|
||||
// // Calls with strings
|
||||
// // assert_eq!(
|
||||
// // Call::from_str("blah(\"foo\", 2)"),
|
||||
// // Ok(Call {
|
||||
// // name: "blah".into(),
|
||||
// // args: vec![Rvalue::String("foo".into()), Rvalue::Expr(Number(2))]
|
||||
// // })
|
||||
// // );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn parse_return() {
|
||||
assert_eq!(
|
||||
Statement::from_str("return;"),
|
||||
Ok(Statement::Return(Return(None)))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Statement::from_str("return 17;"),
|
||||
Ok(Statement::Return(Return(Some(Node::Number(17)))))
|
||||
);
|
||||
// assert_eq!(
|
||||
// Statement::from_str("return;"),
|
||||
// Ok(Statement::Return(Return(None)))
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// Statement::from_str("return 17;"),
|
||||
// Ok(Statement::Return(Return(Some(Node::Number(17)))))
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -941,7 +951,7 @@ mod test {
|
||||
Statement::from_str("foo = 7;"),
|
||||
Ok(Statement::Assignment(Assignment {
|
||||
lvalue: Lvalue::Name("foo".into()),
|
||||
rvalue: Rvalue::Expr(Node::Number(7))
|
||||
rvalue: Rvalue::Expr(7.into()),
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -950,9 +960,9 @@ mod test {
|
||||
Ok(Assignment {
|
||||
lvalue: Lvalue::ArrayRef(ArrayRef {
|
||||
name: "foo".into(),
|
||||
subscript: Node::Number(45).into(),
|
||||
subscript: 45.into(),
|
||||
}),
|
||||
rvalue: Rvalue::Expr(Node::Number(7))
|
||||
rvalue: Rvalue::Expr(7.into()),
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -974,44 +984,44 @@ mod test {
|
||||
Ok(VarDecl {
|
||||
name: "blah".into(),
|
||||
typename: Some("Foo".into()),
|
||||
size: Some(Node::Number(7)),
|
||||
initial: Some(Node::Number(35)),
|
||||
size: Some(7.into()),
|
||||
initial: Some(35.into()),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_block() {
|
||||
assert_eq!(
|
||||
Block::from_str("{ foo(); bar(); }"),
|
||||
Ok(Block(vec![
|
||||
Statement::Call(Call {
|
||||
name: "foo".into(),
|
||||
args: vec![]
|
||||
}),
|
||||
Statement::Call(Call {
|
||||
name: "bar".into(),
|
||||
args: vec![]
|
||||
}),
|
||||
]))
|
||||
);
|
||||
}
|
||||
// #[test]
|
||||
// fn parse_block() {
|
||||
// assert_eq!(
|
||||
// Block::from_str("{ foo(); bar(); }"),
|
||||
// Ok(Block(vec![
|
||||
// Statement::Call(Call {
|
||||
// name: "foo".into(),
|
||||
// args: vec![]
|
||||
// }),
|
||||
// Statement::Call(Call {
|
||||
// name: "bar".into(),
|
||||
// args: vec![]
|
||||
// }),
|
||||
// ]))
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn parse_conditional() {
|
||||
assert_eq!(
|
||||
Statement::from_str("if(cond) { foo(); }"),
|
||||
Ok(Statement::Conditional(Conditional {
|
||||
condition: Node::from_str("cond").unwrap(),
|
||||
condition: Expr::from_str("cond").unwrap(),
|
||||
body: Block::from_str("{ foo(); }").unwrap(),
|
||||
alternative: None
|
||||
alternative: None,
|
||||
}))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Statement::from_str("if(cond) { foo(); } else { bar(); }"),
|
||||
Ok(Statement::Conditional(Conditional {
|
||||
condition: Node::from_str("cond").unwrap(),
|
||||
condition: Expr::from_str("cond").unwrap(),
|
||||
body: Block::from_str("{ foo(); }").unwrap(),
|
||||
alternative: Some(Block::from_str("{ bar(); }").unwrap()),
|
||||
}))
|
||||
@@ -1023,7 +1033,7 @@ mod test {
|
||||
assert_eq!(
|
||||
Statement::from_str("while(cond) { foo(); }"),
|
||||
Ok(Statement::WhileLoop(WhileLoop {
|
||||
condition: Node::from_str("cond").unwrap(),
|
||||
condition: Expr::from_str("cond").unwrap(),
|
||||
body: Block::from_str("{ foo(); }").unwrap(),
|
||||
}))
|
||||
);
|
||||
@@ -1031,33 +1041,33 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn parse_repeat_loops() {
|
||||
assert_eq!(
|
||||
Statement::from_str("repeat(10) x { foo(x); }"),
|
||||
Ok(Statement::RepeatLoop(RepeatLoop {
|
||||
count: Node::Number(10),
|
||||
name: Some("x".into()),
|
||||
body: Block::from_str("{ foo(x); }").unwrap(),
|
||||
}))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Statement::from_str("repeat(10) { foo(); }"),
|
||||
Ok(Statement::RepeatLoop(RepeatLoop {
|
||||
count: Node::Number(10),
|
||||
name: None,
|
||||
body: Block::from_str("{ foo(); }").unwrap(),
|
||||
}))
|
||||
);
|
||||
// assert_eq!(
|
||||
// Statement::from_str("repeat(10) x { foo(x); }"),
|
||||
// Ok(Statement::RepeatLoop(RepeatLoop {
|
||||
// count: Node::Number(10),
|
||||
// name: Some("x".into()),
|
||||
// body: Block::from_str("{ foo(x); }").unwrap(),
|
||||
// }))
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// Statement::from_str("repeat(10) { foo(); }"),
|
||||
// Ok(Statement::RepeatLoop(RepeatLoop {
|
||||
// count: Node::Number(10),
|
||||
// name: None,
|
||||
// body: Block::from_str("{ foo(); }").unwrap(),
|
||||
// }))
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_program() {
|
||||
assert_eq!(
|
||||
Program::from_str("global foo; struct Point { x, y }"),
|
||||
Ok(Program(vec![
|
||||
Declaration::from_str("global foo;").unwrap(),
|
||||
Declaration::from_str("struct Point { x, y }").unwrap(),
|
||||
]))
|
||||
)
|
||||
// assert_eq!(
|
||||
// Program::from_str("global foo; struct Point { x, y }"),
|
||||
// Ok(Program(vec![
|
||||
// Declaration::from_str("global foo;").unwrap(),
|
||||
// Declaration::from_str("struct Point { x, y }").unwrap(),
|
||||
// ]))
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
+2
-13
@@ -64,19 +64,8 @@ operator = _{
|
||||
| ne
|
||||
}
|
||||
|
||||
expr = { prefix? ~ val ~ suffix* ~ (operator ~ prefix? ~ val ~ suffix*)* }
|
||||
val = {
|
||||
// These are what get evaluated and left on the stack:
|
||||
number
|
||||
| // Literal numbers
|
||||
("(" ~ expr ~ ")")
|
||||
| // Nested parenthesized exprs
|
||||
// Bare literal ids, fn calls, array references
|
||||
// (call, or pointer arithmetic, since ids all reference addresses)
|
||||
// (multiple calls / subscripts aren't allowed; use CALL or collapse to one
|
||||
// subscript)
|
||||
name
|
||||
}
|
||||
expr = { val ~ (operator ~ val)* }
|
||||
val = { prefix* ~ (number | name | "(" ~ expr ~ ")") ~ suffix* }
|
||||
|
||||
suffix = { subscript | arglist | member }
|
||||
subscript = { "[" ~ expr ~ "]" }
|
||||
|
||||
Reference in New Issue
Block a user