Fix function and method calling once and for all.
This commit is contained in:
parent
7be2c3e525
commit
a45d9627dc
7 changed files with 154 additions and 81 deletions
|
@ -35,4 +35,5 @@ impl Error for CompileError {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
TypeRedefined,
|
TypeRedefined,
|
||||||
|
UnknownVariable,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::block::Block;
|
use crate::block::Block;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
use crate::error::ErrorKind;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::ir::{Val, IR};
|
use crate::ir::{Val, IR};
|
||||||
use crate::method::Method;
|
use crate::method::Method;
|
||||||
|
@ -67,9 +68,11 @@ impl Builder {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
NodeType::Call => {
|
NodeType::Call => {
|
||||||
let (name, args) = node.call().unwrap();
|
let (callee, args) = node.call().unwrap();
|
||||||
let location = context.location(&node.location());
|
let location = context.location(&node.location());
|
||||||
let name = context.constant_string(name.value_ref().as_str());
|
|
||||||
|
self.build(callee.clone(), &mut context);
|
||||||
|
let callee = self.pop_ir().unwrap();
|
||||||
|
|
||||||
self.push_block();
|
self.push_block();
|
||||||
for node in args {
|
for node in args {
|
||||||
|
@ -79,8 +82,7 @@ impl Builder {
|
||||||
|
|
||||||
let result_type = context.unknown_type(location.clone());
|
let result_type = context.unknown_type(location.clone());
|
||||||
|
|
||||||
let local = IR::new_get_local(result_type.clone(), location.clone(), name);
|
self.push_ir(IR::new_call(result_type, location, callee, arguments));
|
||||||
self.push_ir(IR::new_call(result_type, location, local, arguments));
|
|
||||||
}
|
}
|
||||||
NodeType::Constructor => {
|
NodeType::Constructor => {
|
||||||
let (ty, props) = node.constructor().unwrap();
|
let (ty, props) = node.constructor().unwrap();
|
||||||
|
@ -239,10 +241,16 @@ impl Builder {
|
||||||
}
|
}
|
||||||
NodeType::Local => {
|
NodeType::Local => {
|
||||||
let name = node.local().unwrap();
|
let name = node.local().unwrap();
|
||||||
|
let name = name.value_ref().as_str();
|
||||||
let location = context.location(&node.location());
|
let location = context.location(&node.location());
|
||||||
let name = context.constant_string(name.value_ref().as_str());
|
let constant_name = context.constant_string(name);
|
||||||
let result_type = self.env_get(&name).unwrap().clone();
|
match self.env_get(&constant_name) {
|
||||||
self.push_ir(IR::new_get_local(result_type, location, name));
|
Some(result_type) => self.push_ir(IR::new_get_local(result_type.clone(), location, constant_name)),
|
||||||
|
None => {
|
||||||
|
let message = format!("Unknown variable {}", name);
|
||||||
|
context.compile_error(&message, location, ErrorKind::UnknownVariable);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NodeType::Map => {
|
NodeType::Map => {
|
||||||
let ty_idx = context.constant_string("Huia.Native.Map");
|
let ty_idx = context.constant_string("Huia.Native.Map");
|
||||||
|
@ -263,6 +271,24 @@ impl Builder {
|
||||||
.collect();
|
.collect();
|
||||||
self.push_ir(IR::new_constant(ty, location, Val::Map(elements)));
|
self.push_ir(IR::new_constant(ty, location, Val::Map(elements)));
|
||||||
}
|
}
|
||||||
|
NodeType::MethodCall => {
|
||||||
|
let location = context.location(&node.location());
|
||||||
|
let (callee, method_name, arguments) = node.method_call().unwrap();
|
||||||
|
let method_name = context.constant_string(method_name.value_ref());
|
||||||
|
|
||||||
|
self.build(callee.clone(), &mut context);
|
||||||
|
let callee = self.pop_ir().unwrap();
|
||||||
|
|
||||||
|
self.push_block();
|
||||||
|
for argument in arguments {
|
||||||
|
self.build(argument.clone(), &mut context);
|
||||||
|
}
|
||||||
|
let arguments = self.pop_block().unwrap().ir();
|
||||||
|
|
||||||
|
let result_type = context.unknown_type(location.clone());
|
||||||
|
|
||||||
|
self.push_ir(IR::new_method_call(result_type, location, callee, method_name, arguments))
|
||||||
|
}
|
||||||
NodeType::PropertyGet => {
|
NodeType::PropertyGet => {
|
||||||
let location = context.location(&node.location());
|
let location = context.location(&node.location());
|
||||||
let name =
|
let name =
|
||||||
|
@ -790,10 +816,12 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_call() {
|
fn test_call() {
|
||||||
let term = Term::input(r#" x(123) "#).unwrap()[0].clone();
|
let terms = Term::input(" let x = 1\n x(123) ").unwrap();
|
||||||
let mut builder = Builder::default();
|
let mut builder = Builder::default();
|
||||||
let mut context = Context::test();
|
let mut context = Context::test();
|
||||||
builder.build(term, &mut context);
|
for term in terms {
|
||||||
|
builder.build(term, &mut context);
|
||||||
|
}
|
||||||
let call = builder.pop_ir().unwrap();
|
let call = builder.pop_ir().unwrap();
|
||||||
assert!(call.is_call());
|
assert!(call.is_call());
|
||||||
assert!(context.find_type("Huia.Native.Integer").is_some());
|
assert!(context.find_type("Huia.Native.Integer").is_some());
|
||||||
|
|
|
@ -90,6 +90,12 @@ impl IR {
|
||||||
IRKind::JumpIfTrue(ref mut test, ..) => {
|
IRKind::JumpIfTrue(ref mut test, ..) => {
|
||||||
improver(test, &mut context);
|
improver(test, &mut context);
|
||||||
}
|
}
|
||||||
|
IRKind::MethodCall(ref mut callee, _, ref mut arguments) => {
|
||||||
|
improver(callee, &mut context);
|
||||||
|
for ref mut argument in arguments {
|
||||||
|
improver(argument, &mut context);
|
||||||
|
}
|
||||||
|
}
|
||||||
IRKind::SetLocal(_, ref mut rhs) => {
|
IRKind::SetLocal(_, ref mut rhs) => {
|
||||||
improver(rhs, &mut context);
|
improver(rhs, &mut context);
|
||||||
}
|
}
|
||||||
|
@ -305,6 +311,20 @@ impl IR {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_method_call(
|
||||||
|
result_type: TyIdx,
|
||||||
|
location: Location,
|
||||||
|
callee: IR,
|
||||||
|
method_name: StringIdx,
|
||||||
|
arguments: Vec<IR>,
|
||||||
|
) -> IR {
|
||||||
|
IR {
|
||||||
|
location,
|
||||||
|
result_type,
|
||||||
|
kind: IRKind::MethodCall(Box::new(callee), method_name, arguments),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_set_local(result_type: TyIdx, location: Location, name: StringIdx, value: IR) -> IR {
|
pub fn new_set_local(result_type: TyIdx, location: Location, name: StringIdx, value: IR) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location,
|
location,
|
||||||
|
@ -356,7 +376,7 @@ impl IR {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum IRKind {
|
pub enum IRKind {
|
||||||
/// Call top of the stack with arguments.
|
/// Call the provided callee with arguments.
|
||||||
Call(Box<IR>, Vec<IR>),
|
Call(Box<IR>, Vec<IR>),
|
||||||
/// A constant value of the specified type.
|
/// A constant value of the specified type.
|
||||||
Constant(Val),
|
Constant(Val),
|
||||||
|
@ -376,6 +396,8 @@ pub enum IRKind {
|
||||||
JumpIfTrue(Box<IR>, BlockIdx),
|
JumpIfTrue(Box<IR>, BlockIdx),
|
||||||
/// A binary operation with LHS and RHS.
|
/// A binary operation with LHS and RHS.
|
||||||
Infix(BinOp, Box<IR>, Box<IR>),
|
Infix(BinOp, Box<IR>, Box<IR>),
|
||||||
|
/// Call the named method on the receiver.
|
||||||
|
MethodCall(Box<IR>, StringIdx, Vec<IR>),
|
||||||
/// Take a value and set it as a local variable.
|
/// Take a value and set it as a local variable.
|
||||||
SetLocal(StringIdx, Box<IR>),
|
SetLocal(StringIdx, Box<IR>),
|
||||||
/// Set properties on an instance, returning the modified instance.
|
/// Set properties on an instance, returning the modified instance.
|
||||||
|
|
|
@ -102,10 +102,6 @@ impl<'a> From<Pair<'a, Rule>> for Binary {
|
||||||
value: Operator::LogicalOr,
|
value: Operator::LogicalOr,
|
||||||
location: InputLocation::from(pair.into_span()),
|
location: InputLocation::from(pair.into_span()),
|
||||||
},
|
},
|
||||||
Rule::method => Binary {
|
|
||||||
value: Operator::Method,
|
|
||||||
location: InputLocation::from(pair.into_span()),
|
|
||||||
},
|
|
||||||
Rule::minus => Binary {
|
Rule::minus => Binary {
|
||||||
value: Operator::Minus,
|
value: Operator::Minus,
|
||||||
location: InputLocation::from(pair.into_span()),
|
location: InputLocation::from(pair.into_span()),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::error::ParseError;
|
||||||
use crate::grammar::{Grammar, Rule};
|
use crate::grammar::{Grammar, Rule};
|
||||||
use crate::input_location::InputLocation;
|
use crate::input_location::InputLocation;
|
||||||
use crate::precedence;
|
use crate::precedence;
|
||||||
use pest::iterators::Pair;
|
use pest::iterators::{Pair, Pairs};
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
|
|
||||||
// I've tried to make Huia terms as simple as possible, there are node types for
|
// I've tried to make Huia terms as simple as possible, there are node types for
|
||||||
|
@ -40,6 +40,7 @@ pub enum NodeType {
|
||||||
Function,
|
Function,
|
||||||
If,
|
If,
|
||||||
Local,
|
Local,
|
||||||
|
MethodCall,
|
||||||
PropertyGet,
|
PropertyGet,
|
||||||
PropertySet,
|
PropertySet,
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ enum Inner {
|
||||||
Atom(Atom),
|
Atom(Atom),
|
||||||
Binary(Binary, Box<Term>, Box<Term>),
|
Binary(Binary, Box<Term>, Box<Term>),
|
||||||
Boolean(Boolean),
|
Boolean(Boolean),
|
||||||
Call(Identifier, Vec<Term>),
|
Call(Box<Term>, Vec<Term>),
|
||||||
Constructor(Ty, Vec<(Identifier, Term)>),
|
Constructor(Ty, Vec<(Identifier, Term)>),
|
||||||
Declaration(Identifier, Box<Term>),
|
Declaration(Identifier, Box<Term>),
|
||||||
Float(Float),
|
Float(Float),
|
||||||
|
@ -67,6 +68,7 @@ enum Inner {
|
||||||
Integer(Integer),
|
Integer(Integer),
|
||||||
Local(Local),
|
Local(Local),
|
||||||
Map(Vec<(Term, Term)>),
|
Map(Vec<(Term, Term)>),
|
||||||
|
MethodCall(Box<Term>, Identifier, Vec<Term>),
|
||||||
PrivateMethod(
|
PrivateMethod(
|
||||||
Identifier,
|
Identifier,
|
||||||
Vec<(Identifier, TypeSpec)>,
|
Vec<(Identifier, TypeSpec)>,
|
||||||
|
@ -146,6 +148,7 @@ impl Term {
|
||||||
Inner::Integer(..) => NodeType::Integer,
|
Inner::Integer(..) => NodeType::Integer,
|
||||||
Inner::Local(..) => NodeType::Local,
|
Inner::Local(..) => NodeType::Local,
|
||||||
Inner::Map(..) => NodeType::Map,
|
Inner::Map(..) => NodeType::Map,
|
||||||
|
Inner::MethodCall(..) => NodeType::MethodCall,
|
||||||
Inner::PropertyGet(..) => NodeType::PropertyGet,
|
Inner::PropertyGet(..) => NodeType::PropertyGet,
|
||||||
Inner::PropertySet(..) => NodeType::PropertySet,
|
Inner::PropertySet(..) => NodeType::PropertySet,
|
||||||
Inner::String(..) => NodeType::String,
|
Inner::String(..) => NodeType::String,
|
||||||
|
@ -187,9 +190,9 @@ impl Term {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&self) -> Option<(&Identifier, &Vec<Term>)> {
|
pub fn call(&self) -> Option<(&Term, &Vec<Term>)> {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Inner::Call(ref name, ref args) => Some((name, args)),
|
Inner::Call(ref callee, ref args) => Some((callee, args)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,6 +260,15 @@ impl Term {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn method_call(&self) -> Option<(&Term, &Identifier, &Vec<Term>)> {
|
||||||
|
match self.inner {
|
||||||
|
Inner::MethodCall(ref callee, ref method_name, ref arguments) => {
|
||||||
|
Some((callee, method_name, arguments))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn private_method(
|
pub fn private_method(
|
||||||
&self,
|
&self,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
|
@ -384,23 +396,28 @@ impl<'a> From<Pair<'a, Rule>> for Term {
|
||||||
location: InputLocation::from(pair.clone()),
|
location: InputLocation::from(pair.clone()),
|
||||||
inner: Inner::Boolean(Boolean::from(pair)),
|
inner: Inner::Boolean(Boolean::from(pair)),
|
||||||
},
|
},
|
||||||
Rule::call => {
|
Rule::call_local => {
|
||||||
let mut inner = pair.clone().into_inner();
|
let mut inner = pair.clone().into_inner();
|
||||||
let name = Identifier::from(inner.next().unwrap());
|
let callee = Term::from(inner.next().unwrap());
|
||||||
let mut args = Vec::new();
|
let mut arguments = Vec::new();
|
||||||
for argument in inner {
|
for argument in inner {
|
||||||
match argument.as_rule() {
|
match argument.as_rule() {
|
||||||
Rule::call_argument => {
|
Rule::call_argument => {
|
||||||
args.push(Term::from(argument.into_inner().next().unwrap()))
|
arguments.push(Term::from(argument.into_inner().next().unwrap()))
|
||||||
}
|
}
|
||||||
_ => unreachable!("Expected method_call argument but found {:?}", argument),
|
_ => unreachable!("Expected call argument but found {:?}", argument),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Term {
|
Term {
|
||||||
location: InputLocation::from(pair),
|
location: InputLocation::from(pair),
|
||||||
inner: Inner::Call(name, args),
|
inner: Inner::Call(Box::new(callee), arguments),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Rule::call_method => {
|
||||||
|
let mut inner = pair.clone().into_inner();
|
||||||
|
let callee = Term::from(inner.next().unwrap());
|
||||||
|
unroll_method_call(callee, inner)
|
||||||
|
}
|
||||||
Rule::typename => Term {
|
Rule::typename => Term {
|
||||||
location: InputLocation::from(pair.clone()),
|
location: InputLocation::from(pair.clone()),
|
||||||
inner: Inner::Ty(Ty::from(pair)),
|
inner: Inner::Ty(Ty::from(pair)),
|
||||||
|
@ -643,11 +660,36 @@ impl<'a> From<Pair<'a, Rule>> for Term {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => panic!("Unexpected pair {:?}", pair),
|
_ => panic!("Unexpected pair {:#?}", pair),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expects to be called with the inner of a `call_method` pair.
|
||||||
|
//
|
||||||
|
// Recursively builds a stack of method call terms from the repeating pattern
|
||||||
|
// off identifier followed by arguments.
|
||||||
|
fn unroll_method_call(callee: Term, mut inner: Pairs<'_, Rule>) -> Term {
|
||||||
|
let method_name = Identifier::from(inner.next().unwrap());
|
||||||
|
let mut arguments = Vec::new();
|
||||||
|
|
||||||
|
while inner.peek().is_some() && inner.peek().unwrap().as_rule() == Rule::call_argument {
|
||||||
|
let argument = inner.next().unwrap();
|
||||||
|
arguments.push(Term::from(argument.into_inner().next().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = Term {
|
||||||
|
location: callee.location().clone(), // this location is wrong.
|
||||||
|
inner: Inner::MethodCall(Box::new(callee), method_name, arguments),
|
||||||
|
};
|
||||||
|
|
||||||
|
if inner.peek().is_some() && inner.peek().unwrap().as_rule() == Rule::ident {
|
||||||
|
return unroll_method_call(result, inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -717,8 +759,9 @@ mod test {
|
||||||
fn test_call() {
|
fn test_call() {
|
||||||
let terms = Term::input("hello(\"Marty\", \"McFly\")").unwrap();
|
let terms = Term::input("hello(\"Marty\", \"McFly\")").unwrap();
|
||||||
assert_eq!(terms[0].node_type(), NodeType::Call);
|
assert_eq!(terms[0].node_type(), NodeType::Call);
|
||||||
let (method_name, arguments) = terms[0].call().unwrap();
|
let (callee, arguments) = terms[0].call().unwrap();
|
||||||
assert_eq!(method_name.value_ref(), "hello");
|
let local = callee.local().unwrap();
|
||||||
|
assert_eq!(local.value_ref(), "hello");
|
||||||
assert_eq!(arguments[0].string().unwrap().value_ref(), "Marty");
|
assert_eq!(arguments[0].string().unwrap().value_ref(), "Marty");
|
||||||
assert_eq!(arguments[1].string().unwrap().value_ref(), "McFly");
|
assert_eq!(arguments[1].string().unwrap().value_ref(), "McFly");
|
||||||
}
|
}
|
||||||
|
@ -726,8 +769,9 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_call_no_args() {
|
fn test_call_no_args() {
|
||||||
let terms = Term::input("hello()").unwrap();
|
let terms = Term::input("hello()").unwrap();
|
||||||
let (method_name, arguments) = terms[0].call().unwrap();
|
let (callee, arguments) = terms[0].call().unwrap();
|
||||||
assert_eq!(method_name.value_ref(), "hello");
|
let local = callee.local().unwrap();
|
||||||
|
assert_eq!(local.value_ref(), "hello");
|
||||||
assert_eq!(arguments.len(), 0);
|
assert_eq!(arguments.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,16 +978,9 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_method_call() {
|
fn test_method_call() {
|
||||||
let terms = Term::input("greeter.hello(\"Marty\", \"McFly\")").unwrap();
|
let terms = Term::input("greeter.hello(\"Marty\", \"McFly\")").unwrap();
|
||||||
assert_eq!(terms[0].node_type(), NodeType::Binary);
|
let (callee, method_name, arguments) = terms[0].method_call().unwrap();
|
||||||
|
let local = callee.local().unwrap();
|
||||||
let (op, lhs, rhs) = terms[0].binary().unwrap();
|
assert_eq!(local.value_ref(), "greeter");
|
||||||
assert_eq!(op.value_ref(), &binary::Operator::Method);
|
|
||||||
|
|
||||||
assert_eq!(lhs.node_type(), NodeType::Local);
|
|
||||||
assert_eq!(lhs.local().unwrap().value_ref(), "greeter");
|
|
||||||
|
|
||||||
assert_eq!(rhs.node_type(), NodeType::Call);
|
|
||||||
let (method_name, arguments) = rhs.call().unwrap();
|
|
||||||
assert_eq!(method_name.value_ref(), "hello");
|
assert_eq!(method_name.value_ref(), "hello");
|
||||||
assert_eq!(arguments[0].string().unwrap().value_ref(), "Marty");
|
assert_eq!(arguments[0].string().unwrap().value_ref(), "Marty");
|
||||||
assert_eq!(arguments[1].string().unwrap().value_ref(), "McFly");
|
assert_eq!(arguments[1].string().unwrap().value_ref(), "McFly");
|
||||||
|
@ -953,43 +990,25 @@ mod test {
|
||||||
fn test_method_call_multi() {
|
fn test_method_call_multi() {
|
||||||
let terms = Term::input("delorean.target_year(1985).accellerate(88)").unwrap();
|
let terms = Term::input("delorean.target_year(1985).accellerate(88)").unwrap();
|
||||||
|
|
||||||
println!("terms = {:#?}", terms);
|
let (callee0, method_name0, arguments0) = terms[0].method_call().unwrap();
|
||||||
|
|
||||||
let (op0, lhs0, rhs0) = terms[0].binary().unwrap();
|
|
||||||
assert_eq!(op0.value_ref(), &binary::Operator::Method);
|
|
||||||
|
|
||||||
assert_eq!(lhs0.node_type(), NodeType::Binary);
|
|
||||||
assert_eq!(rhs0.node_type(), NodeType::Call);
|
|
||||||
|
|
||||||
let (method_name0, arguments0) = rhs0.call().unwrap();
|
|
||||||
assert_eq!(method_name0.value_ref(), "accellerate");
|
assert_eq!(method_name0.value_ref(), "accellerate");
|
||||||
assert_eq!(*arguments0[0].integer().unwrap().value_ref(), 88);
|
assert_eq!(arguments0.len(), 1);
|
||||||
|
assert_eq!(arguments0[0].integer().unwrap().value_ref(), &88);
|
||||||
|
|
||||||
let (op1, lhs1, rhs1) = lhs0.binary().unwrap();
|
let (callee1, method_name1, arguments1) = callee0.method_call().unwrap();
|
||||||
assert_eq!(op1.value_ref(), &binary::Operator::Method);
|
assert_eq!(callee1.local().unwrap().value_ref(), "delorean");
|
||||||
|
|
||||||
assert_eq!(lhs1.node_type(), NodeType::Local);
|
|
||||||
assert_eq!(rhs1.node_type(), NodeType::Call);
|
|
||||||
|
|
||||||
assert_eq!(lhs1.local().unwrap().value_ref(), "delorean");
|
|
||||||
|
|
||||||
let (method_name1, arguments1) = rhs1.call().unwrap();
|
|
||||||
assert_eq!(method_name1.value_ref(), "target_year");
|
assert_eq!(method_name1.value_ref(), "target_year");
|
||||||
assert_eq!(*arguments1[0].integer().unwrap().value_ref(), 1985);
|
assert_eq!(arguments1.len(), 1);
|
||||||
|
assert_eq!(arguments1[0].integer().unwrap().value_ref(), &1985);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_method_call_empty() {
|
fn test_method_call_empty() {
|
||||||
let terms = Term::input("greeter.hello()").unwrap();
|
let terms = Term::input("greeter.hello()").unwrap();
|
||||||
|
|
||||||
let (op, lhs, rhs) = terms[0].binary().unwrap();
|
let (callee, method_name, arguments) = terms[0].method_call().unwrap();
|
||||||
assert_eq!(op.value_ref(), &binary::Operator::Method);
|
let local = callee.local().unwrap();
|
||||||
|
assert_eq!(local.value_ref(), "greeter");
|
||||||
assert_eq!(lhs.node_type(), NodeType::Local);
|
|
||||||
assert_eq!(lhs.local().unwrap().value_ref(), "greeter");
|
|
||||||
|
|
||||||
assert_eq!(rhs.node_type(), NodeType::Call);
|
|
||||||
let (method_name, arguments) = rhs.call().unwrap();
|
|
||||||
assert_eq!(method_name.value_ref(), "hello");
|
assert_eq!(method_name.value_ref(), "hello");
|
||||||
assert_eq!(arguments.len(), 0);
|
assert_eq!(arguments.len(), 0);
|
||||||
}
|
}
|
||||||
|
@ -998,14 +1017,9 @@ mod test {
|
||||||
fn test_method_call_on_typename() {
|
fn test_method_call_on_typename() {
|
||||||
let terms = Term::input("My.Greeter.hello()").unwrap();
|
let terms = Term::input("My.Greeter.hello()").unwrap();
|
||||||
|
|
||||||
let (op, lhs, rhs) = terms[0].binary().unwrap();
|
let (callee, method_name, arguments) = terms[0].method_call().unwrap();
|
||||||
assert_eq!(op.value_ref(), &binary::Operator::Method);
|
let ty = callee.ty().unwrap();
|
||||||
|
assert_eq!(ty.value_ref(), "My.Greeter");
|
||||||
assert_eq!(lhs.node_type(), NodeType::Ty);
|
|
||||||
assert_eq!(lhs.ty().unwrap().value_ref(), "My.Greeter");
|
|
||||||
|
|
||||||
assert_eq!(rhs.node_type(), NodeType::Call);
|
|
||||||
let (method_name, arguments) = rhs.call().unwrap();
|
|
||||||
assert_eq!(method_name.value_ref(), "hello");
|
assert_eq!(method_name.value_ref(), "hello");
|
||||||
assert_eq!(arguments.len(), 0);
|
assert_eq!(arguments.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,22 @@ declaration = { "let" ~ ident ~ assign ~ instance_espression }
|
||||||
|
|
||||||
unary = { unary_operator ~ (literal | local | braced_expression) }
|
unary = { unary_operator ~ (literal | local | braced_expression) }
|
||||||
|
|
||||||
call = { ident ~ call_arguments }
|
// Call types:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// foo() - call a local named `foo` which happens to be a function.
|
||||||
|
// @foo() - call a property named `foo` which happens to be a function.
|
||||||
|
// foo.bar() - call the `bar` method on `foo`.
|
||||||
|
// @foo.bar() - call the `bar` method on the property `foo`.
|
||||||
|
// (1 + 2).bar() - call the `bar` method on the result of an arbitrary expression.
|
||||||
|
// foo.bar().baz() - call the `baz` method on the result of `bar` on `foo`.
|
||||||
|
// ```
|
||||||
|
|
||||||
|
call = _{ call_method | call_local }
|
||||||
|
callable = _{ braced_expression | local | property_get | call_local | typename }
|
||||||
|
call_local = { (local | property_get) ~ call_arguments }
|
||||||
|
call_method = { callable ~ ("." ~ ident ~ call_arguments)+ }
|
||||||
|
|
||||||
call_arguments = _{ "(" ~ (call_argument ~ ("," ~ call_argument)* )? ~ ")" }
|
call_arguments = _{ "(" ~ (call_argument ~ ("," ~ call_argument)* )? ~ ")" }
|
||||||
call_argument = { expression }
|
call_argument = { expression }
|
||||||
|
|
||||||
|
@ -123,7 +138,6 @@ bitwise_and = { "&" }
|
||||||
bitwise_or = { "|" }
|
bitwise_or = { "|" }
|
||||||
bitwise_xor = { "^" }
|
bitwise_xor = { "^" }
|
||||||
assign = { "=" }
|
assign = { "=" }
|
||||||
method = { "." }
|
|
||||||
all_operators = _{
|
all_operators = _{
|
||||||
exponent |
|
exponent |
|
||||||
multiply | divide | modulus |
|
multiply | divide | modulus |
|
||||||
|
@ -134,7 +148,7 @@ all_operators = _{
|
||||||
not_equal | equal |
|
not_equal | equal |
|
||||||
logical_and | logical_or | logical_not |
|
logical_and | logical_or | logical_not |
|
||||||
bitwise_and | bitwise_or | bitwise_xor |
|
bitwise_and | bitwise_or | bitwise_xor |
|
||||||
assign | method
|
assign
|
||||||
}
|
}
|
||||||
binary_operator = _{
|
binary_operator = _{
|
||||||
exponent |
|
exponent |
|
||||||
|
@ -145,8 +159,7 @@ binary_operator = _{
|
||||||
greater_than_or_equal | greater_than |
|
greater_than_or_equal | greater_than |
|
||||||
not_equal | equal |
|
not_equal | equal |
|
||||||
logical_and | logical_or |
|
logical_and | logical_or |
|
||||||
bitwise_and | bitwise_or | bitwise_xor |
|
bitwise_and | bitwise_or | bitwise_xor
|
||||||
method
|
|
||||||
}
|
}
|
||||||
unary_operator = _{ plus | minus | logical_not }
|
unary_operator = _{ plus | minus | logical_not }
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ lazy_static! {
|
||||||
|
|
||||||
fn build_precedence_climber() -> PrecClimber<Rule> {
|
fn build_precedence_climber() -> PrecClimber<Rule> {
|
||||||
PrecClimber::new(vec![
|
PrecClimber::new(vec![
|
||||||
Operator::new(Rule::method, Assoc::Left),
|
|
||||||
Operator::new(Rule::logical_or, Assoc::Left),
|
Operator::new(Rule::logical_or, Assoc::Left),
|
||||||
Operator::new(Rule::logical_and, Assoc::Left),
|
Operator::new(Rule::logical_and, Assoc::Left),
|
||||||
Operator::new(Rule::equal, Assoc::Right) | Operator::new(Rule::not_equal, Assoc::Right),
|
Operator::new(Rule::equal, Assoc::Right) | Operator::new(Rule::not_equal, Assoc::Right),
|
||||||
|
|
Reference in a new issue