story-gen 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ --protected
2
+ --hide-api private
data/README.md CHANGED
@@ -30,7 +30,12 @@ A simple story:
30
30
 
31
31
  "Hello, world!"
32
32
 
33
- It just prints "Hello, world!". No escape sequences for you, thus, for example, to start a new chapter you need to write something like this:
33
+ It just prints "Hello, world!". To print newline just write `newline` or `nl`:
34
+
35
+ "Hello, world!" newline
36
+ "Another line of helloness!" nl
37
+
38
+ Or you may insert newline inside the quotes:
34
39
 
35
40
  "
36
41
  Chapter I
@@ -50,7 +55,7 @@ A story is based on Facts. To state a Fact just write it:
50
55
 
51
56
  "John" loves "Liza"
52
57
 
53
- In the Fact expression you may use english and russian words (except keywords, see below), characters from "#№@$%^/-", integer numbers (e.g., `18`) and arbitrary double- or single-quoted strings (`"..."` or `'...'`). Trailing commas are ignored (`xxx yyy zzz,` is the same as `xxx yyy zzz`). The words are case-insensitive (so `loves` and `Loves` mean the same), quoted strings are case-sensitive (so `"John"`≠`"JOHN"`).
58
+ In the Fact expression you may use english and russian words (except keywords, see below), characters from "#№@$%/-", integer numbers (e.g., `18`) and arbitrary double- or single-quoted strings (`"..."` or `'...'`). Trailing commas are ignored (`xxx yyy zzz,` is the same as `xxx yyy zzz`). The words are case-insensitive (so `loves` and `Loves` mean the same), quoted strings are case-sensitive (so `"John"`≠`"JOHN"`).
54
59
 
55
60
  Let's state some Facts:
56
61
 
@@ -78,9 +83,9 @@ Here `X` is a variable. Variable names consist of underscores ("_") and capital
78
83
 
79
84
  You may use the captured variables in the statement after `then` keyword.
80
85
 
81
- To print the captured variable you just write it along with some quoted string:
86
+ To print the captured variable you just write it along with some quoted strings and newlines:
82
87
 
83
- "Let's meet " X;
88
+ "Let's meet " X "." nl;
84
89
 
85
90
  But wait... We have two boys! What does `if` choose as `X` then? The answer is: random. If there are several combinations of variables which fit the condition then a random combination is chosen.
86
91
 
@@ -134,6 +139,16 @@ Or combine `if`-s with some statement:
134
139
 
135
140
  This is like a classical `if ... else if ... else ...` but if multiple conditions are true then random one is chosen (instead of the first one, like in the classical `if-else`). The last `or` is the same as `else` in the classical `if-else` - it is chosen if all conditions are false.
136
141
 
142
+ The full form of `if`:
143
+
144
+ If <condition> [,] then <statement> [,]
145
+ or if <condition> [,] then <statement> [,]
146
+ or if <condition> [,] then <statement> [,]
147
+ ...
148
+ or <statement>
149
+
150
+ Here `[,]` means "optional comma".
151
+
137
152
  You may use captured variables to state a Fact:
138
153
 
139
154
  If X is a boy and Y is a girl then
@@ -148,7 +163,13 @@ Set multiple Facts false:
148
163
 
149
164
  If X is a boy then
150
165
  X not loves *
151
-
166
+
167
+ You may use the captured variables in another conditions (by prefixing them with hat "^"):
168
+
169
+ If X is a boy then
170
+ if ^X loves * then "We found "X" which is in love!"
171
+ or "We found "X" which is stll single."
172
+
152
173
  To combine several statements into one use a colon (":") with the final dot ("."):
153
174
 
154
175
  If X is a boy then:
@@ -257,3 +278,7 @@ Examples
257
278
  --------
258
279
 
259
280
  See them in "sample" directory!
281
+
282
+ You may find the "sample" directory with
283
+
284
+ story --samples
data/bin/story CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'story/compile'
3
3
  require 'optparse'
4
+ require 'story/samples_dir'
4
5
 
5
6
  $output_file = nil
6
7
  $input_file = nil
@@ -40,6 +41,10 @@ OptionParser.new do |opts|
40
41
  story.relations.each { |relation| output.puts relation }
41
42
  end
42
43
  end
44
+ opts.on "--samples", "Show directory with samples and exit" do
45
+ puts Story::SAMPLES_DIR
46
+ exit
47
+ end
43
48
  opts.on "-h", "--help", "Show this message and exit" do
44
49
  puts opts
45
50
  exit
@@ -158,6 +158,8 @@ class Story
158
158
 
159
159
  end
160
160
 
161
+ Above = ASTNode.new :var
162
+
161
163
  Asterisk = ASTNode.new
162
164
 
163
165
  # @!visibility private
@@ -173,6 +175,7 @@ class Story
173
175
  case part
174
176
  when String then part.to_rb
175
177
  when Var then at(part.pos) { part.name }
178
+ when Above[Var] then at(part.pos) { part.var.name }
176
179
  end <<
177
180
  ")"
178
181
  end.join_code("\n") << "\n" <<
@@ -197,6 +200,8 @@ class Story
197
200
  nil
198
201
  when Var then
199
202
  "#{at(arg.pos) { arg.name }} == #{var_arg_valuess}[#{i}]"
203
+ when Above[Var] then
204
+ "#{at(arg.pos) { arg.var.name }} == #{var_arg_valuess}[#{i}]"
200
205
  else
201
206
  "#{arg.to_rb} == #{var_arg_valuess}[#{i}]"
202
207
  end
@@ -222,6 +227,8 @@ class Story
222
227
  raise Parse::Error.new(arg.pos, "not supported")
223
228
  when Var then
224
229
  at(arg.pos) { arg.name }
230
+ when Above[Var] then
231
+ at(arg.pos) { arg.var.name }
225
232
  else
226
233
  arg.to_rb
227
234
  end
@@ -311,9 +318,14 @@ class Story
311
318
  private
312
319
 
313
320
  def to_code_(range_part)
321
+ to_code = lambda do |var, pos|
322
+ at(pos) { code << "(must_be(" << var.name << ", Integer); " << var.name << ")" }
323
+ end
324
+ #
314
325
  case range_part
315
326
  when Integer then range_part.to_rb
316
- when Var then at(range_part.pos) { code << "(must_be(" << range_part.name << ", Integer); " << range_part.name << ")" }
327
+ when Var then to_code.(range_part, range_part.pos)
328
+ when Above[Var] then to_code.(range_part.var, range_part.pos)
317
329
  else raise Parse::Error.new(pos, "what?")
318
330
  end
319
331
  end
@@ -401,6 +413,8 @@ class Story
401
413
  map << "#{var_arg_valuess}[#{i}]"
402
414
  known_var_index[var] = i
403
415
  end
416
+ when Above[Var] then
417
+ select << "#{at(arg.pos) { arg.var.name }} == #{var_arg_valuess}[#{i}]"
404
418
  else
405
419
  select << "#{arg.to_rb} == #{var_arg_valuess}[#{i}]"
406
420
  end
@@ -485,6 +499,7 @@ class Story
485
499
  to_code = lambda do |operand|
486
500
  case operand
487
501
  when Var then "#{var_arg_valuess}[#{e1.var_args_indexes[operand]}]"
502
+ when Above[Var] then at(operand.pos) { operand.var.name }
488
503
  else operand.to_rb
489
504
  end
490
505
  end
@@ -605,7 +620,7 @@ class Story
605
620
 
606
621
  rule :statement_tell do
607
622
  parts = many1 {
608
- _{ string } or _{ var }
623
+ _{ string } or _{ newline } or _{ var }
609
624
  } and
610
625
  _(Statement::Tell[parts])
611
626
  end
@@ -619,7 +634,7 @@ class Story
619
634
  _if_ and c = fact_expr and opt { comma } and _then_ and s = statement and [c, s]
620
635
  }
621
636
  or_delimiter = lambda {
622
- opt { _{ comma } or _{ semicolon } or _{ dot } } and _or_
637
+ opt { comma } and _or_
623
638
  }
624
639
  #
625
640
  c_and_s1 = if_then.() and
@@ -727,6 +742,7 @@ class Story
727
742
  _{ _not_ and act { negated = !negated } } or
728
743
  _{ v = value and act { args << v; relation_id << :* } } or
729
744
  _{ v = var and act { args << v; relation_id << :* } } or
745
+ _{ v = above_var and act { args << v; relation_id << :* } } or
730
746
  _{ w = word and act { relation_id << w.ru_downcase } }
731
747
  } and
732
748
  # act { relation_id.chomp!(",") } and
@@ -784,8 +800,8 @@ class Story
784
800
  scan("*") and _(Asterisk.new)
785
801
  end
786
802
 
787
- token :other_char, "any of `#№@$%^/'" do
788
- scan(/[\#\№\@\$\%\^\/]/)
803
+ token :other_char, "any of `#№@$%/'" do
804
+ scan(/[\#\№\@\$\%\/]/)
789
805
  end
790
806
 
791
807
  token :_for_, "`for'" do
@@ -824,6 +840,10 @@ class Story
824
840
  t = word_ and /^(times|раза?)$/ === t
825
841
  end
826
842
 
843
+ token :newline, "`newline'" do
844
+ t = word_ and /^(newline|nl)$/ === t and "\n"
845
+ end
846
+
827
847
  token :number, "number" do
828
848
  n = scan(/\d+/) and n.to_i
829
849
  end
@@ -855,10 +875,14 @@ class Story
855
875
  _(Var[n.ru_downcase, n])
856
876
  end
857
877
 
878
+ token :above_var, "^variable" do
879
+ scan("^") and v = var and _(Above[v])
880
+ end
881
+
858
882
  token :word, "word" do
859
883
  not_follows(
860
884
  :_not_, :_and_, :_or_, :_if_, :_then_, :var, :_while_, :_for_, :all,
861
- :times
885
+ :times, :newline
862
886
  ) and
863
887
  word_
864
888
  end
@@ -913,11 +937,11 @@ class Story
913
937
  end
914
938
 
915
939
  # begin
916
- # puts Story.compile(<<-STORY)
940
+ # eval(Story.compile(<<-STORY)).new.write
917
941
  #
918
- # ```puts(a)```
942
+ # "Hello, world!" newline
919
943
  # Either "John" loves "Liza",
920
- # or ("Hello, world, again!"; "John" kisses "Liza";).
944
+ # or ("Hello, world, again!" nl; "John" kisses "Liza";).
921
945
  # STORY
922
946
  # rescue Parse::Error => e
923
947
  # puts "error: #{e.pos.file}:#{e.pos.line}:#{e.pos.column}: #{e.message}"
@@ -0,0 +1,6 @@
1
+
2
+ class Story
3
+
4
+ SAMPLES_DIR = "#{File.dirname __FILE__}/../../sample"
5
+
6
+ end
@@ -10,49 +10,46 @@ require 'unique_names'
10
10
  names = UniqueNames.english_male
11
11
  ```
12
12
 
13
- "Today in the fight club the tournament is being held! Meet the participants:"
13
+ "Today in the fight club the tournament is being held! Meet the participants:" nl;
14
14
 
15
- 10 раз:
15
+ 10 times:
16
16
  ```n = names.pop```;
17
- N is the current participant;
18
17
  N is in;
19
- "
20
- - "N", the ";
21
- either "tiger", or "fox", or "bear", or ("horse"; N has hooves);
18
+ "- "N", the ";
19
+ either "tiger", or "fox", or "bear", or ("horse"; N has hooves);
22
20
  " ";
23
21
  either ("with powerful hands"; N uses "hands"),
24
22
  or ("with powerful feet"; N uses "feet"),
25
23
  or:
26
- if X is the current participant and X has hooves then (
27
- "with powerful hooves"; X uses "hooves"
24
+ if ^N has hooves then (
25
+ "with powerful hooves"; N uses "hooves"
28
26
  )
29
27
  or (
30
28
  "with sharp claws"; N uses "claws"
31
29
  )
32
30
  .
33
- N is not the current participant;
31
+ nl;
34
32
  .
35
- "
36
33
 
37
- Let the tournament begin!
34
+ nl
35
+ "Let the tournament begin!" nl
36
+ nl
38
37
 
39
- "
40
38
  While the tournament not complete:
41
39
  If X is in and Y is in and X != Y, then:
42
40
  (note: fight!)
43
41
  either (X won; Y lost), or (Y won; X lost);
44
- (note: yup, it should be made that existing variables can be used in
45
- conditions rather than capturing new ones only)
46
- if A won and B lost and A uses "hands" then ""A" has beaten "B". "
47
- or if A won and B lost and A uses "feet" then ""A" has kicked "B". "
48
- or if A won and B lost and A uses "hooves" then ""A" has kicked "B" and knocked him out. "
49
- or if A won and B lost and A uses "claws" then ""A" has scratched the whole "B". ";
50
- if A lost then (A not lost; A is not in);
51
- if A won then A not won;
42
+ if A won and B lost then:
43
+ if ^A uses "hands" then ""A" has beaten "B". "
44
+ or if ^A uses "feet" then ""A" has kicked "B". "
45
+ or if ^A uses "hooves" then ""A" has kicked "B" and knocked him out. "
46
+ or if ^A uses "claws" then ""A" has scratched the whole "B". ";
47
+ A not won; B not lost;
48
+ B is not in;
49
+ .
52
50
  .
53
51
  Or the tournament complete;
54
52
  .
55
- If X is in then ""X" is the winner!".
56
-
57
- "
58
- "
53
+ If X is in then ""X" is the winner!"
54
+ nl
55
+ nl
@@ -41,13 +41,14 @@ While X is a boy and X not loves *:
41
41
  either
42
42
  "After the lectures they went home and did their home tasks. "
43
43
  or:
44
- "After the lectures "X" caught "Y" and told her that he loves her! "
44
+ "After the lectures "X" caught "Y" and told her that he loves her! ";
45
45
  X loves Y.
46
46
  .
47
47
  .
48
48
  .
49
49
 
50
- "
51
-
52
- Then they all lived long and happy!
53
- "
50
+ nl
51
+ nl
52
+ "Then they all lived long and happy!"
53
+ nl
54
+ nl
@@ -10,49 +10,46 @@ require 'unique_names'
10
10
  names = UniqueNames.russian_male
11
11
  ```
12
12
 
13
- "Сегодня в бойцовском клубе проводится турнир! Поприветствуйте участников:"
13
+ "Сегодня в бойцовском клубе проводится турнир! Поприветствуйте участников:" nl;
14
14
 
15
15
  10 раз:
16
16
  ```n = names.pop```;
17
- N - текущий участник;
18
17
  N участвует в турнире;
19
- "
20
- - "N", ";
18
+ "- "N", ";
21
19
  либо "тигр", либо "лис", либо "медведь", либо ("конь"; N имеет копыта);
22
- " ";
20
+ " " ;
23
21
  либо ("с сильными руками"; N использует "руки"),
24
22
  либо ("с сильными ногами"; N использует "ноги"),
25
23
  либо:
26
- если X - текущий участник и X имеет копыта, то (
27
- "с мощными копытами"; X использует "копыта"
24
+ если ^N имеет копыта, то (
25
+ "с мощными копытами"; N использует "копыта"
28
26
  )
29
27
  или (
30
28
  "с острыми когтями"; N использует "когти"
31
29
  )
32
30
  .
33
- N - не текущий участник;
31
+ nl;
34
32
  .
35
- "
36
33
 
37
- Да начнется турнир!
34
+ nl
35
+ "Да начнется турнир!" nl
36
+ nl
38
37
 
39
- "
40
38
  Пока не конец турнира:
41
39
  Если X участвует в турнире и Y участвует в турнире и X != Y, то:
42
40
  (note: бой!)
43
41
  либо (X победил; Y проиграл), либо (Y победил; X проиграл);
44
- (note: дя, надо сделать, чтобы в условиях можно было существующие
45
- переменные использовать, а не только захватывать новые)
46
- если A победил и B проиграл и A использует "руки", то ""A" избил "B". "
47
- или если A победил и B проиграл и A использует "ноги", то ""A" запинал "B". "
48
- или если A победил и B проиграл и A использует "копыта", то ""A" лягнул "B" и вырубил его. "
49
- или если A победил и B проиграл и A использует "когти", то ""A" исцарапал всего "B". ";
50
- если A проиграл, то (A не проиграл; A не участвует в турнире);
51
- если A победил, то A не победил;
42
+ если A победил и B проиграл, то:
43
+ если ^A использует "руки", то ""A" избил "B". "
44
+ или если ^A использует "ноги", то ""A" запинал "B". "
45
+ или если ^A использует "копыта", то ""A" лягнул "B" и вырубил его. "
46
+ или если ^A использует "когти", то ""A" исцарапал всего "B". ";
47
+ A не победил; B не проиграл;
48
+ A не участвует в турнире;
49
+ .
52
50
  .
53
51
  Или конец турнира;
54
52
  .
55
- Если X участвует в турнире, то ""X" - победитель!".
56
-
57
- "
58
- "
53
+ Если X участвует в турнире, то ""X" - победитель!"
54
+ nl
55
+ nl
@@ -42,13 +42,14 @@
42
42
  либо
43
43
  "После лекций они пошли домой и делали уроки. "
44
44
  либо:
45
- "После лекций "X" поймал "Y" и сказал ей, что любит её! "
45
+ "После лекций "X" поймал "Y" и сказал ей, что любит её! ";
46
46
  X любит Y.
47
47
  .
48
48
  .
49
49
  .
50
50
 
51
- "
52
-
53
- И жили они долго и счастливо!
54
- "
51
+ nl
52
+ nl
53
+ "И жили они долго и счастливо!"
54
+ nl
55
+ nl
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: story-gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-01-10 00:00:00.000000000 Z
12
+ date: 2016-01-30 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Generate stories from descriptions based on Facts!
15
15
  email: various.furriness@gmail.com
@@ -22,6 +22,7 @@ files:
22
22
  - lib/any.rb
23
23
  - lib/fact.rb
24
24
  - lib/story/compile.rb
25
+ - lib/story/samples_dir.rb
25
26
  - lib/array/case_equal_fixed.rb
26
27
  - lib/array/separate.rb
27
28
  - lib/array/to_h.rb
@@ -40,7 +41,7 @@ files:
40
41
  - lib/object/map_.rb
41
42
  - lib/object/to_rb.rb
42
43
  - README.md
43
- - gem.gemspec
44
+ - .yardopts
44
45
  - sample/fight_club.sdl
45
46
  - sample/университет.sdl
46
47
  - sample/бойцовский_клуб.sdl
@@ -1,13 +0,0 @@
1
-
2
- Gem::Specification.new do |s|
3
- s.name = 'story-gen'
4
- s.version = '0.0.1'
5
- s.date = '2016-01-10'
6
- s.summary = "Story generator"
7
- s.description = "Generate stories from descriptions based on Facts!"
8
- s.authors = ["Various Furriness"]
9
- s.email = 'various.furriness@gmail.com'
10
- s.files = Dir["lib/**/*.rb"] + ["README.md", "gem.gemspec"] + Dir["sample/*"]
11
- s.executables = Dir["bin/*"].map { |f| File.basename(f) }
12
- s.license = 'MIT'
13
- end