Skip to content

Commit 4b72fd2

Browse files
committed
Merge branch 'develop'
2 parents bf95501 + 7ae4ed4 commit 4b72fd2

File tree

5 files changed

+109
-96
lines changed

5 files changed

+109
-96
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ before_install:
2020
- git fetch --tags
2121

2222
install:
23-
- sbt compile
23+
- sbt ++$TRAVIS_SCALA_VERSION compile
2424

2525
before_script:
2626
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter

src/main/scala/com/github/tomerghelber/mathematica/ast/ASTNode.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ trait ASTNode
44

55
case class FunctionNode(name: ASTNode, arguments: Seq[ASTNode]) extends ASTNode
66

7-
object PartNode extends ApplyBinaryFunctionNode with UnapplyFunctionNode {
7+
object PartNode extends ApplyManyFunctionNode with UnapplyFunctionNode {
88
protected val name: String = "Part"
99
}
1010

@@ -40,7 +40,7 @@ object NotExistsNode extends ApplyBinaryFunctionNode with UnapplyFunctionNode {
4040
object EquivalentNode extends ApplyBinaryFunctionNode with UnapplyFunctionNode {
4141
protected val name = "Equivalent"
4242
}
43-
object ListNode extends UnapplyFunctionNode {
43+
object ListNode extends ApplyManyFunctionNode with UnapplyFunctionNode {
4444
protected val name = "List"
4545
}
4646

@@ -52,11 +52,18 @@ case class SpanNode(expr1: ASTNode, expr3: ASTNode, expr2: ASTNode) extends ASTN
5252

5353
trait ApplyUnaryFunctionNode {
5454
protected def name: String
55-
def apply(first: ASTNode): FunctionNode = FunctionNode(SymbolNode(name), Seq(first))
55+
def apply(node: ASTNode): FunctionNode = createUnary(node)
56+
val createUnary: ASTNode => FunctionNode = first => FunctionNode(SymbolNode(name), Seq(first))
5657
}
5758
trait ApplyBinaryFunctionNode {
5859
protected def name: String
59-
def apply(first: ASTNode, second: ASTNode): FunctionNode = FunctionNode(SymbolNode(name), Seq(first, second))
60+
def apply(first: ASTNode, second: ASTNode): FunctionNode = createBinary(first, second)
61+
val createBinary: (ASTNode, ASTNode) => FunctionNode = (first, second) => FunctionNode(SymbolNode(name), Seq(first, second))
62+
}
63+
trait ApplyManyFunctionNode {
64+
protected def name: String
65+
def apply(nodes: Seq[ASTNode]): FunctionNode = createMany(nodes)
66+
val createMany: Seq[ASTNode] => FunctionNode = nodes => FunctionNode(SymbolNode(name), nodes)
6067
}
6168
trait UnapplyFunctionNode {
6269
protected def name: String

src/main/scala/com/github/tomerghelber/mathematica/eval/MathematicaEvaluator.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ class MathematicaEvaluator private (globalEnvironment: mutable.Map[String, ASTNo
2929

3030
private def eval(node: ASTNode, environment: mutable.Map[String, ASTNode]): ASTNode = {
3131
node match {
32-
case a @ (_: SymbolNode | _: NumberNode | _: StringNode) => evalTerminals(a, environment)
32+
case a: TerminalNode => evalTerminals(a, environment)
3333
case a @ (PlusNode(_) | TimesNode(_) | DivideNode(_)) => evalMath(a, environment)
34-
case other: Any => throw new MatchError(other)
34+
case other: ASTNode => throw new MatchError(other)
3535
}
3636
}
3737

38-
private def evalTerminals(node: ASTNode, environment: mutable.Map[String, ASTNode]): ASTNode = {
38+
private def evalTerminals(node: TerminalNode, environment: mutable.Map[String, ASTNode]): ASTNode = {
3939
node match {
4040
case NumberNode(fraction) if fraction.contains("/") =>
4141
NumberNode(fraction.split("/").map(_.toDouble).reduce(_ / _).toString)
4242
case SymbolNode(symbol) => environment(symbol)
43-
case node => node
43+
case node: TerminalNode => node
4444
}
4545
}
4646

src/main/scala/com/github/tomerghelber/mathematica/parser/MathematicaParser.scala

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -33,38 +33,44 @@ class MathematicaParser extends StdTokenParsers with ParserUtil with LazyLogging
3333
private def lower: Parser[ASTNode] = terminal | ROUND_BRACKET_OPEN ~> root <~ ROUND_BRACKET_CLOSE
3434

3535
private val overAndUnderscript: Parser[ASTNode] = chainr1(lower,
36-
UNDERSCRIPT ^^ {_=>(e1: ASTNode, e2: ASTNode)=>UnderscriptNode(e1, e2)}
37-
| OVERSCRIPT ^^ {_=>(e1: ASTNode, e2: ASTNode)=>OverscriptNode(e1, e2)}
36+
UNDERSCRIPT ^^ {_=>UnderscriptNode.createBinary}
37+
| OVERSCRIPT ^^ {_=>OverscriptNode.createBinary}
3838
)
3939

4040
private val subscript: Parser[ASTNode] = rep1sep(overAndUnderscript, SUBSCRIPT) ^^
41-
(subscripts => subscripts.reduceRight((e1, e2)=>SubscriptNode(e1, e2)))
41+
(subscripts => subscripts.reduceRight(SubscriptNode.apply))
4242

4343
private val part: Parser[ASTNode] = {
44-
((underparts: Parser[ASTNode]) => underparts ~ rep(
45-
(SQUARE_BRACKET_OPEN ~> rep1sep(underparts, COMMA) <~ SQUARE_BRACKET_CLOSE )
46-
| (SQUARE_BRACKET_OPEN2 ~> rep1sep(underparts, COMMA) <~ SQUARE_BRACKET_CLOSE2)
47-
| (SQUARE_BRACKET_OPEN3 ~> rep1sep(underparts, COMMA) <~ SQUARE_BRACKET_CLOSE3)
44+
((underparts: Parser[ASTNode]) => underparts ~ rep1(
45+
SQUARE_BRACKET_OPEN ~> rep1sep(underparts, COMMA) <~ SQUARE_BRACKET_CLOSE
4846
) ^^ {
49-
case expr ~ parts => parts.flatten.foldLeft(expr)(PartNode.apply)
50-
})(subscript)
47+
case expr ~ parts =>
48+
FunctionNode(expr, parts.flatten)
49+
}
50+
| underparts ~ rep1(
51+
(SQUARE_BRACKET_OPEN2 ~> rep1sep(underparts, COMMA) <~ SQUARE_BRACKET_CLOSE2)
52+
| (SQUARE_BRACKET_OPEN3 ~> rep1sep(underparts, COMMA) <~ SQUARE_BRACKET_CLOSE3)
53+
) ^^ {
54+
case expr ~ parts => PartNode(expr +: parts.flatten)
55+
} | underparts
56+
)(subscript)
5157
}
5258

5359
private val incrementAndDecrement: Parser[ASTNode] = lastFolderRight(part,
54-
INCREASE ^^ { _=>(e1: ASTNode) => IncrementNode.apply(e1) }
55-
| DECREASE ^^ { _=>(e1: ASTNode) => DecrementNode.apply(e1) }
60+
INCREASE ^^ { _=>IncrementNode.createUnary }
61+
| DECREASE ^^ { _=>DecrementNode.createUnary }
5662
)
5763

5864
private val preincrementAndPredecrement: Parser[ASTNode] = firstFolderRight(
59-
INCREASE ^^ { _=>(e1: ASTNode) => PreincrementNode.apply(e1)}
60-
| DECREASE ^^ { _=>(e1: ASTNode) => PredecrementNode.apply(e1)}
65+
INCREASE ^^ { _=>PreincrementNode.createUnary}
66+
| DECREASE ^^ { _=>PredecrementNode.createUnary}
6167
,
6268
incrementAndDecrement
6369
)
6470

6571
private val composition: Parser[ASTNode] = chainl1(preincrementAndPredecrement,
66-
COMPOSITION ^^ {_=>(e1: ASTNode,e2: ASTNode)=>CompositionNode(e1,e2)}
67-
| RIGHT_COMPOSITION ^^ {_=>(e1: ASTNode,e2: ASTNode)=>RightCompositionNode(e1,e2)}
72+
COMPOSITION ^^ {_=>CompositionNode.createBinary}
73+
| RIGHT_COMPOSITION ^^ {_=>RightCompositionNode.createBinary}
6874
)
6975

7076
// private def mapAndApply: Parser[ASTNode] = composition ~ ("/@" | "//@" | "@@" | "@@@") ~ composition ^^ {
@@ -75,15 +81,15 @@ class MathematicaParser extends StdTokenParsers with ParserUtil with LazyLogging
7581
// } | composition
7682

7783
private val factorial: Parser[ASTNode] = lastFolderRight(composition,
78-
(EXCLAMATION_MARK ~ EXCLAMATION_MARK) ^^ {_=>(e: ASTNode) => Factorial2Node(e)}
84+
(EXCLAMATION_MARK ~ EXCLAMATION_MARK) ^^ {_=>Factorial2Node.createUnary}
7985
) ~ opt(EXCLAMATION_MARK) ^^ {
8086
case expr ~ factorialOpt => factorialOpt.map(_=>ast.FactorialNode(expr)).getOrElse(expr)
8187
}
8288

8389
private val conjugateAndTranspose: Parser[ASTNode] = lastFolderRight(factorial,
84-
CONJUGATE ^^ {_=>(e:ASTNode)=>ConjugateNode(e)}
85-
| TRANSPOSE ^^ {_=>(e:ASTNode)=>TransposeNode(e)}
86-
| (CONJUGATE_TRANSPOSE | CONJUGATE_TRANSPOSE2) ^^ {_=>(e:ASTNode)=>ConjugateTransposeNode(e)}
90+
CONJUGATE ^^ {_=>ConjugateNode.createUnary}
91+
| TRANSPOSE ^^ {_=>TransposeNode.createUnary}
92+
| (CONJUGATE_TRANSPOSE | CONJUGATE_TRANSPOSE2) ^^ {_=>ConjugateTransposeNode.createUnary}
8793
)
8894

8995
private val derivative: Parser[ASTNode] = conjugateAndTranspose ~ rep("'") ^^ {
@@ -97,17 +103,16 @@ class MathematicaParser extends StdTokenParsers with ParserUtil with LazyLogging
97103

98104
private val power: Parser[ASTNode] = rep1sep(derivative, CARET) ^^ (values => values.reduceRight(PowerNode.apply))
99105

100-
private val verticalArrowAndVectorOperators: Parser[ASTNode] = CURLY_BRACKET_OPEN ~> rep1sep(power, COMMA) <~ CURLY_BRACKET_CLOSE ^^ {
101-
elements => FunctionNode(SymbolNode("List"), elements)
102-
} | power
106+
private val verticalArrowAndVectorOperators: Parser[ASTNode] =
107+
CURLY_BRACKET_OPEN ~> rep1sep(power, COMMA) <~ CURLY_BRACKET_CLOSE ^^ ListNode.createMany | power
103108

104109
private val sqrt: Parser[ASTNode] = firstFolderRight(
105-
SQRT ^^ {_=>(e:ASTNode)=>SqrtNode(e)},
110+
SQRT ^^ {_=>SqrtNode.createUnary},
106111
verticalArrowAndVectorOperators
107112
)
108113

109114
private val differentialD: Parser[ASTNode] = firstFolderRight(
110-
"d" ^^ {_=>(e:ASTNode)=>DifferentialDNode(e)},
115+
"d" ^^ {_=>DifferentialDNode.createUnary},
111116
sqrt
112117
)
113118

@@ -155,10 +160,10 @@ class MathematicaParser extends StdTokenParsers with ParserUtil with LazyLogging
155160
// // } | integrate
156161

157162
private val plusAndMinus: Parser[ASTNode] = chainr1(times,
158-
PLUS ^^ {_=> (e1: ASTNode, e2: ASTNode) => PlusNode(e1, e2)}
163+
PLUS ^^ {_=> PlusNode.createBinary}
159164
| MINUS ^^ {_=>(expr1: ASTNode, expr2: ASTNode) => PlusNode(expr1, TimesNode(NumberNode("-1"), expr2))}
160-
| PLUS_MINUS ^^ {_=> (expr1: ASTNode, expr2: ASTNode) => PlusMinusNode(expr1, expr2)}
161-
| MINUS_PLUS ^^ {_=> (expr1: ASTNode, expr2: ASTNode) => MinusPlusNode(expr1, expr2)}
165+
| PLUS_MINUS ^^ {_=> PlusMinusNode.createBinary}
166+
| MINUS_PLUS ^^ {_=> MinusPlusNode.createBinary}
162167
)
163168

164169
private val intersection: Parser[ASTNode] = rep1sep(plusAndMinus, INTERSECTION) ^^ (_.reduce(IntersectionNode.apply))
@@ -170,27 +175,27 @@ class MathematicaParser extends StdTokenParsers with ParserUtil with LazyLogging
170175
} | union
171176

172177
private val equalities: Parser[ASTNode] = chainl1(span,
173-
("==" | "\uF7D9") ^^ { _ => (e1: ASTNode, e2: ASTNode)=>EqualNode(e1, e2) }
174-
| "!=" ^^ { _ => (e1: ASTNode, e2: ASTNode)=>UnequalNode(e1, e2) }
175-
| ">" ^^ { _ => (e1: ASTNode, e2: ASTNode)=>GreaterNode(e1, e2) }
176-
| (">=" | "" | "") ^^ { _ => (e1: ASTNode, e2: ASTNode)=>GreaterEqualNode(e1, e2) }
177-
| "<" ^^ { _ => (e1: ASTNode, e2: ASTNode)=>LessNode(e1, e2) }
178-
| ("<=" | "" | "") ^^ { _ => (e1: ASTNode, e2: ASTNode)=>LessEqualNode(e1, e2) }
178+
("==" | "\uF7D9") ^^ { _ => EqualNode.createBinary }
179+
| "!=" ^^ { _ => UnequalNode.createBinary }
180+
| ">" ^^ { _ => GreaterNode.createBinary }
181+
| (">=" | "" | "") ^^ { _ => GreaterEqualNode.createBinary }
182+
| "<" ^^ { _ => LessNode.createBinary }
183+
| ("<=" | "" | "") ^^ { _ => LessEqualNode.createBinary }
179184
)
180185

181186
// private val horizontalArrowAndVectorOperators: Parser[ASTNode] = equalities
182187
// private val diagonalArrowOperators: Parser[ASTNode] = horizontalArrowAndVectorOperators
183188

184189
private val sameQ: Parser[ASTNode] = chainl1(equalities,
185-
"===" ^^ {_ => (e1: ASTNode,e2: ASTNode)=>SameQNode(e1,e2)}
186-
| "=!=" ^^ {_ => (e1: ASTNode,e2: ASTNode)=>UnSameQNode(e1,e2)}
190+
"===" ^^ {_ => SameQNode.createBinary}
191+
| "=!=" ^^ {_ => UnSameQNode.createBinary}
187192
)
188193

189194
private val setRelationOperators: Parser[ASTNode] = chainl1(sameQ,
190-
"" ^^ {_ =>(e1: ASTNode, e2: ASTNode)=>ElementNode(e1, e2)}
191-
| "" ^^ {_ =>(e1: ASTNode, e2: ASTNode)=>NotElementNode(e1, e2)}
192-
| "" ^^ {_ =>(e1: ASTNode, e2: ASTNode)=>SubsetNode(e1, e2)}
193-
| "" ^^ {_ =>(e1: ASTNode, e2: ASTNode)=>SupersetNode(e1, e2)}
195+
"" ^^ {_ =>ElementNode.createBinary}
196+
| "" ^^ {_ =>NotElementNode.createBinary}
197+
| "" ^^ {_ =>SubsetNode.createBinary}
198+
| "" ^^ {_ =>SupersetNode.createBinary}
194199
)
195200

196201
// private def forallAndExists: Parser[ASTNode] = setRelationOperators ~ ("∀" | "∃" | "∄") ~ setRelationOperators ^^ {
@@ -199,7 +204,7 @@ class MathematicaParser extends StdTokenParsers with ParserUtil with LazyLogging
199204
// case expr1 ~ "∄" ~ expr2 => NotExistsNode(expr1, expr2)
200205
// } | setRelationOperators
201206
//
202-
private val not: Parser[ASTNode] = firstFolderRight(("!" | "¬")^^{_=>(e:ASTNode)=>NotNode(e)},
207+
private val not: Parser[ASTNode] = firstFolderRight(("!" | "¬")^^{_=>NotNode.createUnary},
203208
setRelationOperators
204209
)
205210

@@ -236,8 +241,8 @@ class MathematicaParser extends StdTokenParsers with ParserUtil with LazyLogging
236241
// } | implies
237242

238243
private val rules = chainl1(not,
239-
("->" | "\uF522") ^^ {_=>(e1: ASTNode, e2: ASTNode) => RuleNode(e1, e2)}
240-
| (":>" | "\uF51F") ^^ {_=>(e1: ASTNode, e2: ASTNode) => RuleDelayedNode(e1, e2)}
244+
("->" | "\uF522") ^^ {_=>RuleNode.createBinary}
245+
| (":>" | "\uF51F") ^^ {_=>RuleDelayedNode.createBinary}
241246
)
242247

243248
private def root = rules

src/test/scala/com/github/tomerghelber/mathematica/parser/MathematicaParserSpec.scala

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -292,59 +292,60 @@ class MathematicaParserSpec extends FunSpec with Matchers with ScalaCheckPropert
292292

293293

294294
describe("Examples from WolfRam site") {
295-
ignore("https://www.wolfram.com/language/gallery/implement-hello-world-in-the-cloud/ [1]") {
296-
forAll { p: MathematicaParser =>
295+
describe("https://www.wolfram.com/language/gallery/implement-hello-world-in-the-cloud/") {
296+
it("[1]") {
297+
forAll { p: MathematicaParser =>
297298

298-
val out1 = p.parse("\"Hello, World\"")
299-
out1 shouldBe StringNode("Hello, World")
299+
val out1 = p.parse("\"Hello, World\"")
300+
out1 shouldBe StringNode("Hello, World")
301+
}
300302
}
301-
}
302-
ignore("https://www.wolfram.com/language/gallery/implement-hello-world-in-the-cloud/ [2]") {
303-
forAll { p: MathematicaParser =>
304-
val out2 = p.parse("Do[Print[\"Hello, World\"], {5}]")
305-
out2 shouldBe FunctionNode(SymbolNode("Do"), Seq(
306-
FunctionNode(SymbolNode("Print"), Seq(
307-
StringNode("Hello, World")
308-
)),
309-
FunctionNode(SymbolNode("List"), Seq(
310-
NumberNode("5")
303+
ignore("[2]") {
304+
forAll { p: MathematicaParser =>
305+
val out2 = p.parse("Do[Print[\"Hello, World\"], {5}]")
306+
out2 shouldBe FunctionNode(SymbolNode("Do"), Seq(
307+
FunctionNode(SymbolNode("Print"), Seq(
308+
StringNode("Hello, World")
309+
)),
310+
ListNode(Seq(
311+
NumberNode("5")
312+
))
311313
))
312-
))
313314

315+
}
314316
}
315-
}
316-
ignore("https://www.wolfram.com/language/gallery/implement-hello-world-in-the-cloud/ [3]") {
317-
forAll { p: MathematicaParser =>
318-
val out3 = p.parse("CloudObject[\"Hello, World\"]")
319-
out3 shouldBe FunctionNode(SymbolNode("Part"), Seq(
320-
SymbolNode("CloudObject"),
321-
StringNode("Hello, World"),
322-
))
317+
it("[3]") {
318+
forAll { p: MathematicaParser =>
319+
val out3 = p.parse("CloudObject[\"Hello, World\"]")
320+
out3 shouldBe FunctionNode(SymbolNode("CloudObject"), Seq(
321+
StringNode("Hello, World"),
322+
))
323+
}
323324
}
324-
}
325-
ignore("https://www.wolfram.com/language/gallery/implement-hello-world-in-the-cloud/ [4]") {
326-
forAll { p: MathematicaParser =>
327-
val out4 = p.parse(
328-
"CloudDeploy[\n" +
329-
" ExportForm[Style[Framed[\"Hello, World\", ImageMargins -> 60],\n" +
330-
" 80, Orange, FontFamily -> \"Verdana\"], \"GIF\"], \n" +
331-
" Permissions -> \"Public\"]"
332-
)
333-
out4 shouldBe FunctionNode(SymbolNode("CloudDeploy"), Seq(
334-
FunctionNode(SymbolNode("ExportForm"), Seq(
335-
FunctionNode(SymbolNode("Style"), Seq(
336-
FunctionNode(SymbolNode("Framed"), Seq(
337-
StringNode("Hello, World"),
338-
StringNode("ImageMargins -> 60"),
325+
ignore("[4]") {
326+
forAll { p: MathematicaParser =>
327+
val out4 = p.parse(
328+
"CloudDeploy[\n" +
329+
" ExportForm[Style[Framed[\"Hello, World\", ImageMargins -> 60],\n" +
330+
" 80, Orange, FontFamily -> \"Verdana\"], \"GIF\"], \n" +
331+
" Permissions -> \"Public\"]"
332+
)
333+
out4 shouldBe FunctionNode(SymbolNode("CloudDeploy"), Seq(
334+
FunctionNode(SymbolNode("ExportForm"), Seq(
335+
FunctionNode(SymbolNode("Style"), Seq(
336+
FunctionNode(SymbolNode("Framed"), Seq(
337+
StringNode("Hello, World"),
338+
StringNode("ImageMargins -> 60"),
339+
)),
340+
StringNode("80"),
341+
StringNode("Orange"),
342+
StringNode("FontFamily -> \"Verdana\""),
339343
)),
340-
StringNode("80"),
341-
StringNode("Orange"),
342-
StringNode("FontFamily -> \"Verdana\""),
344+
StringNode("GIF"),
343345
)),
344-
StringNode("GIF"),
345-
)),
346-
StringNode("Permissions -> \"Public\""),
347-
))
346+
StringNode("Permissions -> \"Public\""),
347+
))
348+
}
348349
}
349350
}
350351

0 commit comments

Comments
 (0)