yhara-tickets 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/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
|
+
|