razyk 0.0.1 → 0.1.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/lib/razyk/webapp.rb CHANGED
@@ -3,23 +3,33 @@ require "rack"
3
3
  require "tempfile"
4
4
  require "thread"
5
5
  require "stringio"
6
-
7
- require "razyk/graph"
6
+ require "json"
8
7
 
9
8
  module RazyK
10
9
  class WebApp
11
- def initialize
12
- reset
13
- end
14
-
15
- def reset
16
- @vm = nil
17
- @graph = 0
18
- @step = 0
19
- @thread = nil
20
- @port_in = StringIO.new
21
- @port_out = StringIO.new
22
- @queue = Queue.new
10
+ class InputStream
11
+ def initialize(str="")
12
+ @buf = str.b
13
+ @chars = []
14
+ end
15
+ def getbyte
16
+ if @buf
17
+ ret = @buf.unpack("C")[0]
18
+ @buf = @buf[1..-1]
19
+ if ret
20
+ @chars << ret
21
+ end
22
+ ret
23
+ else
24
+ nil
25
+ end
26
+ end
27
+ def wrote
28
+ @chars.pack("C*")
29
+ end
30
+ def remain
31
+ @buf || ""
32
+ end
23
33
  end
24
34
 
25
35
  def template(name)
@@ -27,7 +37,6 @@ module RazyK
27
37
  end
28
38
 
29
39
  def main_page(req)
30
- reset
31
40
  res = Rack::Response.new
32
41
  res.status = 200
33
42
  res.write File.read(template("main.html"))
@@ -44,96 +53,46 @@ module RazyK
44
53
  res
45
54
  end
46
55
 
47
- def reduce_thread(vm)
48
- while q = @queue.pop
49
- begin
50
- q.push(vm.reduce)
51
- rescue
52
- q.push($!)
53
- end
54
- end
55
- end
56
+ def parse(req)
57
+ expression = req.params["expression"] || "(OUT (I IN))"
56
58
 
57
- def set_program(req)
59
+ memory = {}
60
+ tree = RazyK::Parser.parse(expression, memory: memory)
58
61
  res = Rack::Response.new
59
- begin
60
- tree = RazyK::Parser.parse(req.params["program"])
61
- if req.params["mode"] == "true"
62
- root = Pair.new(:OUT, Pair.new(tree, :IN))
63
- @port_in = StringIO.new(req.params["stdin"] || "")
64
- recursive = false
65
- else
66
- root = tree
67
- recursive = true
68
- end
69
- # discard previous vm and thread
70
- if @thread
71
- @thread.kill
72
- @thread = nil
73
- end
74
- @vm = VM.new(root, @port_in, @port_out, recursive)
75
- # start Thread for reduction
76
- @thread = Thread.start do reduce_thread(@vm) end
77
- rescue
78
- res.status = 501
79
- puts $!.message, $@
80
- return res
81
- end
82
- res.header["Content-Type"] = "text/json"
83
- res.write('{"status":"success"}')
62
+ res.header["Content-Type"] = "application/json"
63
+ json_state = JSON::State.from_state(nil)
64
+ json_state.max_nesting = 0
65
+ res.write({
66
+ expression: tree.inspect,
67
+ nodes: tree.as_json,
68
+ }.to_json(json_state))
84
69
  res
85
70
  end
86
71
 
87
- def step(req)
88
- res = Rack::Response.new
89
- res.header["Content-Type"] = "text/plain"
90
- if @thread
91
- rep = Queue.new
92
- @queue.push(rep)
93
- ret = rep.pop
94
- if ret.is_a?(Exception)
95
- raise ret
96
- end
97
- if ret
98
- @step += 1
99
- end
100
- res.write("OK")
101
- else
102
- res.write("enter program first")
103
- end
104
- res
105
- end
106
-
107
- def stdout(req)
108
- res = Rack::Response.new
109
- res.header["Content-Type"] = "text/plain"
110
- res.write(@port_out.string.inspect)
111
- res
112
- end
72
+ def reduce(req)
73
+ stdin_read = req.params["stdin_read"] || ""
74
+ stdin_remain = req.params["stdin_remain"] || ""
75
+ stdout = req.params["stdout"] || ""
76
+ expression = req.params["expression"] || "(OUT (I IN))"
77
+ recursive = (req.params["recursive"] == "true")
78
+ port_in = InputStream.new(stdin_remain)
79
+ port_out = StringIO.new("")
113
80
 
114
- def expression(req)
81
+ memory = {}
82
+ tree = RazyK::Parser.parse(expression, memory: memory)
83
+ vm = VM.new(tree, port_in, port_out, recursive: recursive)
84
+ vm.reduce
115
85
  res = Rack::Response.new
116
- res.header["Content-Type"] = "text/plain"
117
- if @vm
118
- res.write(@vm.tree.inspect)
119
- else
120
- res.write("enter program first")
121
- end
122
- res
123
- end
124
-
125
- def graph(req)
126
- res = Rack::Response.new
127
- if @vm
128
- res.header["Content-Type"] = "image/svg+xml"
129
- tmpfile = Tempfile.new("razyk_graph")
130
- RazyK::Graph.graph(@vm.tree, :style => :dag).output(:svg => tmpfile.path)
131
- res.write(tmpfile.read)
132
- tmpfile.unlink
133
- else
134
- res.header["Content-Type"] = "text/plain"
135
- res.write("enter program first")
136
- end
86
+ res.header["Content-Type"] = "application/json"
87
+ json_state = JSON::State.from_state(nil)
88
+ json_state.max_nesting = 0
89
+ res.write({
90
+ expression: vm.tree.inspect,
91
+ nodes: vm.tree.as_json,
92
+ stdin_read: stdin_read + port_in.wrote,
93
+ stdin_remain: port_in.remain,
94
+ stdout: stdout + port_out.string,
95
+ }.to_json(json_state))
137
96
  res
138
97
  end
139
98
 
@@ -142,16 +101,10 @@ module RazyK
142
101
  case req.path
143
102
  when "/"
144
103
  res = main_page(req)
145
- when "/set_program"
146
- res = set_program(req)
147
- when "/step"
148
- res = step(req)
149
- when "/stdout"
150
- res = stdout(req)
151
- when "/expression"
152
- res = expression(req)
153
- when "/graph"
154
- res = graph(req)
104
+ when "/parse"
105
+ res = parse(req)
106
+ when "/reduce"
107
+ res = reduce(req)
155
108
  else
156
109
  res = not_found(req)
157
110
  end
@@ -2,68 +2,235 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>RazyK</title>
5
- <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
5
+ <meta charset="UTF-8">
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
9
+ <script type="text/javascript" src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
10
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
6
12
  <script type="text/javascript">
7
- function update() {
8
- if ($("#program_mode").is(":checked")) {
9
- $("#output_buffer").load("/stdout");
13
+ var RazyK = function(expression, nodes, stdin_read, stdin_remain, stdout, recursive) {
14
+ this.expression = expression;
15
+ this.nodes = nodes;
16
+ this.stdin_read = stdin_read;
17
+ this.stdin_remain = stdin_remain;
18
+ this.stdout = stdout;
19
+ this.recursive = recursive;
20
+ };
21
+
22
+ function draw(razyk) {
23
+ toggle_mode();
24
+ if ($("#lazyk_mode").is(":checked")) {
25
+ $("#stdin_progress").text(razyk.stdin_read + "_" + razyk.stdin_remain);
26
+ $("#stdout").text(razyk.stdout);
27
+ }
28
+ $("#expression").text(razyk.expression);
29
+
30
+ var max_depth_f = function(node, depth) {
31
+ if (node.children) {
32
+ return Math.max.apply(null, node.children.map(function(elm){ return max_depth_f(elm, depth+1); }));
33
+ }
34
+ return depth;
35
+ }
36
+ var max_width_f = function(node, depth, widths) {
37
+ if (widths[depth]) {
38
+ widths[depth] += 1;
39
+ } else {
40
+ widths[depth] = 1;
41
+ }
42
+ if (node.children) {
43
+ node.children.forEach(function(elm){
44
+ max_width_f(elm, depth+1, widths);
45
+ });
46
+ }
47
+ if (depth == 1) {
48
+ return Math.max.apply(null, Object.keys(widths).map(function(elm) { return widths[elm] }));
49
+ } else {
50
+ return null;
51
+ }
10
52
  }
11
- $("#expression").load("/expression");
12
- $("#graph").attr("src", "/graph");
53
+ var max_depth = max_depth_f(razyk.nodes, 1);
54
+ var max_width = max_width_f(razyk.nodes, 1, {});
55
+ $("div#graph").empty();
56
+ var svg = d3.select("div#graph").append("svg");
57
+ var width = max_width * 20;
58
+ var height = max_depth * 20;
59
+ if (width < 500) { width = 500; }
60
+ if (width < 1000) { width = 1000; }
61
+ svg.attr("width", width + 50);
62
+ svg.attr("height", height + 50);
63
+ var tree = d3.layout.tree().size([width, height]);
64
+ var nodes = tree.nodes(razyk.nodes);
65
+ // segments
66
+ svg.selectAll("path")
67
+ .data(tree.links(nodes))
68
+ .enter()
69
+ .append("path")
70
+ .attr("d", d3.svg.diagonal())
71
+ .attr("fill", "none")
72
+ .attr("stroke", "black")
73
+ .attr("stroke-width", 2)
74
+ .attr("transform", "translate(0, 15)");
75
+ // circles
76
+ var coloring = function(d) {
77
+ if (d.name == "") {
78
+ return "black";
79
+ } else if (d.name == "I") {
80
+ return "blue";
81
+ } else if (d.name == "K") {
82
+ return "green";
83
+ } else if (d.name == "S") {
84
+ return "red";
85
+ } else if (d.name == "OUT" || d.name == "PUTC") {
86
+ return "purple";
87
+ } else if (d.name == "IN") {
88
+ return "yellow";
89
+ } else {
90
+ return "black";
91
+ }
92
+ };
93
+ svg.selectAll("circle")
94
+ .data(nodes)
95
+ .enter()
96
+ .append("circle")
97
+ .attr("cx", function(d){return d.x;})
98
+ .attr("cy", function(d){return d.y+15;})
99
+ .attr("r", function(d) { if (d.name == "") { return 2; } else { return 10; } })
100
+ .style("fill", coloring);
13
101
  }
14
- function set_program() {
15
- $.post("/set_program",
16
- {
17
- program: $("#program").val(),
18
- mode: $("#program_mode").is(":checked"),
19
- stdin: $("#input_buffer").val()
20
- },
21
- function() {
22
- update();
23
- }, "json");
102
+
103
+ function set_program(cb) {
104
+ program = $("#program").val();
105
+ recursive = true;
106
+
107
+ if ($("#lazyk_mode").is(":checked")) {
108
+ program = "($OUT (" + program + " $IN))";
109
+ recursive = false;
110
+ }
111
+ window.razyk = new RazyK(program, {name: ""}, "", $("#input_buffer").val(), "", recursive);
112
+ $.post("/parse", {expression: program},
113
+ function(data) {
114
+ window.razyk.expression = data.expression;
115
+ window.razyk.nodes = data.nodes;
116
+ draw(window.razyk);
117
+ if (cb) { cb(); }
118
+ });
24
119
  }
25
120
 
26
- function step_reduction() {
27
- $.get("/step", {},
28
- function() {
29
- update();
121
+ function reduce(cb) {
122
+ r = window.razyk;
123
+ $.post("/reduce",
124
+ {expression: r.expression, stdin_read: r.stdin_read, stdin_remain: r.stdin_remain, stdout: r.stdout, recursive: r.recursive},
125
+ function(data) {
126
+ finished = r.expression == data.expression;
127
+ window.razyk = new RazyK(data.expression, data.nodes, data.stdin_read, data.stdin_remain, data.stdout, window.razyk.recursive);
128
+ draw(window.razyk);
129
+ if (cb) { cb(finished); }
30
130
  });
31
131
  }
32
132
 
133
+ function run() {
134
+ var wait = 500;
135
+ if (!window.running) { return; }
136
+ if ( window.razyk == undefined ) {
137
+ set_program(function() {
138
+ setTimeout(run, wait);
139
+ });
140
+ } else {
141
+ reduce(function(finished) {
142
+ if (!finished) {
143
+ setTimeout(run, wait);
144
+ } else {
145
+ window.running = false;
146
+ }
147
+ });
148
+ }
149
+ }
150
+
151
+ function start() {
152
+ window.running = true;
153
+ run();
154
+ }
155
+
156
+ function stop() {
157
+ window.running = false;
158
+ }
159
+
33
160
  function toggle_mode() {
34
- if ($("#program_mode").is(":checked")) {
35
- $("#inout_pane").show("fast");
161
+ if ($("#lazyk_mode").is(":checked")) {
162
+ $(".inout_pane").show("fast");
36
163
  } else {
37
- $("#inout_pane").hide("fast");
164
+ $(".inout_pane").hide("fast");
38
165
  }
39
166
  }
40
167
  </script>
41
168
  </head>
42
- <body>
43
- <p><div>enter program</div>
44
- <textarea id="program" cols="100">skki</textarea>
45
- </p>
46
- <p>
47
- <input type="checkbox" id="program_mode" onchange="toggle_mode();" />
48
- <span>LazyK application mode</span>
49
- </p>
50
- <p>
51
- <input type="button" value="set program" onclick="set_program();" />
52
- <input type="button" value="step" onclick="step_reduction();" />
53
- </p>
54
- <span id="inout_pane" style="display: none">
55
- <div>stdin</div>
56
- <textarea id="input_buffer" cols="100"></textarea>
57
- <div>stdout</div>
58
- <div id="output_buffer"></div>
59
- </span>
60
- <p>
61
- <div>combinator expression</div>
62
- <div id="expression"> </div>
63
- </p>
64
- <p>graph expression</p>
65
- <iframe id="graph" height="1000" width="100%">
66
- using iframe.
67
- </iframe>
169
+ <body class="bg-info">
170
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
171
+ <div class="container-fluid bg-info">
172
+ <div class="row"><div class="col-lg-1"></div><div class="col-lg-10">
173
+ <div class="row form-group">
174
+ <div class="col-lg-12">
175
+ <label for="program">enter program</label>
176
+ <textarea id="program" class="form-control" cols="100">SKKI</textarea>
177
+ </div>
178
+ </div>
179
+ <div class="row form-group">
180
+ <div class="col-sm-3">
181
+ <label class="checkbox-inline">
182
+ <input type="checkbox" id="lazyk_mode" onchange="toggle_mode();" /> LazyK application
183
+ </label>
184
+ </div>
185
+ <div class="col-sm-1"><input type="button" class="form-control btn btn-default" value="reset" onclick="set_program();" /></div>
186
+ <div class="col-sm-1"><input type="button" class="form-control btn btn-default" value="step" onclick="reduce();" /></div>
187
+ <div class="col-sm-1"><input type="button" class="form-control btn btn-default" value="run" onclick="start();" /></div>
188
+ <div class="col-sm-1"><input type="button" class="form-control btn btn-default" value="stop" onclick="stop();" /></div>
189
+ </div>
190
+ <div class="row inout_pane form-group" style="display: none">
191
+ <div class="col-lg-12">
192
+ <label for="input_buffer">stdin</label>
193
+ <div>
194
+ <textarea id="input_buffer" class="form-control" cols="100"></textarea>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ <div class="row">
199
+ </div>
200
+ <div class="row"></div>
201
+ <div class="row inout_pane" style="display: none">
202
+ <div class="col-lg-12">
203
+ <div class="panel panel-default">
204
+ <div class="panel-heading">
205
+ <div class="panel-title">stdin</div>
206
+ </div>
207
+ <div id="stdin_progress" class="panel-body"></div>
208
+ </div>
209
+ <div class="panel panel-default">
210
+ <div class="panel-heading">
211
+ <div class="panel-title">stdout</div>
212
+ </div>
213
+ <div id="stdout" class="panel-body"></div>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ <div class="row">
218
+ <div class="col-lg-12">
219
+ <div class="panel panel-default">
220
+ <div class="panel-heading"><div class="panel-title">combinator expression</div></div>
221
+ <div id="expression" class="panel-body"> </div>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ <div class="row">
226
+ <div class="col-lg-12">
227
+ <div class="panel panel-default">
228
+ <div class="panel-heading"><div class="panel-title">graph</div></div>
229
+ <div id="graph" class="panel-body"> </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </div><div class="col-lg-1"></div></div>
234
+ </div>
68
235
  </body>
69
236
  </html>