Skip to content

Commit 64fe439

Browse files
authored
Feature: Python and, or & unary operators (#19)
* Renamed OperatorCode to BasicOperatorCode * Removed LAnd & LOr from BasicOperatorCode list * Added and tested unary operators (`-x`, `+x`, `~x`) * Added support of `not x` * Added and tested peeking jumps * Added and tested compiling `and` & `or` operators * Added integration tests for `and` & `or` operators * Changed VarSet/Get.ToString from 'push/pop' to 'set/get' * Upped python module version to `0.4.3`
1 parent dde835e commit 64fe439

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+651
-275
lines changed

scripts/slack_notify_testrunner.sh

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,28 +87,42 @@ fi
8787
commitRange="$CIRCLE_SHA1^...$CIRCLE_SHA1"
8888
echo "Looking at range $commitRange"
8989

90-
if [ "${CIRCLE_API_KEY:-}" ] && [ "${CIRCLE_PREVIOUS_BUILD_NUM:-}" ]
90+
if [ "${CIRCLE_API_KEY:-}" ]
9191
then
92-
echo "Got CircleCI API key and previous build. Let's find out the SHA1 of last commit..."
93-
curlResult="$(curl -su $CIRCLE_API_KEY: \
94-
https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/$CIRCLE_PREVIOUS_BUILD_NUM \
95-
| grep "\"vcs_revision\" :")"
96-
# $ "vcs_revision" : "c727a7309ff289d7c38465d7f07d7011658aa4b2",
97-
curlRegex='vcs_revision.*"(.+)"'
98-
99-
if [[ $curlResult =~ $curlRegex ]] && [[ "${BASH_REMATCH[1]:-}" ]]
92+
if [ "${CIRCLE_PREVIOUS_BUILD_NUM:-}" ]
10093
then
101-
commitPrevSHA=${BASH_REMATCH[1]}
102-
echo "Found commit '$commitPrevSHA'"
103-
if [ "$commitPrevSHA" == "$CIRCLE_SHA1" ]
94+
echo "Got CircleCI API key and previous build. Let's find out the SHA1 of last commit..."
95+
curlResult="$(curl -su $CIRCLE_API_KEY: \
96+
https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/$CIRCLE_PREVIOUS_BUILD_NUM \
97+
| grep "\"vcs_revision\" :")"
98+
# $ "vcs_revision" : "c727a7309ff289d7c38465d7f07d7011658aa4b2",
99+
curlRegex='vcs_revision.*"(.+)"'
100+
101+
if [[ $curlResult =~ $curlRegex ]] && [[ "${BASH_REMATCH[1]:-}" ]]
104102
then
105-
echo "Oh wait, it's the same commit. Leaving range as-is."
103+
commitPrevSHA=${BASH_REMATCH[1]}
104+
echo "Found commit '$commitPrevSHA'"
105+
if [ "$commitPrevSHA" == "$CIRCLE_SHA1" ]
106+
then
107+
echo "Oh wait, it's the same commit. Leaving range as-is."
108+
else
109+
commitRange="$commitPrevSHA...$CIRCLE_SHA1"
110+
echo "Instead looking at range '$commitRange'"
111+
fi
106112
else
107-
commitRange="$commitPrevSHA...$CIRCLE_SHA1"
108-
echo "Instead looking at range $commitRange"
113+
echo "No match for previous commit."
109114
fi
110115
else
111-
echo "No match for previous commit."
116+
echo "No previous build. Assuming new branch..."
117+
baseBranch=$(git show-branch -a \
118+
| grep '\*' \
119+
| grep -v `git rev-parse --abbrev-ref HEAD` \
120+
| head -n1 \
121+
| sed 's/.*\[\(.*\)\].*/\1/' \
122+
| sed 's/[\^~].*//')
123+
echo "Found base branch '$baseBranch'"
124+
commitRange="$baseBranch...$CIRCLE_SHA1"
125+
echo "Instead looking at range '$commitRange'"
112126
fi
113127
fi
114128

src/Mellis.Lang.Python3.Tests/Compiler/CompileExpressionTests.cs

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,29 @@ namespace Mellis.Lang.Python3.Tests.Compiler
2121
public class CompileExpressionTests
2222
{
2323
[DataTestMethod]
24-
[DataRow(typeof(ArithmeticAdd), OperatorCode.AAdd, DisplayName = "comp op +")]
25-
[DataRow(typeof(ArithmeticSubtract), OperatorCode.ASub, DisplayName = "comp op -")]
26-
[DataRow(typeof(ArithmeticMultiply), OperatorCode.AMul, DisplayName = "comp op *")]
27-
[DataRow(typeof(ArithmeticDivide), OperatorCode.ADiv, DisplayName = "comp op /")]
28-
[DataRow(typeof(ArithmeticFloor), OperatorCode.AFlr, DisplayName = "comp op //")]
29-
[DataRow(typeof(ArithmeticModulus), OperatorCode.AMod, DisplayName = "comp op %")]
30-
[DataRow(typeof(ArithmeticPower), OperatorCode.APow, DisplayName = "comp op **")]
31-
[DataRow(typeof(BinaryAnd), OperatorCode.BAnd, DisplayName = "comp op a&b")]
32-
[DataRow(typeof(BinaryLeftShift), OperatorCode.BLsh, DisplayName = "comp op a<<b")]
33-
[DataRow(typeof(BinaryRightShift), OperatorCode.BRsh, DisplayName = "comp op a>>b")]
34-
[DataRow(typeof(BinaryOr), OperatorCode.BOr, DisplayName = "comp op a|b")]
35-
[DataRow(typeof(BinaryXor), OperatorCode.BXor, DisplayName = "comp op a^b")]
36-
[DataRow(typeof(CompareEquals), OperatorCode.CEq, DisplayName = "comp op a==b")]
37-
[DataRow(typeof(CompareNotEquals), OperatorCode.CNEq, DisplayName = "comp op a!=b")]
38-
[DataRow(typeof(CompareGreaterThan), OperatorCode.CGt, DisplayName = "comp op a>b")]
39-
[DataRow(typeof(CompareGreaterThanOrEqual), OperatorCode.CGtEq, DisplayName = "comp op a>=b")]
40-
[DataRow(typeof(CompareLessThan), OperatorCode.CLt, DisplayName = "comp op a<b")]
41-
[DataRow(typeof(CompareLessThanOrEqual), OperatorCode.CLtEq, DisplayName = "comp op a<=b")]
42-
[DataRow(typeof(CompareIn), OperatorCode.CIn, DisplayName = "comp op a in b")]
43-
[DataRow(typeof(CompareInNot), OperatorCode.CNIn, DisplayName = "comp op a not in b")]
44-
[DataRow(typeof(CompareIs), OperatorCode.CIs, DisplayName = "comp op a is b")]
45-
[DataRow(typeof(CompareIsNot), OperatorCode.CIsN, DisplayName = "comp op a is not b")]
46-
[DataRow(typeof(LogicalAnd), OperatorCode.LAnd, DisplayName = "comp op a&&b")]
47-
[DataRow(typeof(LogicalOr), OperatorCode.LOr, DisplayName = "comp op a||b")]
48-
public void CompileBinaryTests(Type operatorType, OperatorCode expectedCode)
24+
[DataRow(typeof(ArithmeticAdd), BasicOperatorCode.AAdd, DisplayName = "comp op +")]
25+
[DataRow(typeof(ArithmeticSubtract), BasicOperatorCode.ASub, DisplayName = "comp op -")]
26+
[DataRow(typeof(ArithmeticMultiply), BasicOperatorCode.AMul, DisplayName = "comp op *")]
27+
[DataRow(typeof(ArithmeticDivide), BasicOperatorCode.ADiv, DisplayName = "comp op /")]
28+
[DataRow(typeof(ArithmeticFloor), BasicOperatorCode.AFlr, DisplayName = "comp op //")]
29+
[DataRow(typeof(ArithmeticModulus), BasicOperatorCode.AMod, DisplayName = "comp op %")]
30+
[DataRow(typeof(ArithmeticPower), BasicOperatorCode.APow, DisplayName = "comp op **")]
31+
[DataRow(typeof(BinaryAnd), BasicOperatorCode.BAnd, DisplayName = "comp op a&b")]
32+
[DataRow(typeof(BinaryLeftShift), BasicOperatorCode.BLsh, DisplayName = "comp op a<<b")]
33+
[DataRow(typeof(BinaryRightShift), BasicOperatorCode.BRsh, DisplayName = "comp op a>>b")]
34+
[DataRow(typeof(BinaryOr), BasicOperatorCode.BOr, DisplayName = "comp op a|b")]
35+
[DataRow(typeof(BinaryXor), BasicOperatorCode.BXor, DisplayName = "comp op a^b")]
36+
[DataRow(typeof(CompareEquals), BasicOperatorCode.CEq, DisplayName = "comp op a==b")]
37+
[DataRow(typeof(CompareNotEquals), BasicOperatorCode.CNEq, DisplayName = "comp op a!=b")]
38+
[DataRow(typeof(CompareGreaterThan), BasicOperatorCode.CGt, DisplayName = "comp op a>b")]
39+
[DataRow(typeof(CompareGreaterThanOrEqual), BasicOperatorCode.CGtEq, DisplayName = "comp op a>=b")]
40+
[DataRow(typeof(CompareLessThan), BasicOperatorCode.CLt, DisplayName = "comp op a<b")]
41+
[DataRow(typeof(CompareLessThanOrEqual), BasicOperatorCode.CLtEq, DisplayName = "comp op a<=b")]
42+
[DataRow(typeof(CompareIn), BasicOperatorCode.CIn, DisplayName = "comp op a in b")]
43+
[DataRow(typeof(CompareInNot), BasicOperatorCode.CNIn, DisplayName = "comp op a not in b")]
44+
[DataRow(typeof(CompareIs), BasicOperatorCode.CIs, DisplayName = "comp op a is b")]
45+
[DataRow(typeof(CompareIsNot), BasicOperatorCode.CIsN, DisplayName = "comp op a is not b")]
46+
public void CompileBasicBinaryTests(Type operatorType, BasicOperatorCode expectedCode)
4947
{
5048
// Arrange
5149
var compiler = new PyCompiler();
@@ -57,7 +55,7 @@ public void CompileBinaryTests(Type operatorType, OperatorCode expectedCode)
5755
out Mock<ExpressionNode> exprRhsMock,
5856
out NopOp exprRhsOp);
5957

60-
var opNode = (BinaryOperator) Activator.CreateInstance(operatorType,
58+
var opNode = (BinaryOperator)Activator.CreateInstance(operatorType,
6159
exprLhsMock.Object, exprRhsMock.Object);
6260

6361
// Act
@@ -74,11 +72,11 @@ public void CompileBinaryTests(Type operatorType, OperatorCode expectedCode)
7472
}
7573

7674
[DataTestMethod]
77-
[DataRow(typeof(ArithmeticNegative), OperatorCode.ANeg, DisplayName = "comp op +b")]
78-
[DataRow(typeof(ArithmeticPositive), OperatorCode.APos, DisplayName = "comp op -b")]
79-
[DataRow(typeof(BinaryNot), OperatorCode.BNot, DisplayName = "comp op ~b")]
80-
[DataRow(typeof(LogicalNot), OperatorCode.LNot, DisplayName = "comp op !b")]
81-
public void CompileUnaryTests(Type operatorType, OperatorCode expectedCode)
75+
[DataRow(typeof(ArithmeticNegative), BasicOperatorCode.ANeg, DisplayName = "comp op +b")]
76+
[DataRow(typeof(ArithmeticPositive), BasicOperatorCode.APos, DisplayName = "comp op -b")]
77+
[DataRow(typeof(BinaryNot), BasicOperatorCode.BNot, DisplayName = "comp op ~b")]
78+
[DataRow(typeof(LogicalNot), BasicOperatorCode.LNot, DisplayName = "comp op !b")]
79+
public void CompileBasicUnaryTests(Type operatorType, BasicOperatorCode expectedCode)
8280
{
8381
// Arrange
8482
var compiler = new PyCompiler();
@@ -87,7 +85,7 @@ public void CompileUnaryTests(Type operatorType, OperatorCode expectedCode)
8785
out Mock<ExpressionNode> exprMock,
8886
out NopOp exprOp);
8987

90-
var opNode = (UnaryOperator) Activator.CreateInstance(operatorType,
88+
var opNode = (UnaryOperator)Activator.CreateInstance(operatorType,
9189
SourceReference.ClrSource,
9290
exprMock.Object);
9391

@@ -102,6 +100,60 @@ public void CompileUnaryTests(Type operatorType, OperatorCode expectedCode)
102100
exprMock.Verify(o => o.Compile(compiler), Times.Once);
103101
}
104102

103+
[TestMethod]
104+
public void CompileShortCircuitAnd()
105+
{
106+
CompileShortCircuit(typeof(LogicalAnd), jumpOverRhsIfTrue: false);
107+
}
108+
109+
[TestMethod]
110+
public void CompileShortCircuitOr()
111+
{
112+
CompileShortCircuit(typeof(LogicalOr), jumpOverRhsIfTrue: true);
113+
}
114+
115+
private static void CompileShortCircuit(Type operatorType, bool jumpOverRhsIfTrue)
116+
{
117+
// Arrange
118+
var compiler = new PyCompiler();
119+
compiler.CreateAndSetup(
120+
out Mock<ExpressionNode> exprLhsMock,
121+
out NopOp exprLhsOp);
122+
123+
compiler.CreateAndSetup(
124+
out Mock<ExpressionNode> exprRhsMock,
125+
out NopOp exprRhsOp);
126+
127+
var opNode = (BinaryOperator)Activator.CreateInstance(operatorType,
128+
exprLhsMock.Object, exprRhsMock.Object);
129+
130+
// Act
131+
opNode.Compile(compiler);
132+
133+
// Assert
134+
Assert.That.IsExpectedOpCode(compiler, 0, exprLhsOp);
135+
Assert.That.IsOpCode<VarPop>(compiler, 2);
136+
Assert.That.IsExpectedOpCode(compiler, 3, exprRhsOp);
137+
138+
Assert.AreEqual(4, compiler.Count);
139+
140+
if (jumpOverRhsIfTrue)
141+
{
142+
var jump = Assert.That.IsOpCode<JumpIfTrue>(compiler, 1);
143+
Assert.IsTrue(jump.Peek);
144+
Assert.AreEqual(4, jump.Target);
145+
}
146+
else
147+
{
148+
var jump = Assert.That.IsOpCode<JumpIfFalse>(compiler, 1);
149+
Assert.IsTrue(jump.Peek);
150+
Assert.AreEqual(4, jump.Target);
151+
}
152+
153+
exprLhsMock.Verify(o => o.Compile(compiler), Times.Once);
154+
exprRhsMock.Verify(o => o.Compile(compiler), Times.Once);
155+
}
156+
105157
[TestMethod]
106158
public void IdentifierTest()
107159
{
@@ -154,7 +206,7 @@ void Action()
154206
}
155207

156208
// Act
157-
var ex = Assert.ThrowsException<SyntaxUncompilableException>((Action) Action);
209+
var ex = Assert.ThrowsException<SyntaxUncompilableException>((Action)Action);
158210

159211
// Assert
160212
Assert.That.ErrorSyntaxFormatArgsEqual(ex,
@@ -202,9 +254,8 @@ public void FunctionCallSingleArgTest()
202254
const int expectedLiteral = 5;
203255
const string expectedIdentifier = "foo";
204256

205-
var args = new []
206-
{
207-
new LiteralInteger(SourceReference.ClrSource, expectedLiteral),
257+
var args = new[] {
258+
new LiteralInteger(SourceReference.ClrSource, expectedLiteral),
208259
};
209260

210261
var callNode = new FunctionCall(
@@ -241,10 +292,9 @@ public void FunctionCallMultipleArgsTest()
241292

242293
const string expectedIdentifier = "foo";
243294

244-
var args = new ExpressionNode[]
245-
{
295+
var args = new ExpressionNode[] {
246296
new LiteralInteger(SourceReference.ClrSource, expectedLiteral1),
247-
new LiteralString(SourceReference.ClrSource, expectedLiteral2),
297+
new LiteralString(SourceReference.ClrSource, expectedLiteral2),
248298
new LiteralBoolean(SourceReference.ClrSource, expectedLiteral3),
249299
};
250300

@@ -260,7 +310,7 @@ public void FunctionCallMultipleArgsTest()
260310
// Assert
261311
var foo = Assert.That.IsOpCode<VarGet>(compiler, 0);
262312
Assert.AreEqual(expectedIdentifier, foo.Identifier);
263-
313+
264314
Assert.That.IsPushLiteralOpCode(expectedLiteral1, compiler, 1);
265315
Assert.That.IsPushLiteralOpCode(expectedLiteral2, compiler, 2);
266316
Assert.That.IsPushLiteralOpCode(expectedLiteral3, compiler, 3);

src/Mellis.Lang.Python3.Tests/CompilerTestingHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public static Literal<TValue> IsPushLiteralOpCode<TValue>(
2525
return literal;
2626
}
2727

28-
public static void IsBinaryOpCode(this Assert assert, OperatorCode expectedCode, PyCompiler compiler, int index)
28+
public static void IsBinaryOpCode(this Assert assert, BasicOperatorCode expectedCode, PyCompiler compiler, int index)
2929
{
3030
if (index >= compiler.Count)
3131
{

src/Mellis.Lang.Python3.Tests/IntegrationTests.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,54 @@ public void ProcessArithmeticAddTest()
137137
errorCatcher.AssertNoErrors();
138138
}
139139

140+
[TestMethod]
141+
public void ProcessLogicAndTest()
142+
{
143+
// Arrange
144+
const string code = "a = 1024 and 999\n" +
145+
"b = '' and 42";
146+
var processor = (PyProcessor)new PyCompiler().Compile(code, errorCatcher);
147+
148+
// Act
149+
processor.WalkInstruction(); // to enter first op
150+
processor.WalkLine();
151+
processor.WalkLine();
152+
153+
// Assert
154+
var a = processor.GetVariable("a");
155+
Assert.That.ScriptTypeEqual(999, a);
156+
157+
var b = processor.GetVariable("b");
158+
Assert.That.ScriptTypeEqual("", b);
159+
160+
Assert.AreEqual(ProcessState.Ended, processor.State);
161+
errorCatcher.AssertNoErrors();
162+
}
163+
164+
[TestMethod]
165+
public void ProcessLogicOrTest()
166+
{
167+
// Arrange
168+
const string code = "a = 1024 or 999\n" +
169+
"b = '' or 42";
170+
var processor = (PyProcessor)new PyCompiler().Compile(code, errorCatcher);
171+
172+
// Act
173+
processor.WalkInstruction(); // to enter first op
174+
processor.WalkLine();
175+
processor.WalkLine();
176+
177+
// Assert
178+
var a = processor.GetVariable("a");
179+
Assert.That.ScriptTypeEqual(1024, a);
180+
181+
var b = processor.GetVariable("b");
182+
Assert.That.ScriptTypeEqual(42, b);
183+
184+
Assert.AreEqual(ProcessState.Ended, processor.State);
185+
errorCatcher.AssertNoErrors();
186+
}
187+
140188
[TestMethod]
141189
public void ProcessIfTrueTest()
142190
{

0 commit comments

Comments
 (0)