story-gen 0.0.1 → 0.0.2

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.
@@ -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