forked from diegoeche/Raytracer
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Parser.scala
132 lines (113 loc) · 4.81 KB
/
Parser.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import java.awt.Color
import javax.vecmath._;
import scala.util.parsing.combinator.lexical._
import scala.util.parsing.combinator.syntactical._
/**
* ExprLexical
* this is copied from this address. Basic support for floatTokens
* http://jim-mcbeath.blogspot.com/2008/09/scala-parser-combinators.html
*/
class ExprLexical extends StdLexical {
override def token: Parser[Token] = floatingToken | super.token
def floatingToken: Parser[Token] =
rep1(digit) ~ optFraction ~ optExponent ^^
{ case intPart ~ frac ~ exp => NumericLit(
(intPart mkString "") :: frac :: exp :: Nil mkString "")}
def chr(c:Char) = elem("", ch => ch==c )
def sign = chr('+') | chr('-')
def optSign = opt(sign) ^^ {
case None => ""
case Some(sign) => sign
}
def fraction = '.' ~ rep(digit) ^^ {
case dot ~ ff => dot :: (ff mkString "") :: Nil mkString ""
}
def optFraction = opt(fraction) ^^ {
case None => ""
case Some(fraction) => fraction
}
def exponent = (chr('e') | chr('E')) ~ optSign ~ rep1(digit) ^^ {
case e ~ optSign ~ exp => e :: optSign :: (exp mkString "") :: Nil mkString ""
}
def optExponent = opt(exponent) ^^ {
case None => ""
case Some(exponent) => exponent
}
}
object SceneParser extends StandardTokenParsers {
override val lexical = new ExprLexical
lexical.delimiters ++= List("-","<",">",",","{","}")
lexical.reserved ++= List( "background",
"color",
"camera",
"light_source",
"location",
"look_at",
"pigment",
"plane",
"sphere",
"Blue",
"Green",
"Red",
"Yellow",
"Black",
"White")
// Doesn't accept decimals nor negatives
def valueP = opt("-") ~ numericLit ^^
{case Some(_) ~ s => -s.toDouble
case None ~ s => s.toDouble}
def backgroundP = "background" ~>"{" ~> colorP <~ "}" ^^
{ case color => new Background (new Color3f (color)) }
def colorP = "color" ~> ("Blue" | "Green" | "Red" | "Yellow" | "White" | "Black") ^^
{case "Blue" => Color.blue
case "Green" => Color.green
case "Red" => Color.red
case "Yellow" => Color.yellow
case "White" => Color.white
case "Black" => Color.black
}
def pigmentP = "pigment" ~> "{" ~> colorP <~ "}"
def vectorLitP = ("<" ~> valueP) ~
("," ~> valueP) ~
("," ~> valueP <~ ">") ^^
{case x ~ y ~ z => new Vector3d(x.toDouble,
y.toDouble,
-z.toDouble)}
// This one lifts a common constructor of objects like sphere and plane
def vectorValueP(cons: String, f: Function[(Vector3d, Double, Color3f), SceneObject]) =
(cons ~> "{" ~> vectorLitP) ~
("," ~> valueP ) ~
(pigmentP <~ "}") ^^
{case center ~ radius ~ pigment=> f(center, radius, new Color3f ( pigment ))}
def sphereP =
vectorValueP("sphere", {case (center,radius,pigment) =>
new SceneObject (new Sphere(center, radius), new Material(pigment))})
def planeP =
vectorValueP("plane", {case (center,radius,pigment) =>
new SceneObject (new Plane(center, radius), new Material(pigment))})
def cameraP = ("camera" ~> "{" ~> "location"~> vectorLitP ) ~
( "look_at" ~> vectorLitP <~ "}") ^^
{ case location ~ lookAt => new Camera (location,lookAt) }
def lightP = ("light_source" ~> "{" ~> vectorLitP)~
(colorP <~ "}")^^
{ case location ~ color => new LightSource (location,new Color3f (color)) }
def sceneObjP = sphereP | planeP | cameraP | lightP | backgroundP
// The scene is just a list of SceneObjects
def sceneP: Parser[List[SceneElement]] = sceneObjP+
def parse(s:String) = {
val tokens = new lexical.Scanner(s)
// Check there's only one camera and LightSource.
def checkTree (tree:List[SceneObject]) = (tree count (_.isInstanceOf[Camera])) >=0 &&
(tree count (_.isInstanceOf[LightSource])) >= 0
// lastFailure = None
phrase(sceneP)(tokens) match {
case Success(tree,_) =>
if (true) { // checkTree(tree)
Right(tree)
}else{
Left("Error in the number of cameras or light sources.")
}
case x => Left(x.toString)
}
}
}