Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #122 from MoeOrganization/prakashk/quote-ops
quote operators and more tests
  • Loading branch information
Stevan Little committed Jun 11, 2013
2 parents 03a4f4d + ad53c51 commit 442416a
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 76 deletions.
2 changes: 1 addition & 1 deletion src/main/scala/org/moe/interpreter/guts/Literals.scala
Expand Up @@ -118,7 +118,7 @@ object Literals extends Utils {

case (env, EvalExpressionNode(expr)) => {
val body = MoeParser.parseStuff(expr)
i.eval(r, env, body)
i.eval(r, new MoeEnvironment(Some(env)), body)
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/org/moe/interpreter/guts/Operators.scala
Expand Up @@ -4,6 +4,7 @@ import org.moe.interpreter._
import org.moe.runtime._
import org.moe.runtime.nativeobjects._
import org.moe.ast._
import org.moe.parser._

object Operators extends Utils {

Expand Down Expand Up @@ -36,6 +37,8 @@ object Operators extends Utils {
case (env, BinaryOpNode(lhs: AST, "=~", rhs: AST)) => {
rhs match {
case MatchExpressionNode(pattern, flags) => i.evaluate(env, RegexMatchNode(lhs, pattern, flags))
// TODO: interpolation of the regex variable value
case VariableAccessNode (pattern) => i.evaluate(env, RegexMatchNode(lhs, rhs, StringLiteralNode("")))
case SubstExpressionNode(pattern, replacement, flags) => i.evaluate(env, RegexSubstNode(lhs, pattern, replacement, flags))
}
}
Expand Down
20 changes: 17 additions & 3 deletions src/main/scala/org/moe/parser/MoeProductions.scala
Expand Up @@ -112,7 +112,7 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
lazy val applyOp: PackratParser[AST] = (applyOp <~ ".") ~ identifier ~ ("(" ~> repsep(expression, ",") <~ ")").? ^^ {
case invocant ~ method ~ Some(args) => MethodCallNode(invocant, method, args)
case invocant ~ method ~ None => MethodCallNode(invocant, method, List())
} | regexExpression | subroutineCall
} | quoteExpression | subroutineCall

lazy val subroutineCall: PackratParser[AST] = namespacedIdentifier ~ ("(" ~> repsep(expression, ",") <~ ")") ^^ {
case sub ~ args => SubroutineCallNode(sub, args)
Expand Down Expand Up @@ -143,7 +143,7 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
*/

lazy val simpleExpression: PackratParser[AST] = (
regexExpression
quoteExpression
| arrayIndex
| hashIndex
| hash
Expand Down Expand Up @@ -318,9 +318,23 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
case pattern ~ replacement ~ Some(flags) => SubstExpressionNode(RegexLiteralNode(pattern), StringLiteralNode(replacement), flags)
}

private def splitString(str: String) = str.split(" ").map(s => StringLiteralNode(s)).toList

def quoteOp = "q[qwx]?".r ~ quotedString ^^ {
case "qq" ~ expr => MoeStringParser.interpolateStr(expr)
case "qw" ~ expr => ArrayLiteralNode(splitString(expr))
// this is naive; doesn't handle embedded spaces in args
case "qx" ~ expr => SubroutineCallNode("system", splitString(expr))
case "q" ~ expr => StringLiteralNode(expr)
}
def quoteRegexOp = "qr" ~ quotedString ~ opt(regexModifiers) ^^ {
case "qr" ~ expr ~ Some(flags) => MatchExpressionNode(RegexLiteralNode(expr), flags)
case "qr" ~ expr ~ None => MatchExpressionNode(RegexLiteralNode(expr), StringLiteralNode(""))
}

// TODO: tr (transliteration) operator

def regexExpression = (substExpression_2 | substExpression_1 | matchExpression)
def quoteExpression = (substExpression_2 | substExpression_1 | matchExpression | quoteOp | quoteRegexOp)

def matchOp = simpleExpression ~ "=~" ~ expression ^^ {
case left ~ op ~ right => BinaryOpNode(left, op, right)
Expand Down
5 changes: 5 additions & 0 deletions src/main/scala/org/moe/parser/MoeStringParser.scala
Expand Up @@ -24,6 +24,7 @@ object MoeStringParser extends MoeQuoteParser {
| methodName
| scalar
| chars
| delimiters
)

private def block = quoted('{') ^^ EvalExpressionNode
Expand Down Expand Up @@ -58,8 +59,12 @@ object MoeStringParser extends MoeQuoteParser {
case name => VariableAccessNode("$" + name)
}

// any character other than the delimiters
private def chars = """(\\.|[^$@%&{])+""".r ^^ StringLiteralNode

// delimiters themselves
private def delimiters = """[$@%&{]+""".r ^^ StringLiteralNode

private def getEntryPoint: Parser[AST] = string

def interpolateStr(input: String): StringSequenceNode = {
Expand Down
43 changes: 42 additions & 1 deletion t/001-literals/010-interpolation.t
Expand Up @@ -14,7 +14,7 @@ use Test::More;

{
my @array = 1..3;
is("@array[]", "[1, 2, 3]", "... @array[] interpolates");
is("@array[]", "[1, 2, 3]", '... @array[] interpolates');
is("@array", "@array", '... @array (without brackets) doesnt interpolate');
is("@array[1]", "2", '... @array element interpolates');
}
Expand Down Expand Up @@ -43,4 +43,45 @@ use Test::More;
is("$a.foo", '$a.foo', '... method call $a.foo (without brackets) doesnt interpolate');
}
{
my $a = 2; my $b = 21;
is("The Answer = { $a * $b }", "The Answer = 42", '... code block interpolates');
is("The Answer = \{ { $a * $b } \}", 'The Answer = { 42 }', '... escaped braces dont make code block');
}
# tests adapted from https://github.com/perl6/roast/blob/master/S02-literals/misc-interpolation.t
my $world = "World";
my $number = 1;
my @list = [1, 2];
my %hash = {1 => 2};
sub func { "func-y town" }
sub func_w_args($x,$y) { "[$x][$y]" }
# Double quotes
is("Hello $world", 'Hello World', '... double quoted string interpolation works');
is("@list[] 3 4", '[1, 2] 3 4', '... double quoted list interpolation works');
is("@list 3 4", '@list 3 4', '... array without empty square brackets does not interpolate');
is("%hash{}", '{1 => 2}', '... hash interpolation works');
is("%hash", '%hash', '... hash interpolation does not work if not followed by {}');
is("Wont you take me to &func()", 'Wont you take me to func-y town', '... closure interpolation');
is("2 + 2 = { 2+2 }", '2 + 2 = 4', '... double quoted closure interpolation works');
is("&func() is where I live", 'func-y town is where I live', "... make sure function interpolation doesn't eat all trailing whitespace");
is("$number {$number}", '1 1', '... number inside and outside closure works');
is("$number {my $number=2}", '1 2', '... local version of number in closure works');
is("$number {my $number=2} $number", '1 2 1', '... original number still available after local version in closure: works' );
is("&func. () is where I live", '&func. () is where I live', '... "&func. ()" should not interpolate');
# Single quotes
is('Hello $world', 'Hello $world', '... single quoted string interpolation does not work (which is correct)');
is('2 + 2 = { 2+2 }', '2 + 2 = { 2+2 }', '... single quoted closure interpolation does not work (which is correct)');
is('$world @list[] %hash{} &func()', '$world @list[] %hash{} &func()', '... single quoted string interpolation does not work (which is correct)');
# FIXME: this test fails because in Perl6, closures don't interpolated
# when braces are used as delimiters. Moe currently doesn't make this
# distinction
# is(qq{a{chr 98}c}, 'a{chr 98}c', "curly brace delimiters interfere with closure interpolation");
done_testing();
16 changes: 8 additions & 8 deletions t/005-classes/002-inheritance.t
Expand Up @@ -38,20 +38,20 @@ class CheckingAccount extends BankAccount {

is($savings.balance, 100, '... got the expected balance');

eval("$savings.balance(100)");
eval('$savings.balance(100)');
ok($!.defined, '... accessor is read only');

eval("$savings.deposit(200)");
eval('$savings.deposit(200)');
ok(not($!.defined), '... deposit worked');

is($savings.balance, 300, '... got the expected (altered) balance');

eval("$savings.withdraw(50)");
eval('$savings.withdraw(50)');
ok(not($!.defined), '... withdraw worked');

is($savings.balance, 250, '... got the expected (altered) balance');

eval("$savings.withdraw(350)");
eval('$savings.withdraw(350)');
ok($!.defined, '... withdraw worked');

is($savings.balance, 250, '... got the expected (unaltered) balance');
Expand All @@ -71,23 +71,23 @@ class CheckingAccount extends BankAccount {
is($checking.balance, 250, '... got the expected (checking) balance');
is($savings.balance, 100, '... got the expected (savings) balance');

eval("$checking.withdraw(200)");
eval('$checking.withdraw(200)');
ok(not($!.defined), '... withdraw did not die');

is($checking.balance, 50, '... got the expected (checking) balance');
is($savings.balance, 100, '... got the expected (savings) balance');

eval("$checking.withdraw(100)");
eval('$checking.withdraw(100)');
ok(not($!.defined), '... withdraw did not die');

is($checking.balance, 0, '... got the expected (checking) balance');
is($savings.balance, 50, '... got the expected (savings) balance');

eval("$checking.withdraw(100)");
eval('$checking.withdraw(100)');
ok($!.defined, '... withdraw did die this time');

is($checking.balance, 0, '... got the expected unaltered (checking) balance');
is($savings.balance, 50, '... got the expected unaltered (savings) balance');
}

done_testing();
done_testing();
12 changes: 6 additions & 6 deletions t/006-statements/001-if.t
@@ -1,37 +1,37 @@
use Test::More;

{
my $r = eval("if (true) { 10 }");
my $r = eval('if (true) { 10 }');
ok(not($!.defined), '... the statement worked correctly');
is($r, 10, '... got the expected value');
}

{
my $r = eval("if (false) { 10 } else { 20 }");
my $r = eval('if (false) { 10 } else { 20 }');
ok(not($!.defined), '... the statement worked correctly');
is($r, 20, '... got the expected value');
}

{
my $r = eval("if (false) { 10 } elsif (true) { 20 }");
my $r = eval('if (false) { 10 } elsif (true) { 20 }');
ok(not($!.defined), '... the statement worked correctly');
is($r, 20, '... got the expected value');
}

{
my $r = eval("if (false) { 10 } elsif (false) { 20 } else { 30 }");
my $r = eval('if (false) { 10 } elsif (false) { 20 } else { 30 }');
ok(not($!.defined), '... the statement worked correctly');
is($r, 30, '... got the expected value');
}

{
my $r = eval("10 if true");
my $r = eval('10 if true');
ok(not($!.defined), '... the modifier statement worked correctly');
is($r, 10, '... got the expected value');
}

{
my $r = eval("my $a = 0; $a = $a + 1 if $a < 1; $a");
my $r = eval('my $a = 0; $a = $a + 1 if $a < 1; $a');
ok(not($!.defined), '... the modifier statement worked correctly');
is($r, 1, '... got the expected value');
}
Expand Down
24 changes: 12 additions & 12 deletions t/006-statements/002-for.t
Expand Up @@ -2,32 +2,32 @@ use Test::More;

{
my $r = eval(
"my $x = 0;" ~
"my @x = 1 .. 10;" ~
"for (my $i = 0; $i < @x.length; $i = $i + 1) {" ~
"$x = $x + 1;" ~
"}" ~
"$x"
'my $x = 0;' ~
'my @x = 1 .. 10;' ~
'for (my $i = 0; $i < @x.length; $i = $i + 1) {' ~
'$x = $x + 1;' ~
'}' ~
'$x'
);
ok(not($!.defined), '... the statement worked correctly');
is($r, 10, '... got the expected value');
}

{
my $r = eval(
"my $a = 0;" ~
"$a = $a + 1 for 1..5;" ~
"$a"
'my $a = 0;' ~
'$a = $a + 1 for 1..5;' ~
'$a'
);
ok(not($!.defined), '... the modifier statement with range worked correctly');
is($r, 5, '... got the expected value');
}

{
my $r = eval(
"my $a = 0;" ~
"$a = $a + $_ for [1,2,3];" ~
"$a"
'my $a = 0;' ~
'$a = $a + $_ for [1,2,3];' ~
'$a'
);
ok(not($!.defined), '... the modifier statement with list worked correctly');
is($r, 6, '... got the expected value');
Expand Down
34 changes: 17 additions & 17 deletions t/006-statements/003-foreach.t
Expand Up @@ -2,44 +2,44 @@ use Test::More;

{
my $r = eval(
"my $x = 0;" ~
"my @x = 1 .. 10;" ~
"foreach my $i (@x) {" ~
"$x = $x + 1;" ~
"}" ~
"$x"
'my $x = 0;' ~
'my @x = 1 .. 10;' ~
'foreach my $i (@x) {' ~
'$x = $x + 1;' ~
'}' ~
'$x'
);
ok(not($!.defined), '... the statement worked correctly');
is($r, 10, '... got the expected value');
}

{
my $r = eval(
"my $x = 0;" ~
"foreach my $i (1 .. 10) {" ~
"$x = $x + $i;" ~
"}" ~
"$x"
'my $x = 0;' ~
'foreach my $i (1 .. 10) {' ~
'$x = $x + $i;' ~
'}' ~
'$x'
);
ok(not($!.defined), '... the statement worked correctly');
is($r, 55, '... got the expected value');
}

{
my $r = eval(
"my $a = 0;" ~
"$a = $a + 1 foreach 1..5;" ~
"$a"
'my $a = 0;' ~
'$a = $a + 1 foreach 1..5;' ~
'$a'
);
ok(not($!.defined), '... the modifier statement with range worked correctly');
is($r, 5, '... got the expected value');
}

{
my $r = eval(
"my $a = 0;" ~
"$a = $a + $_ foreach [1,2,3];" ~
"$a"
'my $a = 0;' ~
'$a = $a + $_ foreach [1,2,3];' ~
'$a'
);
ok(not($!.defined), '... the modifier statement with list worked correctly');
is($r, 6, '... got the expected value');
Expand Down

0 comments on commit 442416a

Please sign in to comment.