ceml 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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