yhara-tickets 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +20 -0
- data/TODO +61 -0
- data/bin/tickets-server +101 -0
- data/db/migrate/001_create_tickets.rb +14 -0
- data/db/migrate/002_add_deleted_to_tickets.rb +13 -0
- data/db/migrate/003_add_timeouted_to_tickets.rb +13 -0
- data/dot.tickets.conf.sample +9 -0
- data/model/ticket.rb +36 -0
- data/public/biwascheme/MIT-LICENSE.txt +20 -0
- data/public/biwascheme/README +38 -0
- data/public/biwascheme/lib/biwascheme.js +101 -0
- data/public/biwascheme/lib/extra_lib.js +513 -0
- data/public/biwascheme/lib/io.js +326 -0
- data/public/biwascheme/lib/prototype.js +4320 -0
- data/public/biwascheme/lib/r6rs_lib.js +2388 -0
- data/public/biwascheme/lib/stackbase.js +1798 -0
- data/public/biwascheme/lib/webscheme_lib.js +762 -0
- data/public/js/builder.js +136 -0
- data/public/js/controls.js +965 -0
- data/public/js/dragdrop.js +975 -0
- data/public/js/effects.js +1130 -0
- data/public/js/scriptaculous.js +60 -0
- data/public/js/slider.js +275 -0
- data/public/js/sound.js +55 -0
- data/public/js/unittest.js +568 -0
- data/public/scm/main.scm +65 -0
- data/public/scm/ticket.scm +98 -0
- data/tickets.gemspec +41 -0
- data/view/index.xhtml +61 -0
- metadata +121 -0
data/README.rdoc
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
= tickets
|
2
|
+
|
3
|
+
Two-dimentional TODO manager.
|
4
|
+
|
5
|
+
== Install
|
6
|
+
|
7
|
+
gem install ramaze sequel sqlite3-ruby
|
8
|
+
gem install yhara-tickets
|
9
|
+
|
10
|
+
== Execute
|
11
|
+
|
12
|
+
tickets-server
|
13
|
+
|
14
|
+
then open http://localhost:7007/ in your browser.
|
15
|
+
|
16
|
+
in default, ~/.tickets.db is created to save tickets data.
|
17
|
+
|
18
|
+
== Configuration
|
19
|
+
|
20
|
+
write ~/.tickets.conf (see dot.tickets.conf.sample)
|
data/TODO
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
= before 1.x.x
|
2
|
+
|
3
|
+
- write server tests
|
4
|
+
- write client tests
|
5
|
+
- when origin is clicked
|
6
|
+
- new ticket appears on the filed
|
7
|
+
- request: /tickets/create
|
8
|
+
- when a ticket is clicked
|
9
|
+
- ticket info is shown in the hand
|
10
|
+
- when a ticket is moved
|
11
|
+
- request: /tickets/move? id, x, y
|
12
|
+
- when ticket title is cliced
|
13
|
+
- show ticket form
|
14
|
+
- when ok is clicked
|
15
|
+
- show new title in the hand
|
16
|
+
- show new title in the field
|
17
|
+
- request: /tickets/rename? id, new
|
18
|
+
- when delete button is clicked
|
19
|
+
- ask really delete the ticket
|
20
|
+
- when Y is clicked
|
21
|
+
- remove the ticket from the field
|
22
|
+
- clear the hand
|
23
|
+
- request: /tickets/delete? id
|
24
|
+
- when N is clicked
|
25
|
+
- do not remove the ticket from the field
|
26
|
+
- keep the hand
|
27
|
+
- no request
|
28
|
+
|
29
|
+
- refactor scheme code (use hashtable as object)
|
30
|
+
|
31
|
+
= before 0.4.0
|
32
|
+
|
33
|
+
- change orm to datamapper(or ActiveRecord?)
|
34
|
+
- implement tags
|
35
|
+
- different color
|
36
|
+
- show certain tag only
|
37
|
+
- tag 'Done' means fixed tags
|
38
|
+
|
39
|
+
= before 0.3.0
|
40
|
+
|
41
|
+
- memorize date when a ticket is created
|
42
|
+
- memorize date when a ticket is deleted
|
43
|
+
|
44
|
+
= before 0.2.0
|
45
|
+
|
46
|
+
- add Tickets::VERSION?
|
47
|
+
- edit ticket body
|
48
|
+
|
49
|
+
= before 0.1.0
|
50
|
+
|
51
|
+
+ fix ticket positions (center=0,0)
|
52
|
+
+ show deleted tickets (including timeouted ones)
|
53
|
+
+ automatically move tickets
|
54
|
+
|
55
|
+
+ Make it installable :-)
|
56
|
+
+ write gemspec (with rtask)
|
57
|
+
+ dependency (ramaze)
|
58
|
+
+ automatic migration in start.rb
|
59
|
+
+ save database on ~/.tickets.db
|
60
|
+
+ move executable to bin/
|
61
|
+
+ command line option (port, db)
|
data/bin/tickets-server
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ramaze'
|
4
|
+
require 'ramaze/setup'
|
5
|
+
|
6
|
+
# load config
|
7
|
+
conf_path = File.expand_path("~/.tickets.conf")
|
8
|
+
load conf_path if File.exist?(conf_path)
|
9
|
+
|
10
|
+
# update config
|
11
|
+
module Tickets
|
12
|
+
module Config
|
13
|
+
DB_PATH = File.expand_path("~/.tickets.db") unless defined?(DB_PATH)
|
14
|
+
PORT = 7007 unless defined?(PORT)
|
15
|
+
|
16
|
+
SHAKE_INTERVAL = 24 # hours
|
17
|
+
SHAKE_DISTANCE = 8 # pixels
|
18
|
+
|
19
|
+
BOARD_WIDTH = 600 # pixels
|
20
|
+
BOARD_HEIGHT = 600 # pixels
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# connect database
|
25
|
+
$LOAD_PATH.unshift(File.expand_path("../", File.dirname(__FILE__)))
|
26
|
+
require 'model/ticket'
|
27
|
+
|
28
|
+
# controllers
|
29
|
+
class MainController < Ramaze::Controller
|
30
|
+
def index
|
31
|
+
Ticket.shake! if Ticket.needs_shaking?
|
32
|
+
end
|
33
|
+
|
34
|
+
def track
|
35
|
+
@tickets = Ticket.filter(:deleted => true).reverse_order(:id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ConfigController < Ramaze::Controller
|
40
|
+
map '/config'
|
41
|
+
deny_layout :all
|
42
|
+
|
43
|
+
def board_size
|
44
|
+
"(xy . (#{Tickets::Config::BOARD_WIDTH} . #{Tickets::Config::BOARD_HEIGHT}))"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class TicktesController < Ramaze::Controller
|
49
|
+
map '/tickets'
|
50
|
+
deny_layout :all
|
51
|
+
|
52
|
+
def create
|
53
|
+
ticket = Ticket.new
|
54
|
+
ticket.save
|
55
|
+
|
56
|
+
"(id . #{ticket.id})"
|
57
|
+
end
|
58
|
+
|
59
|
+
def list
|
60
|
+
tickets = Ticket.filter(:deleted => false).all
|
61
|
+
|
62
|
+
'(' +
|
63
|
+
tickets.map{|ticket|
|
64
|
+
"(#{ticket.id} #{ticket.title.inspect} #{ticket.emergency} #{ticket.importance})"
|
65
|
+
}.join(' ') +
|
66
|
+
')'
|
67
|
+
end
|
68
|
+
|
69
|
+
def move
|
70
|
+
raise "must be via POST" unless request.post?
|
71
|
+
ticket = Ticket.find(:id => request["id"])
|
72
|
+
raise "ticket not found" unless ticket
|
73
|
+
|
74
|
+
ticket.update(:emergency => request["x"],
|
75
|
+
:importance => request["y"])
|
76
|
+
"#t"
|
77
|
+
end
|
78
|
+
|
79
|
+
def rename
|
80
|
+
raise "must be via POST" unless request.post?
|
81
|
+
ticket = Ticket.find(:id => request["id"])
|
82
|
+
raise "ticket not found" unless ticket
|
83
|
+
|
84
|
+
ticket.update(:title => request["title"])
|
85
|
+
"#t"
|
86
|
+
end
|
87
|
+
|
88
|
+
def delete
|
89
|
+
raise "must be via POST" unless request.post?
|
90
|
+
ticket = Ticket.find(:id => request["id"])
|
91
|
+
raise "ticket not found" unless ticket
|
92
|
+
|
93
|
+
ticket.update(:deleted => true)
|
94
|
+
"#t"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
Ramaze.setup do
|
99
|
+
option.port = Tickets::Config::PORT
|
100
|
+
end
|
101
|
+
Ramaze.start
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateTickets < Sequel::Migration
|
2
|
+
def up
|
3
|
+
create_table :tickets do
|
4
|
+
primary_key :id
|
5
|
+
integer :importance, :null => false, :default => 0
|
6
|
+
integer :emergency, :null => false, :default => 0
|
7
|
+
string :title, :null => false, :default => ""
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def down
|
12
|
+
drop_table :tickets
|
13
|
+
end
|
14
|
+
end
|
data/model/ticket.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'sequel/extensions/migration'
|
3
|
+
Sequel::Model.plugin(:schema) # for table_exists?
|
4
|
+
|
5
|
+
$db = Sequel.sqlite(Tickets::Config::DB_PATH)
|
6
|
+
|
7
|
+
class Ticket < Sequel::Model(:tickets)
|
8
|
+
@@last_shook = Time.now
|
9
|
+
|
10
|
+
def self.needs_shaking?
|
11
|
+
(Time.now - @@last_shook) > Tickets::Config::SHAKE_INTERVAL*60*60
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.shake!
|
15
|
+
Ticket.each do |ticket|
|
16
|
+
pos = ticket.emergency
|
17
|
+
dir = (ticket.importance < 0 ? 1 : -1)
|
18
|
+
newpos = pos + Tickets::Config::SHAKE_DISTANCE * dir
|
19
|
+
if newpos > (Tickets::Config::BOARD_WIDTH / 2)
|
20
|
+
newpos = (Tickets::Config::BOARD_WIDTH / 2)
|
21
|
+
end
|
22
|
+
|
23
|
+
ticket.update(:emergency => newpos) if newpos != pos
|
24
|
+
if newpos < -(Tickets::Config::BOARD_WIDTH / 2)
|
25
|
+
ticket.update(:deleted => true, :timeouted => true)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@@last_shook = Time.now
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
unless Ticket.table_exists?
|
33
|
+
migration_dir = File.expand_path("../db/migrate/",
|
34
|
+
File.dirname(__FILE__))
|
35
|
+
Sequel::Migrator.apply($db, migration_dir)
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007-2009 Yutaka Hara (http://route477.net)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
! BiwaScheme
|
2
|
+
|
3
|
+
BiwaScheme is a (yet another) Scheme interpreter written in JavaScript.
|
4
|
+
|
5
|
+
official page: http://mono.kmc.gr.jp/~yhara/w/?BiwaScheme
|
6
|
+
|
7
|
+
!! Files
|
8
|
+
|
9
|
+
!!! Core
|
10
|
+
:lib/biwascheme.js:for application(see hello_world.html)
|
11
|
+
:lib/stackbase.js:interpreter
|
12
|
+
|
13
|
+
!!! Libraries
|
14
|
+
:lib/r6rs_lib.js:library (R6RS functions)
|
15
|
+
:lib/webscheme_lib.js:library (functions which needs a browser)
|
16
|
+
:lib/extra_lib.js:library (other functions)
|
17
|
+
:lib/prototype.js:see http://prototypejs.org/
|
18
|
+
|
19
|
+
!!! Tests
|
20
|
+
:index.html:REPL (read-eval-print-loop)
|
21
|
+
:test/spec.html:Unit test of the interpreter (requires JSSpec)
|
22
|
+
:test/spidermonkey.sh:runner for SpiderMonkey (requires console_test.js)
|
23
|
+
:test/console_test.js:runner for CScript
|
24
|
+
:test/browser_test.js:runner for browsers (requires console_test.js)
|
25
|
+
:test/JSSpec.js:see http://jania.pe.kr/aw/moin.cgi/JSSpec
|
26
|
+
:test/JSSpec.css:used by JSSpec.js
|
27
|
+
:test/diff_match_patch.js:used by JSSpec.js
|
28
|
+
|
29
|
+
!! Acknowledgements
|
30
|
+
|
31
|
+
* Kent Dyvbig, Three implementation models for scheme
|
32
|
+
** http://www.cs.indiana.edu/~dyb/pubs/3imp.pdf
|
33
|
+
|
34
|
+
!! Contact
|
35
|
+
|
36
|
+
yhara (HARA Yutaka)
|
37
|
+
yhara/at/kmc.gr.jp
|
38
|
+
http://mono.kmc.gr.jp/~yhara/
|
@@ -0,0 +1,101 @@
|
|
1
|
+
//for application
|
2
|
+
|
3
|
+
function dump(){}
|
4
|
+
|
5
|
+
var BiwaScheme = {
|
6
|
+
Version: 0.5,
|
7
|
+
|
8
|
+
// load library and execute proc after loading
|
9
|
+
require: function(src, check, proc){
|
10
|
+
var script = document.createElement('script')
|
11
|
+
script.src = src;
|
12
|
+
document.body.appendChild(script);
|
13
|
+
|
14
|
+
var checker = new Function("return !!(" + check + ")");
|
15
|
+
|
16
|
+
if(checker()) proc();
|
17
|
+
else setTimeout(function(){ checker() ? proc() : setTimeout(arguments.callee, 10); }, 10);
|
18
|
+
}
|
19
|
+
};
|
20
|
+
|
21
|
+
(function(){ //local namespace
|
22
|
+
var require = BiwaScheme.require;
|
23
|
+
|
24
|
+
// taken from prototype.js 1.6.0
|
25
|
+
var readAttribute = function(element, name) {
|
26
|
+
if (/*Prototype.Browser.IE*/ !!(window.attachEvent && !window.opera)){
|
27
|
+
var t = {
|
28
|
+
names: {
|
29
|
+
'class': 'className',
|
30
|
+
'for': 'htmlFor'
|
31
|
+
},
|
32
|
+
values: {
|
33
|
+
_getAttr: function(element, attribute) {
|
34
|
+
return element.getAttribute(attribute, 2);
|
35
|
+
},
|
36
|
+
_getAttrNode: function(element, attribute) {
|
37
|
+
var node = element.getAttributeNode(attribute);
|
38
|
+
return node ? node.value : "";
|
39
|
+
},
|
40
|
+
_getEv: function(element, attribute) {
|
41
|
+
var attribute = element.getAttribute(attribute);
|
42
|
+
return attribute ? attribute.toString().slice(23, -2) : null;
|
43
|
+
},
|
44
|
+
_flag: function(element, attribute) {
|
45
|
+
return $(element).hasAttribute(attribute) ? attribute : null;
|
46
|
+
},
|
47
|
+
style: function(element) {
|
48
|
+
return element.style.cssText.toLowerCase();
|
49
|
+
},
|
50
|
+
title: function(element) {
|
51
|
+
return element.title;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
};
|
55
|
+
if (t.values[name]) return t.values[name](element, name);
|
56
|
+
if (t.names[name]) name = t.names[name];
|
57
|
+
if (name.indexOf(':') > -1){
|
58
|
+
return (!element.attributes || !element.attributes[name]) ? null :
|
59
|
+
element.attributes[name].value;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
return element.getAttribute(name);
|
63
|
+
}
|
64
|
+
|
65
|
+
// main
|
66
|
+
var script = (function(e){
|
67
|
+
if(e.id == '_firebugConsole'){
|
68
|
+
return arguments.callee(document.body);
|
69
|
+
}
|
70
|
+
else if(e.nodeName.toLowerCase() == 'script'){
|
71
|
+
return e;
|
72
|
+
}
|
73
|
+
else{
|
74
|
+
return arguments.callee(e.lastChild);
|
75
|
+
}
|
76
|
+
})(document);
|
77
|
+
|
78
|
+
var src = readAttribute(script, 'src');
|
79
|
+
var dir = src.match(/(.*)biwascheme.js/)[1];
|
80
|
+
|
81
|
+
require(dir+'prototype.js', 'window.$$', function(){
|
82
|
+
require(dir+'stackbase.js', 'window.BiwaScheme.CoreEnv', function(){
|
83
|
+
require(dir+'r6rs_lib.js', 'window.BiwaScheme.CoreEnv["+"]', function(){
|
84
|
+
require(dir+'webscheme_lib.js', 'window.BiwaScheme.CoreEnv["getelem"]', function(){
|
85
|
+
require(dir+'extra_lib.js', 'window.BiwaScheme.CoreEnv["print"]', function(){
|
86
|
+
require(dir+'io.js', 'window.JSIO', function(){
|
87
|
+
var onError = function(e){
|
88
|
+
puts(e.message);
|
89
|
+
throw(e);
|
90
|
+
}
|
91
|
+
var biwascheme = new BiwaScheme.Interpreter(onError);
|
92
|
+
try{
|
93
|
+
biwascheme.evaluate(script.innerHTML, Prototype.emptyFunction);
|
94
|
+
}
|
95
|
+
catch(e){
|
96
|
+
onError(e);
|
97
|
+
}
|
98
|
+
})})})})})});
|
99
|
+
})();
|
100
|
+
|
101
|
+
//vim: set ft=javascript:
|
@@ -0,0 +1,513 @@
|
|
1
|
+
|
2
|
+
if( typeof(BiwaScheme)!='object' ) BiwaScheme={}; with(BiwaScheme) {
|
3
|
+
define_libfunc("html-escape", 1, 1, function(ar){
|
4
|
+
assert_string(ar[0]);
|
5
|
+
return ar[0].escapeHTML();
|
6
|
+
});
|
7
|
+
BiwaScheme.inspect_objs = function(objs){
|
8
|
+
return objs.map(function(obj){
|
9
|
+
if(obj.inspect)
|
10
|
+
return obj.inspect();
|
11
|
+
else
|
12
|
+
return Object.inspect($H(obj));
|
13
|
+
}).join(" ");
|
14
|
+
};
|
15
|
+
define_libfunc("inspect", 1, null, function(ar){
|
16
|
+
return BiwaScheme.inspect_objs(ar);
|
17
|
+
});
|
18
|
+
define_libfunc("inspect!", 1, null, function(ar){
|
19
|
+
return puts(BiwaScheme.inspect_objs(ar));
|
20
|
+
});
|
21
|
+
|
22
|
+
//
|
23
|
+
// json
|
24
|
+
//
|
25
|
+
// json->sexp
|
26
|
+
// Array -> list
|
27
|
+
// Object -> alist
|
28
|
+
// (number, boolean, string,
|
29
|
+
//
|
30
|
+
BiwaScheme.json2sexp = function(json){
|
31
|
+
switch(true){
|
32
|
+
case Object.isNumber(json) ||
|
33
|
+
Object.isString(json) ||
|
34
|
+
json === true || json === false:
|
35
|
+
return json;
|
36
|
+
case Object.isArray(json):
|
37
|
+
return json.map(function(item){
|
38
|
+
return json2sexp(item);
|
39
|
+
}).to_list();
|
40
|
+
case typeof(json) == "object":
|
41
|
+
var ls = nil;
|
42
|
+
for(key in json){
|
43
|
+
ls = new Pair(new Pair(key, json2sexp(json[key])),
|
44
|
+
ls);
|
45
|
+
}
|
46
|
+
return ls;
|
47
|
+
default:
|
48
|
+
throw new Error("json->sexp: detected invalid value for json: "+Object.inspect(json));
|
49
|
+
}
|
50
|
+
throw new Bug("must not happen");
|
51
|
+
}
|
52
|
+
define_libfunc("json->sexp", 1, 1, function(ar){
|
53
|
+
return json2sexp(ar[0]);
|
54
|
+
})
|
55
|
+
|
56
|
+
//
|
57
|
+
//from Gauche
|
58
|
+
//
|
59
|
+
|
60
|
+
define_libfunc("string-concat", 1, 1, function(ar){
|
61
|
+
assert_pair(ar[0]);
|
62
|
+
return ar[0].to_array().join("");
|
63
|
+
})
|
64
|
+
define_libfunc("string-split", 2, 2, function(ar){
|
65
|
+
assert_string(ar[0]);
|
66
|
+
assert_string(ar[1]);
|
67
|
+
return ar[0].split(ar[1]).to_list();
|
68
|
+
})
|
69
|
+
define_libfunc("string-join", 1, 2, function(ar){
|
70
|
+
assert_pair(ar[0]);
|
71
|
+
var delim = ""
|
72
|
+
if(ar[1]){
|
73
|
+
assert_string(ar[1]);
|
74
|
+
delim = ar[1];
|
75
|
+
}
|
76
|
+
return ar[0].to_array().join(delim);
|
77
|
+
})
|
78
|
+
|
79
|
+
define_libfunc("intersperse", 2, 2, function(ar){
|
80
|
+
var item = ar[0], ls = ar[1];
|
81
|
+
assert_pair(ls);
|
82
|
+
|
83
|
+
var ret = [];
|
84
|
+
ls.to_array().reverse().each(function(x){
|
85
|
+
ret.push(x);
|
86
|
+
ret.push(item);
|
87
|
+
});
|
88
|
+
ret.pop();
|
89
|
+
return ret.to_list();
|
90
|
+
});
|
91
|
+
|
92
|
+
//(define-macro (foo x) body ...)
|
93
|
+
//(define-macro foo lambda)
|
94
|
+
|
95
|
+
var rearrange_args = function (expected, given) {
|
96
|
+
var args = [];
|
97
|
+
var dotpos = (new Compiler).find_dot_pos(expected);
|
98
|
+
if (dotpos == -1)
|
99
|
+
args = given;
|
100
|
+
else {
|
101
|
+
for (var i = 0; i < dotpos; i++)
|
102
|
+
args[i] = given[i];
|
103
|
+
args[i] = given.slice(i).to_list();
|
104
|
+
}
|
105
|
+
return args;
|
106
|
+
}
|
107
|
+
define_syntax("define-macro", function(x){
|
108
|
+
var head = x.cdr.car;
|
109
|
+
var expected_args;
|
110
|
+
if(head instanceof Pair){
|
111
|
+
var name = head.car;
|
112
|
+
expected_args = head.cdr;
|
113
|
+
var body = x.cdr.cdr;
|
114
|
+
var lambda = new Pair(Sym("lambda"),
|
115
|
+
new Pair(expected_args,
|
116
|
+
body))
|
117
|
+
}
|
118
|
+
else{
|
119
|
+
var name = head;
|
120
|
+
var lambda = x.cdr.cdr.car;
|
121
|
+
expected_args = lambda.cdr.car;
|
122
|
+
}
|
123
|
+
|
124
|
+
//[close, n_frees, do_body, next]
|
125
|
+
var opc = Compiler.compile(lambda);
|
126
|
+
if(opc[1] != 0)
|
127
|
+
throw new Bug("you cannot use free variables in macro expander (or define-macro must be on toplevel)")
|
128
|
+
var cls = [opc[2]];
|
129
|
+
|
130
|
+
TopEnv[name.name] = new Syntax(function(sexp){
|
131
|
+
var given_args = sexp.to_array();
|
132
|
+
|
133
|
+
given_args.shift();
|
134
|
+
|
135
|
+
var intp = new Interpreter();
|
136
|
+
var args = rearrange_args(expected_args, given_args);
|
137
|
+
var result = intp.invoke_closure(cls, args);
|
138
|
+
return result;
|
139
|
+
})
|
140
|
+
})
|
141
|
+
|
142
|
+
var macroexpand_1 = function(x){
|
143
|
+
if(x instanceof Pair){
|
144
|
+
if(x.car instanceof Symbol && TopEnv[x.car.name] instanceof Syntax){
|
145
|
+
var transformer = TopEnv[x.car.name];
|
146
|
+
x = transformer.transform(x);
|
147
|
+
}
|
148
|
+
else
|
149
|
+
throw new Error("macroexpand-1: `" + to_write_ss(x) + "' is not a macro");
|
150
|
+
}
|
151
|
+
return x;
|
152
|
+
}
|
153
|
+
define_syntax("%macroexpand", function(x){
|
154
|
+
var expanded = (new Interpreter).expand(x.cdr.car);
|
155
|
+
return [Sym("quote"), expanded].to_list();
|
156
|
+
});
|
157
|
+
define_syntax("%macroexpand-1", function(x){
|
158
|
+
var expanded = macroexpand_1(x.cdr.car);
|
159
|
+
return [Sym("quote"), expanded].to_list();
|
160
|
+
});
|
161
|
+
|
162
|
+
define_libfunc("macroexpand", 1, 1, function(ar){
|
163
|
+
return (new Interpreter).expand(ar[0]);
|
164
|
+
});
|
165
|
+
define_libfunc("macroexpand-1", 1, 1, function(ar){
|
166
|
+
return macroexpand_1(ar[0]);
|
167
|
+
});
|
168
|
+
|
169
|
+
define_libfunc("print", 1, null, function(ar){
|
170
|
+
ar.map(function(item){
|
171
|
+
puts(to_display(item), true);
|
172
|
+
})
|
173
|
+
puts(""); //newline
|
174
|
+
})
|
175
|
+
define_syntax("let1", function(x){
|
176
|
+
//(let1 vari expr body ...)
|
177
|
+
//=> ((lambda (var) body ...) expr)
|
178
|
+
var vari = x.cdr.car;
|
179
|
+
var expr = x.cdr.cdr.car;
|
180
|
+
var body = x.cdr.cdr.cdr;
|
181
|
+
|
182
|
+
return new Pair(new Pair(Sym("lambda"),
|
183
|
+
new Pair(new Pair(vari, nil),
|
184
|
+
body)),
|
185
|
+
new Pair(expr, nil));
|
186
|
+
})
|
187
|
+
|
188
|
+
define_libfunc("write-to-string", 1, 1, function(ar){
|
189
|
+
return to_write(ar[0]);
|
190
|
+
});
|
191
|
+
define_libfunc("read-from-string", 1, 1, function(ar){
|
192
|
+
assert_string(ar[0]);
|
193
|
+
return new Parser(ar[0]).getObject();
|
194
|
+
});
|
195
|
+
|
196
|
+
//
|
197
|
+
// srfi
|
198
|
+
//
|
199
|
+
|
200
|
+
// srfi-1 (list)
|
201
|
+
define_libfunc("iota", 1, 3, function(ar){
|
202
|
+
var count = ar[0];
|
203
|
+
var start = ar[1] || 0;
|
204
|
+
var step = (ar[2]===undefined) ? 1 : ar[2];
|
205
|
+
assert_integer(count);
|
206
|
+
assert_number(start);
|
207
|
+
assert_number(step);
|
208
|
+
|
209
|
+
var a = [], n = start;
|
210
|
+
for(var i=0; i<count; i++){
|
211
|
+
a.push(n);
|
212
|
+
n += step;
|
213
|
+
}
|
214
|
+
return a.to_list();
|
215
|
+
});
|
216
|
+
|
217
|
+
// srfi-6 & Gauche (string port)
|
218
|
+
BiwaScheme.Port.StringOutput = Class.create(Port, {
|
219
|
+
initialize: function($super){
|
220
|
+
this.buffer = [];
|
221
|
+
$super(false, true);
|
222
|
+
},
|
223
|
+
put_string: function(str){
|
224
|
+
this.buffer.push(str);
|
225
|
+
},
|
226
|
+
output_string: function(str){
|
227
|
+
return this.buffer.join("");
|
228
|
+
}
|
229
|
+
});
|
230
|
+
BiwaScheme.Port.StringInput = Class.create(Port, {
|
231
|
+
initialize: function($super){
|
232
|
+
$super(true, false);
|
233
|
+
},
|
234
|
+
get_string: function(after){
|
235
|
+
}
|
236
|
+
});
|
237
|
+
define_libfunc("open-input-string", 1, 1, function(ar){
|
238
|
+
assert_string(ar[0]);
|
239
|
+
return new Port.StringInput(ar[0]);
|
240
|
+
})
|
241
|
+
define_libfunc("open-output-string", 0, 0, function(ar){
|
242
|
+
return new Port.StringOutput();
|
243
|
+
})
|
244
|
+
define_libfunc("get-output-string", 1, 1, function(ar){
|
245
|
+
assert_port(ar[0]);
|
246
|
+
if(!(ar[0] instanceof Port.StringOutput))
|
247
|
+
throw new Error("get-output-string: port must be made by 'open-output-string'");
|
248
|
+
return ar[0].output_string();
|
249
|
+
})
|
250
|
+
|
251
|
+
// srfi-19 (time)
|
252
|
+
//
|
253
|
+
// // constants
|
254
|
+
//time-duration
|
255
|
+
//time-monotonic
|
256
|
+
//time-process
|
257
|
+
//time-tai
|
258
|
+
//time-thread
|
259
|
+
//time-utc
|
260
|
+
// Current time and clock resolution
|
261
|
+
define_libfunc("current-date", 0, 1, function(ar){
|
262
|
+
//todo: tz-offset (ar[1])
|
263
|
+
return new Date();
|
264
|
+
})
|
265
|
+
//
|
266
|
+
//current-julian-day -> jdn
|
267
|
+
//current-modified-julian-day -> mjdn
|
268
|
+
//current-time [time-type] -> time
|
269
|
+
//time-resolution [time-type] -> integer
|
270
|
+
// // Time object and accessors
|
271
|
+
//make-time type nanosecond second -> time
|
272
|
+
//time? object -> boolean
|
273
|
+
//time-type time -> time-type
|
274
|
+
//time-nanosecond time -> integer
|
275
|
+
//time-second time -> integer
|
276
|
+
//set-time-type! time time-type
|
277
|
+
//set-time-nanosecond! time integer
|
278
|
+
//set-time-second! time integer
|
279
|
+
//copy-time time1 -> time2
|
280
|
+
// // Time comparison procedures
|
281
|
+
//time<=? time1 time2 -> boolean
|
282
|
+
//time<? time1 time2 -> boolean
|
283
|
+
//time=? time1 time2 -> boolean
|
284
|
+
//time>=? time1 time2 -> boolean
|
285
|
+
//time>? time1 time2 -> boolean
|
286
|
+
// // Time arithmetic procedures
|
287
|
+
//time-difference time1 time2 -> time-duration
|
288
|
+
//time-difference! time1 time2 -> time-duration
|
289
|
+
//add-duration time1 time-duration -> time
|
290
|
+
//add-duration! time1 time-duration -> time
|
291
|
+
//subtract-duration time1 time-duration -> time
|
292
|
+
//subtract-duration! time1 time-duration -> time
|
293
|
+
// Date object and accessors
|
294
|
+
// make-date
|
295
|
+
define_libfunc("date?", 1, 1, function(ar){
|
296
|
+
return (ar[0] instanceof Date);
|
297
|
+
})
|
298
|
+
define_libfunc("date-nanosecond", 1, 1, function(ar){
|
299
|
+
assert_date(ar[0]);
|
300
|
+
return ar[0].getMilliseconds() * 1000000;
|
301
|
+
})
|
302
|
+
define_libfunc("date-millisecond", 1, 1, function(ar){ // not srfi-19
|
303
|
+
assert_date(ar[0]);
|
304
|
+
return ar[0].getMilliseconds();
|
305
|
+
})
|
306
|
+
define_libfunc("date-second", 1, 1, function(ar){
|
307
|
+
assert_date(ar[0]);
|
308
|
+
return ar[0].getSeconds();
|
309
|
+
})
|
310
|
+
define_libfunc("date-minute", 1, 1, function(ar){
|
311
|
+
assert_date(ar[0]);
|
312
|
+
return ar[0].getMinutes();
|
313
|
+
})
|
314
|
+
define_libfunc("date-hour", 1, 1, function(ar){
|
315
|
+
assert_date(ar[0]);
|
316
|
+
return ar[0].getHours();
|
317
|
+
})
|
318
|
+
define_libfunc("date-day", 1, 1, function(ar){
|
319
|
+
assert_date(ar[0]);
|
320
|
+
return ar[0].getDate();
|
321
|
+
})
|
322
|
+
define_libfunc("date-month", 1, 1, function(ar){
|
323
|
+
assert_date(ar[0]);
|
324
|
+
return ar[0].getMonth() + 1; //Jan = 0 in javascript..
|
325
|
+
})
|
326
|
+
define_libfunc("date-year", 1, 1, function(ar){
|
327
|
+
assert_date(ar[0]);
|
328
|
+
return ar[0].getFullYear();
|
329
|
+
})
|
330
|
+
//date-zone-offset
|
331
|
+
//date-year-day
|
332
|
+
define_libfunc("date-week-day", 1, 1, function(ar){
|
333
|
+
assert_date(ar[0]);
|
334
|
+
return ar[0].getDay();
|
335
|
+
})
|
336
|
+
//date-week-number
|
337
|
+
|
338
|
+
// Time/Date/Julian Day/Modified Julian Day Converters
|
339
|
+
// (snipped)
|
340
|
+
|
341
|
+
// Date to String/String to Date Converters
|
342
|
+
// TODO: support locale
|
343
|
+
// * date_names
|
344
|
+
// * ~f 5.2 sec
|
345
|
+
// * ~p AM/PM
|
346
|
+
// * ~X 2007/01/01
|
347
|
+
BiwaScheme.date_names = {
|
348
|
+
weekday: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
349
|
+
full_weekday: ["Sunday", "Monday", "Tuesday",
|
350
|
+
"Wednesday", "Thursday", "Friday", "Saturday"],
|
351
|
+
month: ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
352
|
+
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
353
|
+
full_month: ["January", "February", "March", "April",
|
354
|
+
"May", "June", "July", "August", "September",
|
355
|
+
"Octorber", "November", "December"]
|
356
|
+
}
|
357
|
+
|
358
|
+
BiwaScheme.date2string = function(date, format){
|
359
|
+
var zeropad = function(n){ return n<10 ? "0"+n : ""+n };
|
360
|
+
var spacepad = function(n){ return n<10 ? " "+n : ""+n };
|
361
|
+
|
362
|
+
var getter = {
|
363
|
+
a: function(x){ return date_names.weekday[x.getDay()] },
|
364
|
+
A: function(x){ return date_names.full_weekday[x.getDay()] },
|
365
|
+
b: function(x){ return date_names.month[x.getMonth()] },
|
366
|
+
B: function(x){ return date_names.full_month[x.getMonth()] },
|
367
|
+
c: function(x){ return x.toString() },
|
368
|
+
d: function(x){ return zeropad(x.getDate()) },
|
369
|
+
D: function(x){ return getter.d(x) + getter.m(x) + getter.y(x); },
|
370
|
+
e: function(x){ return spacepad(x.getDate()) },
|
371
|
+
f: function(x){ return x.getSeconds() + x.getMilliseconds()/1000; },
|
372
|
+
h: function(x){ return date_names.month[x.getMonth()] },
|
373
|
+
H: function(x){ return zeropad(x.getHours()) },
|
374
|
+
I: function(x){ var h = x.getHours(); return zeropad(h<13 ? h : h-12) },
|
375
|
+
j: function(x){ throw new Bug("not implemented: day of year") },
|
376
|
+
k: function(x){ return spacepad(x.getHours()) },
|
377
|
+
l: function(x){ var h = x.getHours(); return spacepad(h<13 ? h : h-12) },
|
378
|
+
m: function(x){ return zeropad(x.getMonth()) },
|
379
|
+
M: function(x){ return zeropad(x.getMinutes()) },
|
380
|
+
n: function(x){ return "\n" },
|
381
|
+
N: function(x){ throw new Bug("not implemented: nanoseconds") },
|
382
|
+
p: function(x){ return x.getHours()<13 ? "AM" : "PM" },
|
383
|
+
r: function(x){ return getter.I(x) + ":" + getter.M(x) + ":" + getter.S(x) + " " + getter.p(x) },
|
384
|
+
s: function(x){ return Math.floor(x.getTime() / 1000) },
|
385
|
+
S: function(x){ return zeropad(x.getSeconds()) },
|
386
|
+
t: function(x){ return "\t" },
|
387
|
+
T: function(x){ return getter.H(x) + ":" + getter.M(x) + ":" + getter.S(x) },
|
388
|
+
U: function(x){ throw new Bug("not implemented: weeknum(0~, Sun)") },
|
389
|
+
V: function(x){ throw new Bug("not implemented: weeknum(1~, Sun?)") },
|
390
|
+
w: function(x){ return x.getDay() },
|
391
|
+
W: function(x){ throw new Bug("not implemented: weeknum(0~, Mon)") },
|
392
|
+
x: function(x){ throw new Bug("not implemented: weeknum(1~, Mon)") },
|
393
|
+
X: function(x){ return getter.Y(x) + "/" + getter.m(x) + "/" + getter.d(x) },
|
394
|
+
y: function(x){ return x.getFullYear() % 100 },
|
395
|
+
Y: function(x){ return x.getFullYear() },
|
396
|
+
z: function(x){ throw new Bug("not implemented: time-zone") },
|
397
|
+
Z: function(x){ throw new Bug("not implemented: symbol time zone") },
|
398
|
+
1: function(x){ throw new Bug("not implemented: ISO-8601 year-month-day format") },
|
399
|
+
2: function(x){ throw new Bug("not implemented: ISO-8601 hour-minute-second-timezone format") },
|
400
|
+
3: function(x){ throw new Bug("not implemented: ISO-8601 hour-minute-second format") },
|
401
|
+
4: function(x){ throw new Bug("not implemented: ISO-8601 year-month-day-hour-minute-second-timezone format") },
|
402
|
+
5: function(x){ throw new Bug("not implemented: ISO-8601 year-month-day-hour-minute-second format") }
|
403
|
+
}
|
404
|
+
|
405
|
+
return format.replace(/~([\w1-5~])/g, function(_, x){
|
406
|
+
var func = getter[x];
|
407
|
+
if(func)
|
408
|
+
return func(date);
|
409
|
+
else if(x == "~")
|
410
|
+
return "~";
|
411
|
+
else
|
412
|
+
return x;
|
413
|
+
})
|
414
|
+
}
|
415
|
+
|
416
|
+
// date->string
|
417
|
+
define_libfunc("date->string", 1, 2, function(ar){
|
418
|
+
assert_date(ar[0]);
|
419
|
+
|
420
|
+
if(ar[1]){
|
421
|
+
assert_string(ar[1]);
|
422
|
+
return date2string(ar[0], ar[1]);
|
423
|
+
}
|
424
|
+
else
|
425
|
+
return ar[0].toString();
|
426
|
+
})
|
427
|
+
// string->date
|
428
|
+
|
429
|
+
define_libfunc("parse-date", 1, 1, function(ar){ // not srfi-19
|
430
|
+
assert_string(ar[0]);
|
431
|
+
return new Date(Date.parse(ar[0]));
|
432
|
+
})
|
433
|
+
|
434
|
+
//
|
435
|
+
// srfi-38 (write/ss)
|
436
|
+
//
|
437
|
+
var user_write_ss = function(ar){
|
438
|
+
puts(write_ss(ar[0]), true);
|
439
|
+
}
|
440
|
+
define_libfunc("write/ss", 1, 2, user_write_ss);
|
441
|
+
define_libfunc("write-with-shared-structure", 1, 2, user_write_ss);
|
442
|
+
define_libfunc("write*", 1, 2, user_write_ss); //from Gauche(STklos)
|
443
|
+
|
444
|
+
//
|
445
|
+
// srfi-43 (vector library)
|
446
|
+
//
|
447
|
+
define_libfunc("vector-append", 2, null, function(ar){
|
448
|
+
var vec = [];
|
449
|
+
return vec.concat.apply(vec, ar);
|
450
|
+
})
|
451
|
+
|
452
|
+
//
|
453
|
+
// Regular Expression
|
454
|
+
//
|
455
|
+
var assert_regexp = function(obj, fname){
|
456
|
+
if(!(obj instanceof RegExp))
|
457
|
+
throw new Error(fname + ": regexp required, but got " + to_write(obj));
|
458
|
+
}
|
459
|
+
|
460
|
+
//Function: string->regexp string &keyword case-fold
|
461
|
+
define_libfunc("string->regexp", 1, 1, function(ar){
|
462
|
+
assert_string(ar[0], "string->regexp");
|
463
|
+
return new RegExp(ar[0]); //todo: case-fold
|
464
|
+
})
|
465
|
+
//Function: regexp? obj
|
466
|
+
define_libfunc("regexp?", 1, 1, function(ar){
|
467
|
+
return (ar[0] instanceof RegExp);
|
468
|
+
})
|
469
|
+
//Function: regexp->string regexp
|
470
|
+
define_libfunc("regexp->string", 1, 1, function(ar){
|
471
|
+
assert_regexp(ar[0], "regexp->string");
|
472
|
+
return ar[0].toString().slice(1, -1); //cut '/'
|
473
|
+
})
|
474
|
+
|
475
|
+
define_libfunc("regexp-exec", 2, 2, function(ar){
|
476
|
+
var rexp = ar[0];
|
477
|
+
if(Object.isString(ar[0])){
|
478
|
+
rexp = new RegExp(ar[0]);
|
479
|
+
}
|
480
|
+
assert_regexp(rexp, "regexp-exec");
|
481
|
+
assert_string(ar[1], "regexp-exec");
|
482
|
+
var ret = rexp.exec(ar[1])
|
483
|
+
return (ret === null) ? false : ret.to_list();
|
484
|
+
})
|
485
|
+
|
486
|
+
// //Function: rxmatch regexp string
|
487
|
+
// define_libfunc("rxmatch", 1, 1, function(ar){
|
488
|
+
// assert_regexp(ar[0], "rxmatch");
|
489
|
+
// assert_string(ar[1], "rxmatch");
|
490
|
+
// return ar[0].match(ar[1]);
|
491
|
+
// });
|
492
|
+
//Function: rxmatch-start match &optional (i 0)
|
493
|
+
//Function: rxmatch-end match &optional (i 0)
|
494
|
+
//Function: rxmatch-substring match &optional (i 0)
|
495
|
+
//Function: rxmatch-num-matches match
|
496
|
+
//Function: rxmatch-after match &optional (i 0)
|
497
|
+
//Function: rxmatch-before match &optional (i 0)
|
498
|
+
//Generic application: regmatch &optional index
|
499
|
+
//Generic application: regmatch 'before &optional index
|
500
|
+
//Generic application: regmatch 'after &optional index
|
501
|
+
//Function: regexp-replace regexp string substitution
|
502
|
+
//Function: regexp-replace-all regexp string substitution
|
503
|
+
//Function: regexp-replace* string rx1 sub1 rx2 sub2 ...
|
504
|
+
//Function: regexp-replace-all* string rx1 sub1 rx2 sub2 ...
|
505
|
+
//Function: regexp-quote string
|
506
|
+
//Macro: rxmatch-let match-expr (var ...) form ...
|
507
|
+
//Macro: rxmatch-if match-expr (var ...) then-form else-form
|
508
|
+
//Macro: rxmatch-cond clause ...
|
509
|
+
//Macro: rxmatch-case string-expr clause ...
|
510
|
+
|
511
|
+
}
|
512
|
+
|
513
|
+
|