TDP007 — Konstruktion av datorspråk — Ola Leifler & Peter Dalenius

advertisement
TDP007
— Konstruktion av datorspråk —
Institutionen för datavetenskap, Linköpings universitet
Ola Leifler & Peter Dalenius
{olale,petda}@ida.liu.se
Acknowledgments
Much material from these slides comes from Brian Amberg at the university of Freiburg
(http://ruby.brian-amberg.de/course/ ).
Ola Leifler
TDDB27
Licence
c
Original version copyright 2004-2006
Brian Schroeder. TDP007 course copyright
c
2007-2008
Ola Leifler.
Permission is granted to copy, distribute and/or modify this document under the terms of
the GNU Free Documentation License, Version 1.2 or any later version published by the
Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no
Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free
Documentation License".
Part I
Domain-specific languages
Ola Leifler
TDDB27
Overview
◮
What? Language for a small domain
◮
Why? Efficient representation
◮
When? Awkward to write/read
Ola Leifler
TDDB27
Overview
◮
What? Language for a small domain
◮
Why? Efficient representation
◮
When? Awkward to write/read
How?
Using Ruby of course!
Ola Leifler
TDDB27
Custom dispatch
catching errors
◮
const_missing When constants (names) cannot resolve to references, this method is used
◮
method_missing when undefined methods are called, method_missing is used
Ola Leifler
TDDB27
Configuration of a gearbox
Consider the following gearbox configuration:
1 /ω 1
ωin
ut
2 /ω 2
ωin
ut
...
Ola Leifler
=
=
33/15
28/20
TDDB27
In Ruby
class Configuration
5
class Configuration
6
7
8
9
10
11
12
attr_reader :logger
def initialize(filename="config.rb")
@filename=filename
@logger=Logger.new(STDOUT)
instance_eval(File.new(filename).read())
end
13
14
15
16
17
18
19
20
21
def method_missing(method_name,arg)
@@var="#{method_name}"
class << self
class_eval { attr_accessor(@@var) }
end
logger.debug "added accessor #{method_name}"
instance_eval("self.#{method_name}=#{arg}")
end
22
23
end
Ola Leifler
TDDB27
The gearbox configuration
1
2
3
# Förhållandet mellan mellanaxeln och drivaxeln
z_ut 27.0
z_in 19.0
4
5
6
7
8
# 1:ans växel
z1_ut 33.0
z1_in 15.0
z1_ratio (z1_ut/z1_in)*(z_ut/z_in)
9
10
11
12
13
# 2:ans växel
z2_ut 28.0
z2_in 20.0
z2_ratio (z2_ut/z2_in)*(z_ut/z_in)
14
15
16
17
18
# 3:ans växel
z3_ut 23.0
z3_in 24.0
z3_ratio (z3_ut/z3_in)*(z_ut/z_in)
19
20
21
22
23
# 4:ans växel
z4_ut 19.0
z4_in 27.0
z4_ratio (z4_ut/z4_in)*(z_ut/z_in)
Ola Leifler
TDDB27
Claimant class
Consider the following example:
3
4
5
6
7
8
9
class Claimant
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
@benefits = 0
end
10
11
attr_reader :name,:gender,:age,:benefits
12
13
14
15
def retired?
@gender == :male && @age >= 65 || @gender == :female && @age >= 60
end
16
17
18
19
def to_s
@name
end
20
21
22
23
def display
print "#{@name}\t#{gender}\t#{age}\n"
end
Ola Leifler
TDDB27
claimants
Now we add some claimants:
46
47
48
49
alice = Claimant.new("Alice", :female, 50)
bill = Claimant.new("Bill", :male, 40)
carol = Claimant.new("Carol", :female, 63)
dave = Claimant.new("Dave", :male, 70)
50
51
claimants = [alice, bill, carol, dave]
Ola Leifler
TDDB27
Now, some queries
Query examples:
54
55
56
57
58
59
60
puts "All claimants:"
All claimants: » nil
claimants.each { |c| c.display }
Alice female 50
Bill male 40
Carol female 63
Dave male 70 » [#<Claimant:0x108d65c @age=50, @gender=:female, @benefits=0, @name="
61
62
63
64
65
66
67
puts » nil
puts "Retired:"
Retired: » nil
(claimants.select { |c| c.retired?}).each { |c| c.display }
Carol female 63
Dave male 70 » [#<Claimant:0x108798c @age=63, @gender=:female, @benefits=0, @name="
68
69
70
71
72
73
74
puts » nil
puts "Men:"
Men: » nil
(claimants.select { |c| c.gender == :male}).each { |c| c.display }
Bill male 40
Dave male 70 » [#<Claimant:0x108a894 @age=40, @gender=:male, @benefits=0, @name="B
75
Ola Leifler
TDDB27
Some more queries
Query examples:
76
77
78
79
80
81
puts » nil
puts "Women:"
Women: » nil
(claimants.select { |c| c.gender == :female}).each { |c| c.display }
Alice female 50
Carol female 63 » [#<Claimant:0x108d65c @age=50, @gender=:female, @benefits=0, @name="
82
83
84
85
86
87
88
puts » nil
puts "Not retired:"
Not retired: » nil
(claimants.select { |c| not c.retired?}).each { |c| c.display }
Alice female 50
Bill male 40 » [#<Claimant:0x108d65c @age=50, @gender=:female, @benefits=0, @name="A
89
90
91
92
93
94
puts » nil
puts "Retired men:"
Retired men: » nil
((claimants.select { |c| c.gender == :male}).select { |c| c.retired?}).each { |c| c.display }
Dave male 70 » [#<Claimant:0x1084bd8 @age=70, @gender=:male, @benefits=0, @name="D
Ola Leifler
TDDB27
Same queries, now rewritten using HOM
Query examples using higher-order message passing:
96
97
98
99
100
101
102
puts "All claimants:"
All claimants: » nil
claimants.do.display
Alice female 50
Bill male 40
Carol female 63
Dave male 70 » nil
103
104
105
106
107
108
109
puts » nil
puts "Retired:"
Retired: » nil
claimants.that.are.retired?.do.display
Carol female 63
Dave male 70 » nil
110
111
112
113
114
115
116
puts » nil
puts "Men:"
Men: » nil
(claimants.that.have.gender == :male).do.display
Bill male 40
Dave male 70 » nil
Ola Leifler
TDDB27
Same queries, now rewritten using HOM
Query examples using higher-order message passing:
117
118
119
120
121
122
123
puts » nil
puts "Women:"
Women: » nil
(claimants.that.have.gender == :female).do.display
Alice female 50
Carol female 63 » nil
124
125
126
127
128
129
130
puts » nil
puts "Not retired:"
Not retired: » nil
claimants.that.are_not.retired?.do.display
Alice female 50
Bill male 40 » nil
131
132
133
134
135
136
puts » nil
puts "Retired men:"
Retired men: » nil
(claimants.that.have.gender == :male).that.are.retired?.do.display
Dave male 70 » nil
Ola Leifler
TDDB27
Higher-order message passing
We extend Enumerable
132
133
134
135
module Enumerable
def do
return HOM::Do.new(self)
end
136
137
138
139
def that
return HOM::That.new(self)
end
140
141
142
143
def all
return HOM::All.new(self)
end
144
145
146
147
def any
return HOM::Any.new(self)
end
Ola Leifler
TDDB27
Higher-order message passing
We create wrapper classes That, All, Any
105
106
107
108
109
class That < Collator
def apply(&block)
return @receiver.select(&block)
end
end
110
111
112
113
114
115
class All < Collator
def apply(&block)
return @receiver.all?(&block)
end
end
116
117
118
119
120
121
class Any < Collator
def apply(&block)
return @receiver.any?(&block)
end
end
Ola Leifler
TDDB27
Higher-order message passing
Such objects respond to messages are, are_not and have
87
88
89
90
class Collator
def initialize(receiver)
@receiver = receiver
end
91
def are
return HOM::Are.new(self)
end
92
93
94
95
def are_not
return HOM::AreNot.new(self)
end
96
97
98
99
100
101
102
103
def have
return HOM::Have.new(self)
end
end
Ola Leifler
TDDB27
Higher-order message passing
Selectors Are, AreNot, and Have
27
28
29
30
31
class Are < HigherOrderMessage
def method_missing(id, *args)
return @handler.apply {|e| e.__send__(id,*args)}
end
end
32
33
34
35
36
37
class AreNot < HigherOrderMessage
def method_missing(id, *args)
return @handler.apply {|e| not e.__send__(id,*args)}
end
end
38
39
40
41
42
43
class Have < HigherOrderMessage
def method_missing(id, *args)
return ResultMatcher.new(@handler,id,args)
end
end
Ola Leifler
TDDB27
Logic programming example
Example of logic programming as a DSL
1
2
3
+ auckland.by_car_to(hamilton)
+ hamilton.by_car_to(raglan)
+ valmont.by_car_to(metz)
4
5
+ metz.by_train_to(paris)
6
7
8
+ paris.by_plane_to(los_angeles)
+ los_angeles.by_plane_to(auckland)
9
10
11
12
+ X.direct_to(Y).if { X.by_car_to(Y) }
+ X.direct_to(Y).if { X.by_train_to(Y) }
+ X.direct_to(Y).if { X.by_plane_to(Y) }
13
14
15
+ X.travel_to(Y).if { X.direct_to(Y) }
+ X.travel_to(Y).if { X.direct_to(Z) & Z.travel_to(Y) }
16
17
18
19
+ X.direct_to(Y,H).if { X.by_car_to(Y) & H.bind_to(car) }
+ X.direct_to(Y,H).if { X.by_train_to(Y) & H.bind_to(train) }
+ X.direct_to(Y,H).if { X.by_plane_to(Y) & H.bind_to(plane) }
20
21
22
+ X.travel_to(Y,P).if { X.direct_to(Y,H) & P.bind_to(H.go(X,Y)) }
+ X.travel_to(Y,P).if { X.direct_to(Z,H) & Z.travel_to(Y,P1) & P.bind_to(H.go(X,Z,P1)) }
Ola Leifler
TDDB27
Part II
Language Parsing
Ola Leifler
TDDB27
Grammar
Example grammar for arithmetic language
expression = ["+"|"-"] term {("+"|"-") term} .
term = factor {("*"|"/") factor} .
factor = ident | number | "(" expression ")" .
Grammar
Ola Leifler
TDDB27
Example
Parsing an arithmetic expression
1
Ola Leifler
+
(
2
*
3
)
TDDB27
Dice Language
1
class DiceRoller
2
3
4
5
6
7
def initialize
@diceParser = Parser.new("dice roller") do
token(/\s+/)
token(/\d+/) {|m| m.to_i }
token(/./) {|m| m }
8
9
10
11
12
13
start :expr do
match(:expr, ’+’, :term) {|a, _, b| a + b }
match(:expr, ’-’, :term) {|a, _, b| a - b }
match(:term)
end
14
15
16
17
18
19
Ola Leifler
rule :term do
match(:term, ’*’, :dice) {|a, _, b| a * b }
match(:term, ’/’, :dice) {|a, _, b| a / b }
match(:dice)
end
TDDB27
GNU Free Documentation License
The GNU Free Documentation License as applicable to this document can be found at:
http://www.gnu.org/copyleft/fdl.html
Ola Leifler
TDDB27
Download