razyk 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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>