ceml 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +4 -0
- data/Makefile +5 -0
- data/README.markdown +86 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/editors/CEML.tmbundle/Syntaxes/ceml.tmLanguage +322 -0
- data/editors/CEML.tmbundle/info.plist +17 -0
- data/examples/breakfast-exchange.ceml +15 -0
- data/examples/citizen-investigation.ceml +24 -0
- data/examples/high-fives.ceml +19 -0
- data/lib/ceml/casting.rb +52 -0
- data/lib/ceml/engine.rb +138 -0
- data/lib/ceml/instructions.rb +31 -0
- data/lib/ceml/script.rb +103 -0
- data/lib/ceml/tt/casting.rb +618 -0
- data/lib/ceml/tt/casting.treetop +50 -0
- data/lib/ceml/tt/instructions.rb +320 -0
- data/lib/ceml/tt/instructions.treetop +42 -0
- data/lib/ceml/tt/lexer.rb +400 -0
- data/lib/ceml/tt/lexer.treetop +34 -0
- data/lib/ceml/tt/scripts.rb +345 -0
- data/lib/ceml/tt/scripts.treetop +32 -0
- data/lib/ceml.rb +35 -0
- data/test/helper.rb +55 -0
- data/test/test_casting.rb +78 -0
- data/test/test_engine.rb +30 -0
- data/test/test_instructions.rb +27 -0
- data/test/test_scripts.rb +57 -0
- metadata +107 -0
data/lib/ceml/engine.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module CEML
|
4
|
+
class Dummy
|
5
|
+
def method_missing(meth, *args, &blk)
|
6
|
+
# puts "#{meth}: #{args.to_s.inspect}"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Engine
|
11
|
+
attr_reader :script, :parts
|
12
|
+
def this; @parts[@current_id]; end
|
13
|
+
def roles; this[:roles] ||= Set.new; end
|
14
|
+
def got; this[:received]; end
|
15
|
+
def recognized; this[:recognized]; end
|
16
|
+
def pc; this[:pc] ||= 0; end
|
17
|
+
|
18
|
+
def initialize(script_text, delg = Dummy.new)
|
19
|
+
@script = CEML.parse(:script, script_text)
|
20
|
+
@delg = delg
|
21
|
+
@parts = {}
|
22
|
+
@seq = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(id, *roles)
|
26
|
+
obj = Hash === roles[-1] ? roles.pop : {}
|
27
|
+
parts[id] = obj.merge :roles => Set.new(roles)
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
:loop while parts.keys.any? do |@current_id|
|
32
|
+
# puts "trying: #{@current_id}: #{seq[pc]}"
|
33
|
+
next unless seq[pc] and send(*seq[pc])
|
34
|
+
@delg.send(*seq[pc] + [@current_id])
|
35
|
+
this[:pc]+=1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def seq
|
40
|
+
@seq[roles] ||= begin
|
41
|
+
bytecode = [[:start]]
|
42
|
+
instrs = script.instructions_for(roles)
|
43
|
+
instrs.each do |inst|
|
44
|
+
case inst.cmd
|
45
|
+
when :ask
|
46
|
+
bytecode << [:ask_q, inst]
|
47
|
+
bytecode << [:answered_q, inst]
|
48
|
+
when :tell
|
49
|
+
if script.title
|
50
|
+
bytecode << [:assign, inst]
|
51
|
+
bytecode << [:complete_assign, inst]
|
52
|
+
else
|
53
|
+
bytecode << [:send_msg, inst]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
if instrs.empty? and script.title
|
58
|
+
bytecode << [:null_assign]
|
59
|
+
bytecode << [:complete_assign]
|
60
|
+
end
|
61
|
+
bytecode << [:finish]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def say x, params = {}
|
66
|
+
this[:said] = x
|
67
|
+
this.merge! params
|
68
|
+
end
|
69
|
+
|
70
|
+
def qs_answers
|
71
|
+
this[:qs_answers] ||= Hash.new
|
72
|
+
end
|
73
|
+
|
74
|
+
def expand(role, var)
|
75
|
+
role = nil if role == 'otherguy'
|
76
|
+
role = role.to_sym if role
|
77
|
+
parts.each do |key, thing|
|
78
|
+
next if key == @current_id
|
79
|
+
next if role and not thing[:roles].include? role
|
80
|
+
value = (thing[:qs_answers]||{})[var] and return value
|
81
|
+
end
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
# ==============
|
86
|
+
# = basic flow =
|
87
|
+
# ==============
|
88
|
+
|
89
|
+
def start
|
90
|
+
roles.include? :agent or return false
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
def ask_q q
|
95
|
+
text = q.interpolate(self) or return false
|
96
|
+
say :ask, :q => text
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
def answered_q q
|
101
|
+
got or return false
|
102
|
+
qs_answers[q.key] = got
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
def send_msg a
|
107
|
+
text = a.interpolate(self) or return false
|
108
|
+
say :message, :msg => text
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
def assign a
|
113
|
+
text = a.interpolate(self) or return false
|
114
|
+
say :assignment, :msg => text
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
118
|
+
def complete_assign a = nil
|
119
|
+
got or return false
|
120
|
+
if recognized == :done
|
121
|
+
say :ok
|
122
|
+
true
|
123
|
+
else
|
124
|
+
@delg.send :did_report
|
125
|
+
false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def null_assign
|
130
|
+
say :proceed
|
131
|
+
true
|
132
|
+
end
|
133
|
+
|
134
|
+
def finish
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CEML
|
2
|
+
module Instructions
|
3
|
+
def validate!(allowed_roles)
|
4
|
+
extra_roles = roles - allowed_roles
|
5
|
+
raise "unrecognized rolenames: #{extra_roles.inspect}" unless extra_roles.empty?
|
6
|
+
|
7
|
+
# max one tell per role
|
8
|
+
roles.each{ |r| tell([r]) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def roles
|
12
|
+
elements.map{ |s| s.role.to_sym }.uniq
|
13
|
+
end
|
14
|
+
|
15
|
+
def for(roles)
|
16
|
+
elements.select{ |s| roles.include?(s.role.to_sym) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def asks(roles)
|
20
|
+
elements.select do |s|
|
21
|
+
s.text_value =~ /^ask/ && roles.include?(s.role.to_sym)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def tell(roles)
|
26
|
+
ss = elements.select{ |s| s.text_value =~ /^tell/ && roles.include?(s.role.to_sym) }
|
27
|
+
raise "multiple assignments for role: #{role}" if ss.size > 1
|
28
|
+
ss.first
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/ceml/script.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
module CEML
|
2
|
+
module Script
|
3
|
+
|
4
|
+
def to_hash *fields
|
5
|
+
fields.inject({}){ |h, s| x = send(s); h[s] = x if x; h }
|
6
|
+
end
|
7
|
+
|
8
|
+
def script
|
9
|
+
text_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def title
|
13
|
+
super && !super.empty? && super.value
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
title || label
|
18
|
+
end
|
19
|
+
|
20
|
+
def radius
|
21
|
+
casting_statement.empty? ? nil : casting_statement.radius
|
22
|
+
end
|
23
|
+
|
24
|
+
def roles
|
25
|
+
return [:agents] if casting_statement.empty?
|
26
|
+
return casting_statement.roles
|
27
|
+
end
|
28
|
+
|
29
|
+
def dramatis_personae
|
30
|
+
casting_statement.empty? ? nil : casting_statement
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :dp, :dramatis_personae
|
34
|
+
|
35
|
+
def simple?
|
36
|
+
(roles - [:agents, :organizer]).empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def expand_roles(roles)
|
40
|
+
roles.map{ |r| r == :agent ? [:agent, :agents] : r }.flatten.concat([:both, :all, :everyone])
|
41
|
+
end
|
42
|
+
|
43
|
+
def instructions_for(roles)
|
44
|
+
return [] if !instructions or instructions.empty?
|
45
|
+
return instructions.for(expand_roles(roles))
|
46
|
+
end
|
47
|
+
|
48
|
+
def asks(roles)
|
49
|
+
return [] if instructions.empty?
|
50
|
+
instructions.asks([*roles])
|
51
|
+
end
|
52
|
+
|
53
|
+
def params
|
54
|
+
asks(:organizer).map do |ask|
|
55
|
+
[ask.var, ask.var.capitalize, ask.text]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def tell(roles)
|
60
|
+
return nil if instructions.empty?
|
61
|
+
instructions.tell([*roles])
|
62
|
+
end
|
63
|
+
|
64
|
+
def type
|
65
|
+
return 'mission' if title
|
66
|
+
return 'unknown' if instructions.empty?
|
67
|
+
return 'question' if not instructions.asks.empty?
|
68
|
+
return 'message' if instructions.tell(:agents)
|
69
|
+
return 'unknown'
|
70
|
+
end
|
71
|
+
|
72
|
+
def message?
|
73
|
+
type == 'message'
|
74
|
+
end
|
75
|
+
|
76
|
+
def label
|
77
|
+
return title || begin
|
78
|
+
return "unknown" unless simple?
|
79
|
+
if q = instructions.asks(:agents).first
|
80
|
+
"Question: #{q.text}"
|
81
|
+
elsif tell = instructions.tell(:agents)
|
82
|
+
"Message: #{tell}" if tell
|
83
|
+
else
|
84
|
+
"unknown"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def validate!
|
90
|
+
instructions.validate!(allowed_roles) unless instructions.empty?
|
91
|
+
end
|
92
|
+
|
93
|
+
def allowed_roles
|
94
|
+
allowed_roles = [:organizer, :agents]
|
95
|
+
allowed_roles += casting_statement.roles unless casting_statement.empty?
|
96
|
+
allowed_roles
|
97
|
+
end
|
98
|
+
|
99
|
+
def concludes_immediately?
|
100
|
+
!title and instructions.asks([:agents]).empty?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|