ceml 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.4.0
data/ceml.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ceml}
8
- s.version = "0.3.1"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Joe Edelman"]
12
- s.date = %q{2010-12-26}
12
+ s.date = %q{2011-01-09}
13
13
  s.description = %q{a language for coordinating real world events}
14
14
  s.email = %q{joe@citizenlogistics.com}
15
15
  s.extra_rdoc_files = [
@@ -31,11 +31,13 @@ Gem::Specification.new do |s|
31
31
  "examples/breakfast-exchange.ceml",
32
32
  "examples/citizen-investigation.ceml",
33
33
  "examples/high-fives.ceml",
34
+ "examples/sample_delegate.rb",
34
35
  "lib/ceml.rb",
35
- "lib/ceml/casting.rb",
36
36
  "lib/ceml/casting_criterion.rb",
37
+ "lib/ceml/casting_statement.rb",
38
+ "lib/ceml/delegate.rb",
37
39
  "lib/ceml/incident.rb",
38
- "lib/ceml/instructions.rb",
40
+ "lib/ceml/instruction_statements.rb",
39
41
  "lib/ceml/script.rb",
40
42
  "lib/ceml/tt/casting.treetop",
41
43
  "lib/ceml/tt/instructions.treetop",
@@ -4,11 +4,53 @@ require "forwardable"
4
4
  module CEML
5
5
  class Candidate < Struct.new :uid, :tags, :matchables, :lat, :lng
6
6
  include Geokit::Mappable
7
+ attr_reader :criteria
8
+
9
+ def load(criteria)
10
+ @criteria = criteria.select{ |c| c =~ self }
11
+ if @criteria.empty? then nil else self end
12
+ end
13
+
14
+ def criteria_for_location(star)
15
+ criteria.select{ |c| c.fits?(self, star) }
16
+ end
17
+ end
18
+
19
+ class CastingLocation < Struct.new :script, :hash, :created
20
+ attr_accessor :added
21
+ def star; hash.values.flatten.first; end
22
+
23
+ def criteria
24
+ @criteria ||= script.awaited_criteria.sort_by{ |c| [-c.complexity, c.min_match] }
25
+ end
26
+
27
+ def push candidate
28
+ matching_criteria = candidate.criteria_for_location(star)
29
+ return if matching_criteria.empty?
30
+ matching_criteria.each{ |c| (hash[c] ||= []) << candidate }
31
+ @added = true
32
+ end
33
+
34
+ def self.create script, candidate
35
+ new(script, {}, true).tap{ |x| x.push candidate }
36
+ end
37
+
38
+ # this method will miss possible castings and does not handle ranges at all
39
+ def cast
40
+ {}.tap do |casting|
41
+ criteria.each do |c|
42
+ return nil unless folks = hash[c].dup
43
+ folks -= casting.keys
44
+ return nil unless folks.size >= c.min_match
45
+ c.role_counts.each do |role, minct|
46
+ folks.shift(minct).each{ |guy| casting[guy.uid] = role.to_sym }
47
+ end
48
+ end
49
+ end
50
+ end
7
51
  end
8
52
 
9
53
  class CastingCriterion < Struct.new :script, :plus_tags, :minus_tags, :grouped_by, :radius, :role_counts
10
- extend Forwardable
11
- def_delegators :script, :locations
12
54
  def min_match; role_counts.values.reduce(:+); end
13
55
  def complexity; plus_tags.size; end
14
56
 
@@ -16,24 +58,28 @@ module CEML
16
58
  (plus_tags - candidate.tags).empty? and (minus_tags & candidate.tags).empty?
17
59
  end
18
60
 
19
- def satisfied_by_group?(people, already_used)
20
- if (people - already_used).size >= min_match
21
- already_used.concat((people - already_used).first(min_match))
22
- true
23
- end
24
- end
25
-
26
- def list_candidate(candidate)
27
- locs = locations.select do |l|
28
- star = l.values.flatten.first
29
- next unless grouped_by.all?{ |g| candidate.matchables[g] == star.matchables[g] }
30
- !radius or candidate.distance_to(star, :meters) <= radius
31
- end
32
- if locs.empty?
33
- new_loc = {}
34
- [locations, locs].each{ |l| l << new_loc }
35
- end
36
- locs.each{ |l| (l[hash] ||= []) << candidate }
61
+ def fits?(candidate, star)
62
+ return true unless star
63
+ return unless grouped_by.all?{ |g| candidate.matchables[g] == star.matchables[g] }
64
+ p radius
65
+ !radius or candidate.distance_to(star, :meters) <= radius
37
66
  end
38
67
  end
39
68
  end
69
+
70
+ # def satisfied_by_group?(people, already_used)
71
+ # if (people - already_used).size >= min_match
72
+ # already_used.concat((people - already_used).first(min_match))
73
+ # true
74
+ # end
75
+ # end
76
+
77
+ # this method and the one below will miss possible
78
+ # castings and do not handle ranges at all
79
+ # def complete?(loc)
80
+ # people_used = []
81
+ # @criteria.all? do |c|
82
+ # next unless folks = loc[c.hash]
83
+ # c.satisfied_by_group?(folks, people_used)
84
+ # end
85
+ # end
@@ -24,7 +24,7 @@ module CEML
24
24
  end
25
25
 
26
26
  def radius
27
- within_phrase.empty? ? 1600 * 50 : within_phrase.distance.meters
27
+ within_phrase.empty? ? nil : within_phrase.distance.meters
28
28
  end
29
29
 
30
30
  def nab?
@@ -0,0 +1,28 @@
1
+ module Kernel
2
+ def gen_code(size = 8)
3
+ rand(36**size).to_s(36)
4
+ end
5
+ end
6
+
7
+ module CEML
8
+ class Delegate
9
+ LOCATIONS = {}
10
+ PLAYERS = {}
11
+
12
+ # yields thing w. #keys, #each, #[], #[]=
13
+ def with_players id
14
+ yield PLAYERS[id] ||= {}
15
+ end
16
+
17
+ # yields a thing with #each and #<<
18
+ def with_locations script
19
+ yield LOCATIONS[script] ||= []
20
+ LOCATIONS[script].delete_if{ |loc| loc.cast and Incident.new(script, loc.cast).run }
21
+ end
22
+
23
+ def method_missing(meth, *args, &blk)
24
+ puts "#{meth}: #{args.map(&:to_s).join(', ')}"
25
+ end
26
+
27
+ end
28
+ end
data/lib/ceml/incident.rb CHANGED
@@ -2,34 +2,39 @@ require 'set'
2
2
 
3
3
  module CEML
4
4
  class Incident
5
- attr_reader :script
6
- def this; players[@current_id]; end
5
+ attr_reader :script, :id, :players
6
+ def this; @players[@current_id]; end
7
7
  def roles; this[:roles] ||= Set.new; end
8
8
  def got; this[:received]; end
9
9
  def recognized; this[:recognized]; end
10
10
  def pc; this[:pc] ||= 0; end
11
11
  def qs_answers; this[:qs_answers] ||= Hash.new; end
12
12
 
13
- def initialize(script_text, id = rand(36**10).to_s(36))
14
- @script = CEML.parse(:script, script_text)
15
- @id = id
16
- end
17
-
18
- def players
19
- @players ||= CEML.delegate.players(@id) || {}
13
+ def initialize(script, cast = {}, id = rand(36**10).to_s(36))
14
+ @id = id
15
+ @script = Script === script ? script : CEML.parse(:script, script)
16
+ run do
17
+ cast.each{ |guy,role| add guy, role }
18
+ end
20
19
  end
21
20
 
22
21
  def add(id, *roles)
23
22
  obj = Hash === roles[-1] ? roles.pop : {}
24
- players[id] = obj.merge :roles => Set.new(roles)
23
+ @players[id] = obj.merge :roles => Set.new(roles)
25
24
  end
26
25
 
27
26
  def run
28
- :loop while players.keys.any? do |@current_id|
29
- # puts "trying: #{@current_id}: #{seq[pc]}"
30
- next unless seq[pc] and send(*seq[pc])
31
- CEML.delegate.send(*seq[pc] + [@iid, @current_id])
32
- this[:pc]+=1
27
+ CEML.delegate.with_players(@id) do |players|
28
+ @players = players
29
+ yield self if block_given?
30
+ :loop while players.keys.any? do |@current_id|
31
+ # puts "seq for roles: #{roles.inspect} #{seq.inspect}"
32
+ # puts "trying: #{@current_id}: #{seq[pc]}"
33
+ next unless seq[pc] and send(*seq[pc])
34
+ CEML.delegate.send(*seq[pc] + [@iid, @current_id])
35
+ this[:pc]+=1
36
+ end
37
+ @players = nil
33
38
  end
34
39
  end
35
40
 
@@ -68,7 +73,7 @@ module CEML
68
73
  def expand(role, var)
69
74
  role = nil if role == 'otherguy'
70
75
  role = role.to_sym if role
71
- players.each do |key, thing|
76
+ @players.each do |key, thing|
72
77
  next if key == @current_id
73
78
  next if role and not thing[:roles].include? role
74
79
  value = (thing[:qs_answers]||{})[var] and return value
@@ -81,7 +86,7 @@ module CEML
81
86
  # ==============
82
87
 
83
88
  def start
84
- roles.include? :agent or return false
89
+ # roles.include? :agent or return false
85
90
  true
86
91
  end
87
92
 
@@ -1,9 +1,13 @@
1
1
  module CEML
2
- module Instructions
2
+ module InstructionStatements
3
3
  def list
4
4
  [instruction_stmt] + more.elements.map(&:instruction_stmt)
5
5
  end
6
6
 
7
+ def instructions
8
+ self
9
+ end
10
+
7
11
  def validate_instructions!(allowed_roles)
8
12
  extra_roles = roles - allowed_roles
9
13
  raise "unrecognized rolenames: #{extra_roles.inspect}" unless extra_roles.empty?
data/lib/ceml/script.rb CHANGED
@@ -8,23 +8,13 @@ module CEML
8
8
  # = casting =
9
9
  # ===========
10
10
 
11
- def locations
12
- @locations ||= (delegate.locations(self) || [])
13
- end
14
-
15
- def launchable_location
16
- criteria = awaited_criteria.sort_by{ |c| [-c.complexity, c.min_match] }
17
- locations.find do |l|
18
- people_used = []
19
- criteria.all?{ |c| folks = l[c.hash] and c.satisfied_by_group?(folks, people_used) }
20
- end
21
- end
22
-
23
11
  # public
24
12
  def post candidate
25
- criteria = awaited_criteria.select{ |c| c =~ candidate }
26
- criteria.each{ |c| c.list_candidate(candidate) }
27
- not criteria.empty?
13
+ return unless candidate = candidate.dup.load(awaited_criteria)
14
+ delegate.with_locations(self) do |locs|
15
+ locs.each{ |l| l.push candidate }
16
+ locs << CastingLocation.create(self, candidate) if locs.none?(&:added)
17
+ end
28
18
  end
29
19
 
30
20
  def awaited_criteria
@@ -115,8 +105,8 @@ module CEML
115
105
  # ================
116
106
 
117
107
  def instructions
118
- return super if defined?(super)
119
108
  return self if Instructions === self
109
+ return super if defined?(super)
120
110
  nil
121
111
  end
122
112
 
@@ -34,8 +34,14 @@ grammar Casting
34
34
  rule role
35
35
  (rolename &and / range ws qualifier ws rolename / range ws rolename / qualifier ws rolename / rolename) {
36
36
  def name; if respond_to? :rolename then rolename.text_value else text_value end; end
37
- def min; if respond_to? :range then range.min else 2 end; end
38
- def max; if respond_to? :range then range.max else 10000 end; end
37
+ def min
38
+ return range.min if respond_to? :range
39
+ name =~ /s$/ ? 2 : 1
40
+ end
41
+ def max
42
+ return range.max if respond_to? :range
43
+ name =~ /s$/ ? 10000 : 1
44
+ end
39
45
  def qualifiers
40
46
  return [qualifier.text_value] if respond_to? :qualifier
41
47
  return []
@@ -40,7 +40,7 @@ grammar Scripts
40
40
  end
41
41
 
42
42
  rule instructions
43
- instruction_stmt more:(nl instruction_stmt)* <Instructions>
43
+ instruction_stmt more:(nl instruction_stmt)* <InstructionStatements>
44
44
  end
45
45
 
46
46
  end
data/lib/ceml.rb CHANGED
@@ -1,22 +1,22 @@
1
1
  require 'forwardable'
2
2
  require 'treetop'
3
3
 
4
- require 'ceml/casting'
5
- require 'ceml/instructions'
6
4
  require 'ceml/script'
5
+ require 'ceml/casting_statement'
6
+ require 'ceml/instruction_statements'
7
7
  require 'ceml/tt/lexer'
8
8
  require 'ceml/tt/casting'
9
9
  require 'ceml/tt/instructions'
10
10
  require 'ceml/tt/scripts'
11
11
 
12
+ require 'ceml/delegate'
12
13
  require 'ceml/casting_criterion'
13
14
  require 'ceml/incident'
14
15
 
15
16
  module CEML
16
17
  extend self
17
18
  attr_accessor :delegate
18
- @delegate = Class.new{ def method_missing(meth, *args, &blk);end }.new
19
- # puts "#{meth}: #{args.to_s.inspect}"
19
+ @delegate = Delegate.new
20
20
  end
21
21
 
22
22
  module CEML
data/test/helper.rb CHANGED
@@ -6,31 +6,47 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
6
  require 'ceml'
7
7
 
8
8
  class Test::Unit::TestCase
9
- def play script
10
- @e = CEML::Incident.new(script)
9
+ def play script = nil
10
+ @e = CEML::Incident.new(script) if script
11
+ yield
12
+ CEML::Delegate::PLAYERS.clear
11
13
  end
12
14
 
13
15
  def player id, *roles
14
- @e.add id, *roles
15
- @e.run
16
+ @e.run do |script|
17
+ script.add id, *roles
18
+ end
16
19
  end
17
20
 
18
21
  def asked id, rx
19
- p = @e.players[id]
22
+ assert g = CEML::Delegate::PLAYERS.values.find{ |game| game[id] }
23
+ p = g[id]
20
24
  assert_equal :ask, p[:said]
21
25
  assert_match rx, p[:q]
22
26
  p.delete :said
23
27
  end
24
28
 
29
+ def silent id
30
+ if g = CEML::Delegate::PLAYERS.values.find{ |game| game[id] }
31
+ p = g[id]
32
+ assert !p[:msg]
33
+ end
34
+ end
35
+
25
36
  def told id, rx
26
- p = @e.players[id]
37
+ assert g = CEML::Delegate::PLAYERS.values.find{ |game| game[id] }
38
+ p = g[id]
27
39
  assert_match rx, p[:msg]
28
40
  p.delete :said
29
41
  end
30
42
 
31
43
  def says id, str
32
- @e.players[id][:received] = str
33
- @e.run
34
- @e.players[id][:received] = nil
44
+ @e.run do |incident|
45
+ incident.players[id][:received] = str
46
+ end
47
+
48
+ CEML.delegate.with_players(@e.id) do |players|
49
+ players[id][:received] = nil
50
+ end
35
51
  end
36
52
  end
@@ -20,36 +20,64 @@ XXX
20
20
 
21
21
  class TestIncident < Test::Unit::TestCase
22
22
 
23
- SIGNUP_SCRIPT = "await 1 new signup\ntell signup: thanks"
24
- def test_signup_script
25
- s = CEML.parse(:script, SIGNUP_SCRIPT)
26
- c = CEML::Candidate.new('fred', ['new'], {}, nil, nil)
27
- s.post c
28
- assert s.launchable_location
23
+ def test_signup_1
24
+ s = CEML.parse(:script, "await 1 new signup\ntell signup: thanks")
25
+ play do
26
+ s.post CEML::Candidate.new('fred', ['new'], {})
27
+ told 'fred', /thanks/
28
+ end
29
+ end
30
+
31
+ def test_signup_2
32
+ s = CEML.parse(:script, "await 2 new signups\ntell signups: thanks")
33
+ play do
34
+ s.post CEML::Candidate.new('fred', ['new'], {})
35
+ silent 'fred'
36
+ s.post CEML::Candidate.new('wilma', ['old'], {})
37
+ silent 'fred'
38
+ s.post CEML::Candidate.new('betty', ['new'], {})
39
+ told 'fred', /thanks/
40
+ end
41
+ end
42
+
43
+ def test_await
44
+ s = CEML.parse(:script, "await a,b,c\ntell a: foo\ntell b: bar\ntell c: baz")
45
+ play do
46
+ s.post CEML::Candidate.new('fred', [], {})
47
+ silent 'fred'
48
+ s.post CEML::Candidate.new('wilma', [], {})
49
+ silent 'fred'
50
+ silent 'wilma'
51
+ s.post CEML::Candidate.new('betty', [], {})
52
+ told 'fred', /foo/
53
+ told 'betty', /baz/
54
+ end
29
55
  end
30
56
 
31
57
  def test_incident
32
- play COMPLIMENT_SCRIPT
33
- player :joe, :organizer, :agent
34
- player :bill, :agent
58
+ play COMPLIMENT_SCRIPT do
59
+ player :joe, :organizer, :agent
60
+ player :bill, :agent
35
61
 
36
- asked :joe, /^Describe/
37
- says :joe, 'red people'
62
+ asked :joe, /^Describe/
63
+ says :joe, 'red people'
38
64
 
39
- told :bill, /^Look for red people/
65
+ told :bill, /^Look for red people/
66
+ end
40
67
  end
41
68
 
42
69
  def test_askchain
43
- play ASKCHAIN_SCRIPT
44
- player :joe, :players, :agent
45
- player :bill, :players, :agent
46
-
47
- asked :joe, /favorite color/
48
- asked :bill, /favorite color/
49
- says :joe, "red"
50
- says :bill, "green"
51
- asked :joe, /with the color green/
52
- asked :bill, /with the color red/
70
+ play ASKCHAIN_SCRIPT do
71
+ player :joe, :players, :agent
72
+ player :bill, :players, :agent
73
+
74
+ asked :joe, /favorite color/
75
+ asked :bill, /favorite color/
76
+ says :joe, "red"
77
+ says :bill, "green"
78
+ asked :joe, /with the color green/
79
+ asked :bill, /with the color red/
80
+ end
53
81
  end
54
82
 
55
83
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
8
- - 1
9
- version: 0.3.1
7
+ - 4
8
+ - 0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Joe Edelman
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-26 00:00:00 -08:00
17
+ date: 2011-01-09 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -53,11 +53,13 @@ files:
53
53
  - examples/breakfast-exchange.ceml
54
54
  - examples/citizen-investigation.ceml
55
55
  - examples/high-fives.ceml
56
+ - examples/sample_delegate.rb
56
57
  - lib/ceml.rb
57
- - lib/ceml/casting.rb
58
58
  - lib/ceml/casting_criterion.rb
59
+ - lib/ceml/casting_statement.rb
60
+ - lib/ceml/delegate.rb
59
61
  - lib/ceml/incident.rb
60
- - lib/ceml/instructions.rb
62
+ - lib/ceml/instruction_statements.rb
61
63
  - lib/ceml/script.rb
62
64
  - lib/ceml/tt/casting.treetop
63
65
  - lib/ceml/tt/instructions.treetop