├── .gitignore ├── README.md ├── autotest.sh ├── build2.sh ├── evaluator.rb ├── grammar2.garb ├── inter2.sh ├── interpreter.rb ├── lib.rb ├── lisp.garb ├── mark.garb ├── parser.rb ├── program.garb ├── proto1.rb ├── proto2.rb ├── repr.rb ├── rule.rb ├── run2.sh ├── stream.rb ├── test_evaluator.rb ├── test_garbanzo.rb ├── test_grammar2.rb ├── test_lib.rb ├── test_parser.rb ├── test_proto1.rb ├── test_proto2.rb ├── test_repr.rb └── test_stream.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *.tmp.garb 4 | *.dump 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## How to run: 2 | 3 | 1. Install the latest version of [Ruby](https://www.ruby-lang.org/ja/downloads/) 4 | 1. Install gem (Stackprof) `gem install stackprof` 5 | 1. `sh build2.sh` to generate prerequisite syntax from "grammar2.garb" and run "program.garb", the main source code. 6 | 1. `ruby test_garbanzo.rb` to run testcases. 7 | 1. After running `build2.sh`, run `inter2.sh` to launch interactive environment. 8 | 9 | Please visit wiki for more details. (not up-to-date) 10 | 11 | 12 | This research is supported by Cybozu-Lab Youth. 13 | http://labs.cybozu.co.jp/recruit/youth.html 14 | -------------------------------------------------------------------------------- /autotest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | trap exit INT 4 | 5 | while true 6 | do 7 | ruby test_garbanzo.rb 8 | date 9 | fswatch -1 . 10 | done 11 | -------------------------------------------------------------------------------- /build2.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | ruby proto1.rb grammar2.garb > predef.tmp.garb 4 | cat predef.tmp.garb program.garb > test.tmp.garb 5 | 6 | ruby proto2.rb test.tmp.garb 7 | -------------------------------------------------------------------------------- /evaluator.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require './repr.rb' 3 | require './lib.rb' 4 | require './rule.rb' 5 | require './stream.rb' 6 | 7 | module Garbanzo 8 | # analyzeのためのやつ 9 | class Repr::GarbObject 10 | private 11 | # 環境を受け取ることができる```proc'''を返す. 12 | def analyze 13 | proc { self } 14 | end 15 | 16 | public 17 | def analyzed 18 | @analyzed ||= analyze 19 | return @analyzed 20 | end 21 | 22 | def refresh_analyzed 23 | @analyzed = nil 24 | end 25 | end 26 | 27 | class Repr::Store 28 | @@commands = {} 29 | @@callcount = Hash.new { 0 } 30 | 31 | def self.command(commandname, *arguments) 32 | @@commands[commandname] = lambda { |store| 33 | @@callcount[commandname] += 1 34 | proc { |env| 35 | yield(*arguments.map {|aname| store.get_raw(aname) }, env) 36 | } 37 | } 38 | end 39 | 40 | def self.debug_print 41 | $stderr.puts @@callcount 42 | end 43 | 44 | def self.operator(opname, *args) 45 | raise "invalid argument list" if args.length % 2 == 1 46 | 47 | arglist = args.each_slice(2).map {|name, type| 48 | [name, type] 49 | } 50 | 51 | @@commands[opname] = lambda { |store| 52 | proc { |env| 53 | @@callcount[opname] += 1 54 | param = arglist.map {|name_type| 55 | e = store.evaluate_sub_expr(name_type[0], env) 56 | unless e.is_a? name_type[1] 57 | raise "operator `#{opname}' type mismatch: #{name_type}, not #{e}" 58 | end 59 | e 60 | } 61 | yield(*param, env) 62 | } 63 | } 64 | end 65 | 66 | # operator and command definiton 67 | ## operators 68 | ### arithmetic and logical operators 69 | operator("add", "left", Repr::Num, "right", Repr::Num) do |left, right| 70 | Repr::num(left.num + right.num) 71 | end 72 | 73 | operator("sub", "left", Repr::Num, "right", Repr::Num) do |left, right| 74 | Repr::num(left.num - right.num) 75 | end 76 | 77 | operator("mult", "left", Repr::Num, "right", Repr::Num) do |left, right| 78 | Repr::num(left.num * right.num) 79 | end 80 | 81 | operator("div", "left", Repr::Num, "right", Repr::Num) do |left, right| 82 | Repr::num(left.num / right.num) 83 | end 84 | 85 | operator("mod", "left", Repr::Num, "right", Repr::Num) do |left, right| 86 | Repr::num(left.num % right.num) 87 | end 88 | 89 | operator("equal", "left", Repr::GarbObject, "right", Repr::GarbObject) do |left, right| 90 | Repr::bool(left == right) 91 | end 92 | 93 | operator("notequal", "left", Repr::GarbObject, "right", Repr::GarbObject) do |left, right| 94 | Repr::bool(left != right) 95 | end 96 | 97 | operator("lessthan", "left", Repr::Num, "right", Repr::Num) do |left, right| 98 | Repr::bool(left.num < right.num) 99 | end 100 | 101 | operator("and", "left", Repr::Bool, "right", Repr::Bool) do |left, right| 102 | Repr::bool(left.value && right.value) 103 | end 104 | 105 | operator("or", "left", Repr::Bool, "right", Repr::Bool) do |left, right| 106 | Repr::bool(left.value || right.value) 107 | end 108 | 109 | operator("not", "value", Repr::Bool) do |value| 110 | Repr::bool(!value.value) 111 | end 112 | 113 | ### datastore operators 114 | operator("set", "object", Repr::Store, "key", Repr::GarbObject, "value", Repr::GarbObject) do |object, key, value| 115 | object[key] = value 116 | end 117 | 118 | operator("get", "object", Repr::Store, "key", Repr::GarbObject) do |object, key| 119 | result = object[key] 120 | raise "GET: undefined key #{object.inspect} for #{key.inspect}" unless result 121 | result 122 | end 123 | 124 | operator("size", "object", Repr::Store) do |object| 125 | object.size 126 | end 127 | 128 | operator("remove", "object", Repr::Store, "key", Repr::GarbObject) do |object, key| 129 | object.remove(key) 130 | end 131 | 132 | operator("exist", "object", Repr::Store, "key", Repr::GarbObject) do |object, key| 133 | object.exist(key) 134 | end 135 | 136 | operator("getprevkey", "object", Repr::Store, "origin", Repr::GarbObject) do |object, key| 137 | object.get_prev_key(key) 138 | end 139 | 140 | operator("getnextkey", "object", Repr::Store, "origin", Repr::GarbObject) do |object, key| 141 | object.get_next_key(key) 142 | end 143 | 144 | operator("insertprev", "object", Repr::Store, "origin", Repr::GarbObject, 145 | "key", Repr::GarbObject, "value", Repr::GarbObject) do |object, origin, key, value| 146 | object.insert_prev(origin, key, value) 147 | end 148 | 149 | operator("insertnext", "object", Repr::Store, "origin", Repr::GarbObject, 150 | "key", Repr::GarbObject, "value", Repr::GarbObject) do |object, origin, key, value| 151 | object.insert_next(origin, key, value) 152 | end 153 | 154 | operator("firstkey", "object", Repr::Store) do |object| 155 | object.first_key 156 | end 157 | 158 | operator("lastkey", "object", Repr::Store) do |object| 159 | object.last_key 160 | end 161 | 162 | ### string operators 163 | operator("append", "left", Repr::String, "right", Repr::String) do |left, right| 164 | Repr::string(left.value + right.value) 165 | end 166 | 167 | operator("charat", "string", Repr::String, "index", Repr::Num) do |string, index| 168 | if index.num < string.value.length 169 | Repr::string(string.value[index.num]) 170 | else 171 | raise "CHARAT: index out of string's length" 172 | end 173 | end 174 | 175 | operator("length", "string", Repr::String) do |string| 176 | Repr::num(string.value.length) 177 | end 178 | 179 | operator("tocode", "string", Repr::String) do |string| 180 | if string.value.size > 0 181 | Repr::num(string.value.ord) 182 | else 183 | raise "TOCODE: empty string" 184 | end 185 | end 186 | 187 | operator("fromcode", "num", Repr::Num) do |num| 188 | Repr::string(num.num.chr(Encoding::UTF_8)) 189 | end 190 | 191 | 192 | ### parsing operators 193 | operator("token") do |env| 194 | s = env.dot["/"]["source"] 195 | 196 | s.parse_token 197 | end 198 | 199 | operator("fail", "message", Repr::GarbObject) do |message, env| 200 | s = env.dot['/']['source'] 201 | line = s.line 202 | column = s.column 203 | 204 | ex = Rule::ParseError.new(message, line, column) 205 | ex.set_backtrace("fail") 206 | 207 | raise ex 208 | end 209 | 210 | def self.deepest_error(key_exception_table) 211 | key, deepest = key_exception_table.max_by {|k, e| [e.line, e.column]} 212 | 213 | if key 214 | deepest.backtrace.push key.inspect 215 | 216 | return deepest 217 | else 218 | return Rule::ParseError.new("empty choice") 219 | end 220 | end 221 | 222 | 223 | command("choice", "children") do |children, evaluator| 224 | s = evaluator.dot["/"]["source"] 225 | state = s.copy_state 226 | errors = {} 227 | 228 | result = nil 229 | # puts "choice called" 230 | # puts state 231 | 232 | children.each_key { |k, v| 233 | # p k, v 234 | begin 235 | s.set_state(state) 236 | result = evaluator.evaluate(v) 237 | # puts "result : #{res}" 238 | break 239 | rescue Rule::ParseError => e 240 | errors[k] = e 241 | end 242 | } 243 | 244 | if result 245 | result 246 | else 247 | raise deepest_error(errors) 248 | end 249 | end 250 | 251 | operator("terminal", "string", Repr::String) do |string, evaluator| 252 | s = evaluator.dot["/"]["source"] 253 | s.parse_terminal(string) 254 | end 255 | 256 | operator("many", "parser", Repr::GarbObject) do |parser, evaluator| 257 | i = 0 258 | result = Repr::store({}) 259 | s = evaluator.dot['/']["source"] 260 | state = s.copy_state 261 | 262 | begin 263 | while true 264 | result[i.to_repr] = evaluator.evaluate(parser) 265 | i += 1 266 | state = s.copy_state # すくなくとも,ここまでは成功した,という意味でのパーサの状態 267 | end 268 | rescue Rule::ParseError # 失敗した時,直前までの成功のところまで巻き戻す. 269 | s.set_state(state) 270 | end 271 | 272 | result 273 | end 274 | 275 | command("parsestring") do |evaluator| 276 | s = evaluator.dot["/"]["source"] 277 | s.parse_string 278 | end 279 | 280 | operator("oneof", "string", Repr::String) do |string, evaluator| 281 | source = evaluator.dot["/"]["source"] 282 | source.one_of(string) 283 | end 284 | 285 | operator("noneof", "string", Repr::String) do |string, evaluator| 286 | source = evaluator.dot["/"]["source"] 287 | source.none_of(string) 288 | end 289 | 290 | command("regex") do |regex, evaluator| 291 | unless regex.is_a? Repr::String 292 | raise "regex command requires `regex' as String" 293 | end 294 | 295 | re = Regexp.new("^" + regex.value, Regexp::MULTILINE) 296 | 297 | source.regex_match(re) 298 | end 299 | 300 | def self.cached_choice(children, cache, evaluator) 301 | s = evaluator.dot["/"]["source"] 302 | pos = s.index 303 | state = s.copy_state 304 | errors = {} 305 | 306 | # キャッシュのテーブル自体が作られてなかったら 307 | cache[pos] = {}.to_repr unless cache.exist(pos).value == true 308 | 309 | children.each_key {|k, v| 310 | # $stderr.puts "#{children.size.inspect}, #{s.line.inspect}, #{k.inspect}" 311 | 312 | # 各自を試す.まず,キャッシュに入っているかどうか. 313 | if cache[pos].exist(k).value 314 | entry = cache[pos][k] # キャッシュデータが出てくる. 315 | 316 | if entry['status'].value == 'success' # 成功していた場合 317 | result = entry['value'] 318 | s.set_state(entry['next']) 319 | return result 320 | elsif entry['status'].value == 'fail' # 失敗していた場合 321 | errors[k] = entry['value'] 322 | next 323 | else 324 | raise "cannot reach!" 325 | end 326 | else # キャッシュに当たんなかった.仕方ないので実際に試す. 327 | # $stderr.puts ["miss!", k, pos.num].inspect 328 | 329 | begin 330 | s.set_state(state) 331 | res = evaluator.evaluate(v) 332 | 333 | # キャッシュに貯める 334 | cache[pos][k] = Repr::store({ "status".to_repr => "success".to_repr, 335 | "value".to_repr => res, 336 | "next".to_repr => s.copy_state }) 337 | 338 | # $stderr.puts "success!" 339 | return res 340 | rescue Rule::ParseError => e 341 | errors[k] = e 342 | 343 | # 失敗情報をキャッシュに貯める 344 | cache[pos][k] = Repr::store({ "status".to_repr => "fail".to_repr, "value".to_repr => e }) 345 | # $stderr.puts "failure!" 346 | end 347 | end 348 | } 349 | 350 | raise deepest_error(errors) 351 | end 352 | 353 | def self.precrule(table, prec, evaluator) 354 | rules = [] 355 | # まず,tableから優先度がprec以下のルールを抽出 356 | table.each_key {|k, v| 357 | if k.is_a?(Repr::String) && k.value.start_with?("@") # 特殊エントリーなので飛ばす 358 | next 359 | end 360 | 361 | unless v.is_a?(Repr::Store) && v.exist('prec').value && v.exist('parser').value 362 | raise "precrule: invalid table entry: #{v.inspect}" 363 | end 364 | 365 | p = v.get_raw('prec').num 366 | if p <= prec.num 367 | rules << [k, v] 368 | end 369 | } 370 | 371 | # 次に,さっきのやつをソート 372 | rules.sort_by! {|kv| 373 | -kv[1].get_raw('prec').num 374 | } 375 | 376 | children = {}.to_repr 377 | 378 | # 最後に,データストア形式に直す 379 | rules.each do |kv| 380 | children[kv[0]] = kv[1]['parser'] 381 | end 382 | 383 | # 順番に試す. 384 | cached_choice(children, table['@cache'], evaluator) 385 | end 386 | 387 | operator("precrule", "table", Repr::Store, "prec", Repr::Num) do |table, prec, evaluator| 388 | precrule(table, prec, evaluator) 389 | end 390 | 391 | operator("withcache", "table", Repr::Store) do |table, evaluator| 392 | cachekey = "@cache" 393 | needclear = table.exist(cachekey).value == false # 元々キャッシュがなかったのなら,クリアする必要がある. 394 | prec = 1000.to_repr 395 | 396 | if needclear 397 | # $stderr.puts "allocate cache" 398 | 399 | table[cachekey] = {}.to_repr 400 | begin 401 | precrule(table, prec, evaluator) 402 | ensure 403 | # $stderr.puts "dispose cache" 404 | # $stderr.puts table[cachekey].inspect 405 | 406 | table.remove(cachekey) 407 | end 408 | else 409 | precrule(table, prec, evaluator) 410 | end 411 | end 412 | 413 | ### miscellaneous operators 414 | operator("print", "value", Repr::GarbObject) do |value, evaluator| 415 | puts evaluator.show(value) 416 | value 417 | end 418 | 419 | operator("isdatastore", "value", Repr::GarbObject) do |value| 420 | (value.is_a? Repr::Store).to_repr 421 | end 422 | 423 | operator("copy", "object", Repr::GarbObject) do |object| 424 | object.copy 425 | end 426 | 427 | 428 | ### operators cooperate with environment 429 | operator("call", "func", Repr::Callable, "args", Repr::Store) do |func, args, evaluator| 430 | case func 431 | when Repr::Function 432 | evaluator.extend_scope(args, func.env) do 433 | evaluator.evaluate(func.body) 434 | end 435 | when Repr::Procedure 436 | evaluator.extend_scope(args, {}.to_repr) do 437 | func.procedure.call(evaluator, args) 438 | end 439 | else 440 | raise "EVALUATE: callee is not a function: #{func}" unless f.is_a? Repr::Function 441 | end 442 | end 443 | 444 | operator("eval", "env", Repr::Store, "program", Repr::GarbObject) do |env, program, evaluator| 445 | prevEnv = evaluator.dot 446 | evaluator.dot = env 447 | begin 448 | result = evaluator.evaluate(program) 449 | ensure 450 | evaluator.dot = prevEnv 451 | end 452 | 453 | result 454 | end 455 | 456 | operator("getenv") do |evaluator| 457 | evaluator.dot 458 | end 459 | 460 | operator("setenv", "env", Repr::Store) do |env, evaluator| 461 | evaluator.dot = env 462 | end 463 | 464 | ## commands 465 | ### control flow 466 | command("while", "condition", "body") do |condition, body, evaluator| 467 | falseObj = Repr::Bool.new(false) 468 | result = Repr::Bool.new(true) 469 | 470 | while evaluator.evaluate(condition) != falseObj 471 | result = evaluator.evaluate(body) 472 | end 473 | 474 | result 475 | end 476 | 477 | command("if", "condition", "consequence", "alternative") do |condition, consequence, alternative, evaluator| 478 | if evaluator.evaluate(condition) != false.to_repr 479 | evaluator.evaluate(consequence) 480 | else 481 | evaluator.evaluate(alternative) 482 | end 483 | end 484 | 485 | 486 | command("begin", "body") do |body, evaluator| 487 | result = false.to_repr 488 | 489 | body.each_key do |k, v| 490 | result = evaluator.evaluate(v) 491 | end 492 | 493 | result 494 | end 495 | 496 | # スコープを導入するコマンド。 497 | command("scope", "body") do |body, evaluator| 498 | emptyenv = {}.to_repr 499 | evaluator.extend_scope(emptyenv, evaluator.dot) do 500 | result = false.to_repr 501 | 502 | body.each_key do |k, v| 503 | result = evaluator.evaluate(v) 504 | end 505 | 506 | result 507 | end 508 | end 509 | 510 | ### object allocation 511 | command("datastore", "object") do |object, evaluator| 512 | store = Repr::store({}) 513 | 514 | object.each_key { |k| 515 | store[k] = evaluator.evaluate(object[k]) 516 | } 517 | store 518 | end 519 | 520 | def self.quasiquote(evaluator, object, level) 521 | if object.is_a? Repr::Store 522 | exists = object.exist('@').value 523 | 524 | if exists && object['@'] == 'quasiquote'.to_repr 525 | Repr::quasiquote(self.quasiquote(evaluator, object['value'], level + 1)) 526 | elsif exists && object['@'] == 'unquote'.to_repr 527 | if level == 1 528 | evaluator.evaluate(object['value']) 529 | elsif level > 1 530 | {"@" => "unquote", 531 | "value" => self.quasiquote(evaluator, object['value'], level - 1) 532 | }.to_repr 533 | else 534 | raise "unquote outside quasiquote" 535 | end 536 | else 537 | result = {}.to_repr 538 | 539 | object.each_key do |k, v| 540 | result[k] = self.quasiquote(evaluator, v, level) 541 | end 542 | 543 | result 544 | end 545 | else 546 | object # 単純にクオートする 547 | end 548 | end 549 | 550 | command("quasiquote", "value") do |object, evaluator| 551 | # $stderr.puts "quasiquote: #{object.inspect}" 552 | res = quasiquote(evaluator, object, 1) 553 | # $stderr.puts "result: #{res.inspect}" 554 | res 555 | end 556 | 557 | command("quote", "value") do |value, evaluator| 558 | value 559 | end 560 | 561 | command("lambda", "env", "body") do |env, body, evaluator| 562 | Repr::function(evaluator.evaluate(env), body) 563 | end 564 | 565 | def evaluate_sub_expr(key, env) 566 | prog = self.get_raw(key) 567 | 568 | if prog == nil 569 | raise "the value for key was nil: #{key}" 570 | end 571 | 572 | expr = prog.analyzed.call(env) 573 | expr 574 | end 575 | 576 | private 577 | def analyze_non_command 578 | proc {|env| 579 | result = {}.to_repr 580 | 581 | self.each_key {|k, v| 582 | result[k] = self[k].analyzed.call(env) 583 | } 584 | 585 | result 586 | } 587 | end 588 | 589 | def analyze 590 | comname = self.get_raw('@') 591 | 592 | if comname == nil 593 | return analyze_non_command 594 | end 595 | 596 | unless comname.is_a? Repr::String 597 | raise "#{comname.inspect} is not a Repr::String, in #{self.inspect}" 598 | end 599 | 600 | feature = @@commands[comname.value] 601 | 602 | if feature == nil 603 | raise "undefined command #{comname}" 604 | end 605 | 606 | return feature.call(self) 607 | end 608 | 609 | public 610 | end 611 | 612 | # 評価するやつ。変数とか文脈とか何も考えていないので単純 613 | class Evaluator 614 | include Repr 615 | 616 | attr_accessor :dot 617 | attr_accessor :stacktrace 618 | 619 | def initialize(root = Repr::store({})) 620 | @dot = root 621 | @stacktrace = [] 622 | end 623 | 624 | def trace_log(feature, s) 625 | if @dot.exist('/').value && 626 | @dot['/'].exist('verbose').value && 627 | @dot['/']['verbose'] == 'on'.to_repr 628 | puts "-[EVAL: #{feature.inspect}]--------------------" 629 | puts s.inspect 630 | # puts "[DOT] " 631 | # puts @dot.inspect 632 | end 633 | end 634 | 635 | def evaluate(program) 636 | # トレース用に,どこをどういった順番で評価したかを保持しておく. 637 | # 表示するときは,これらをうまく加工する. 638 | @stacktrace.push program 639 | begin 640 | program.analyzed.call(self) 641 | ensure 642 | @stacktrace.pop 643 | end 644 | end 645 | 646 | def extend_scope(newenv, parent) 647 | oldenv = @dot 648 | newenv[".."] = parent 649 | 650 | unless newenv.exist("/".to_repr).value 651 | newenv["/"] = oldenv["/"] 652 | end 653 | 654 | @dot = newenv 655 | result = nil 656 | begin 657 | result = yield 658 | ensure 659 | @dot = oldenv 660 | end 661 | 662 | result 663 | end 664 | 665 | def trace_string(prog) 666 | case prog 667 | when Repr::Store 668 | if prog.exists?('@').value 669 | prog['@'].inspect 670 | else 671 | "datastore" 672 | end 673 | else 674 | prog.inspect 675 | end 676 | end 677 | 678 | def get_stack_trace_array 679 | @stacktrace.map { |prog| 680 | trace_string(prog) 681 | } 682 | end 683 | 684 | def show(p) 685 | return p.inspect 686 | end 687 | 688 | def debug_print 689 | Repr::Store.debug_print 690 | end 691 | end 692 | end 693 | 694 | -------------------------------------------------------------------------------- /grammar2.garb: -------------------------------------------------------------------------------- 1 | #{ 2 | grammar.rules[:sentence].children << Rule::many_one(Rule::whitespace).map { "\n".to_repr } 3 | #} 4 | #{ 5 | grammar.rules[:eof] = Rule::not(Rule::any) 6 | grammar.rules[:comment] = "#".to_rule >> Rule::many(Rule::not("\n") >> Rule::any) >> ("\n".to_rule | :eof) 7 | grammar.rules[:sentence].children << Rule::call(:comment) 8 | #} 9 | # Garbanzo predefined grammar set v.2 10 | # 2016/02/29 akamah 11 | # 12 | 13 | # 最初に,バージョン1に対する構文定義が続く. 14 | # 15 | # 数値を定義 (1428) 16 | #{ 17 | grammar.rules[:number] = Rule::many_one(Rule::one_of("0123456789")).map {|as| 18 | as.map(&:value).join.to_i.to_repr 19 | }.token 20 | 21 | # 識別子を定義 (hoge, poyo) 22 | grammar.rules[:identifier] = Rule::many_one(Rule::one_of("@abcdefghijklmnopqrstuvwxyz.")).map {|as| 23 | as.map(&:value).join.to_repr 24 | }.token 25 | #} 26 | 27 | # 文字列を定義 ("string") 28 | #{ 29 | grammar.rules[:string] = ["\"", Rule::many(!"\"".to_rule >> Rule::any), Rule::token("\"")].sequence {|_, cs, _| 30 | cs.map(&:value).join.to_repr 31 | }.token 32 | #} 33 | 34 | # print文 (print 3) 35 | #{ 36 | grammar.rules[:sentence].children << ["print", Rule::many_one(Rule::whitespace), :expression].sequence { |_, _, n| 37 | Repr::print(n) 38 | } 39 | #} 40 | 41 | # pathとは、a/b/cのように、オブジェクトのキーを指定するもの。 42 | #{ 43 | grammar.rules[:path] = [:identifier, Rule::many(Rule::string("/") >> :identifier)].sequence { |a, idents| 44 | [a] + idents 45 | } 46 | #} 47 | 48 | # $path という記法で、親を辿れるようにする 49 | #{ 50 | grammar.rules[:path] = grammar.rules[:path] | ["$", :path].sequence { |_, idents| 51 | ["..".to_repr] + idents 52 | } 53 | #} 54 | 55 | # 変数はパスで表される 56 | #{ 57 | grammar.rules[:variable] = :path.to_rule.map {|idents| 58 | idents.reduce(Repr::getenv) { |body, key| 59 | Repr::get(body, key) 60 | } 61 | } | ["/", :path].sequence {|_, idents| 62 | idents.reduce(Repr::get(Repr::getenv, '/'.to_repr)) { |body, key| 63 | Repr::get(body, key) 64 | } 65 | } 66 | #} 67 | 68 | 69 | # 原始式は、数値か文字列か変数のいずれかである 70 | #{ 71 | grammar.rules[:primitive] = [:number, :string, :variable].choice 72 | #} 73 | 74 | # おっと忘れてた、ストアオブジェクトも原始式 75 | #{ 76 | grammar.rules[:pair] = [:identifier, Rule::token(":"), :expression].sequence { |id, _, expr| 77 | [id, expr] 78 | } 79 | 80 | grammar.rules[:primitive].children << 81 | [Rule::token("["), Rule::separate_by(:pair, Rule::token(",")), Rule::token("]")].sequence { |_, lst, _| 82 | Repr::store(lst.to_h) 83 | } 84 | #} 85 | 86 | # 関数呼び出しは、原始式にカッコを後置したもの, func(expr) 87 | #{ 88 | grammar.rules[:call] = [:primitive, Rule::token("("), :expression, Rule::token(")")].sequence {|func, _, arg, _| 89 | Repr::call(func, arg) 90 | } | :primitive 91 | #} 92 | 93 | 94 | # termは、関数呼び出しとパスでのアクセスを加えたもの 95 | #{ 96 | grammar.rules[:term] = 97 | [:call, Rule::optional(Rule::string("/") >> :path, [])].sequence { |a, idents| 98 | idents.reduce(a) { |body, key| 99 | Repr::get(body, key) 100 | } 101 | } 102 | #} 103 | 104 | # かけ算は、termをくっつけたもの 105 | #{ 106 | grammar.rules[:multiplication] = 107 | [:term, Rule::many(Rule::string('*').token >> :term)].sequence { |num, mults| 108 | mults.reduce(num) { |expr, adder| 109 | Repr::mult(expr, adder) 110 | } 111 | } 112 | #} 113 | 114 | # 足し算は、multiplication二つをプラスでくっつけたもの。 115 | #{ 116 | grammar.rules[:addition] = 117 | [:multiplication, Rule::many(Rule::string('+').token >> :multiplication)].sequence { |num, adds| 118 | adds.reduce(num) { |expr, adder| 119 | Repr::add(expr, adder) 120 | } 121 | } 122 | #} 123 | 124 | # 関数は、いまのところ^{ }と書く 125 | #{ 126 | grammar.rules[:function] = 127 | [Rule::token("^{"), Rule::many(:sentence), Rule::token("}")].sequence {|_, body, _| 128 | a = Repr::lambda(Repr::getenv, Repr::begin(Lib::make_list(*body))) 129 | a 130 | } 131 | #} 132 | 133 | #{ 134 | grammar.rules[:expression] = [:addition, :function].choice 135 | #} 136 | 137 | #{ 138 | grammar.rules[:condition] = :expression.to_rule 139 | #} 140 | 141 | 142 | 143 | # 代入構文 144 | #{ 145 | grammar.rules[:sentence].children << 146 | [:expression, Rule::token("="), :expression].sequence {|object, _, expr| 147 | Repr::set(object['object'], object['key'], expr) 148 | } 149 | #} 150 | 151 | # 繰り返しのwhileループ 152 | #{ 153 | grammar.rules[:sentence].children << [Rule::token("while"), :condition, :sentence].sequence {|_, cond, body| 154 | Repr::while(cond, body) 155 | } 156 | #} 157 | 158 | # if文 159 | #{ 160 | grammar.rules[:sentence].children << [Rule::token('if'), :expression, 161 | :sentence, 162 | Rule::token('else'), 163 | :sentence].sequence { |_, cond, csq, _, alt| 164 | Repr::if(cond, csq, alt) 165 | } 166 | #} 167 | 168 | # スコープの導入。これは、空の引数を取る関数を呼び出すこととする。 169 | #{ 170 | grammar.rules[:sentence].children << [Rule::token("{"), Rule::many(:sentence), Rule::token("}")].sequence { |_, lst, _| 171 | Repr::scope(Lib::make_list(*lst)) 172 | } 173 | #} 174 | # Repr::call(Repr::lambda(Repr::getenv, body), Repr::Store.new({})) 175 | 176 | 177 | # 式の末尾にセミコロンをつけると文になるやつ 178 | #{ 179 | grammar.rules[:sentence].children << [:expression, Rule::token(";")].sequence {|e, _| e} 180 | #} 181 | 182 | #{ 183 | grammar.rules[:expression].children << ["'".to_rule, :sentence].sequence {|_, s| 184 | Repr::quote(s) 185 | } 186 | #} 187 | 188 | #{ 189 | grammar.rules[:expression].children << ["%".to_rule, :expression].sequence {|_, s| 190 | Repr::eval(Repr::getenv, s) 191 | } 192 | #} 193 | 194 | 195 | 196 | 197 | 198 | # 改行 ################################################ 199 | newline = '/parser/sentence/children/newline = '{ 200 | [@: "terminal", string:" 201 | "]; 202 | } 203 | 204 | setnewline = '/parser/newline = /parser/sentence/children/newline 205 | 206 | print newline 207 | print setnewline 208 | 209 | # 一行コメント 210 | comment = '/parser/comment = '{ 211 | [@: "terminal", string: "#"]; 212 | [@: "many", parser: [@: "quote", value: [@: "noneof", string: " 213 | "]]]; 214 | ""; 215 | } 216 | 217 | setcomment = '/parser/sentence/children/comment = /parser/comment 218 | 219 | print comment 220 | print setcomment 221 | 222 | # シンボル ################################################ 223 | symbol = 'parser/symbol = '{ 224 | oo = [@: "quote", value: [@: "oneof", string: ".@abcdefghijklmnopqrstuvwxyz"]] 225 | %/parser/whitespaces; 226 | result = %oo 227 | cs = [@: "many", parser: oo] 228 | %/parser/whitespaces; 229 | 230 | /foreach([store: cs, func: ^{ 231 | ../result = [@: "append", left: ../result, right: value] 232 | }]); 233 | 234 | result; 235 | } 236 | 237 | print symbol 238 | 239 | 240 | # 整数 ################################################ 241 | integer = '/parser/integer = '{ 242 | digit = [@: "quote", value: [@: "oneof", string: "0123456789"]] 243 | a = [@: "sub", left: [@: "tocode", string: %digit], right: [@: "tocode", string: "0"]] 244 | rest = [@: "many", parser: digit] 245 | ten = [@: "sub", left: [@: "tocode", string: "K"], right: [@: "tocode", string: "A"]] 246 | 247 | "generate"; 248 | /foreach([store: rest, func: ^{ 249 | n = [@: "sub", left: [@: "tocode", string: value], right: [@: "tocode", string: "0"]] 250 | ../a = ../a * ../ten + n 251 | }]); 252 | 253 | a; 254 | } 255 | 256 | setinteger = '/parser/expression/children/integer = /parser/integer 257 | 258 | print integer 259 | print setinteger 260 | 261 | # 真偽値 ################################################ 262 | true = '/parser/true = '{ 263 | [@: "terminal", string: "true"]; 264 | [@: "equal", left: "", right: ""]; 265 | } 266 | 267 | false = '/parser/false = '{ 268 | [@: "terminal", string: "false"]; 269 | [@: "equal", left: "", right: "a"]; 270 | } 271 | 272 | print true 273 | print false 274 | 275 | # ユーティリティ ######################################### 276 | # manytill: あるパーサendが成功するまで,pの結果を順々に追加してゆく. 277 | 278 | manytillbase = '/manytillbase = ^{ 279 | %end; 280 | result; 281 | } 282 | 283 | manytillrec = '/manytillrec = ^{ 284 | x = %p 285 | [@: "set", object: result, key: i, value: x]; 286 | 287 | [@: "choice", children: 288 | [end: /manytillbase([end: end, result: result]), 289 | next: /manytillrec([p: p, end: end, i: i+1, result: result])]]; 290 | } 291 | 292 | manytill = '/manytill = ^{ 293 | result = [@: "datastore", object: []] 294 | [@: "choice", children: 295 | [end: /manytillbase([end: end, result: result]), 296 | next: /manytillrec([p: p, end: end, i: 0, result: result])]]; 297 | } 298 | 299 | print manytillbase 300 | print manytillrec 301 | print manytill 302 | 303 | manytilltest = '/manytilltest = '{ 304 | /manytill([p: /parser/string, end: /parser/integer]); 305 | } 306 | 307 | print manytilltest 308 | 309 | ## 複数の文 310 | somesentences = '/parser/somesentences = ^{ 311 | endp = [@: "datastore", object: [@: "terminal", string: end]] 312 | %/parser/whitespaces; 313 | 314 | sts = /manytill([p: '{ 315 | s = %/parser/sentence 316 | %/parser/whitespaces; 317 | s; 318 | }, end: endp]) 319 | 320 | %/parser/whitespaces; 321 | 322 | sts; 323 | } 324 | 325 | print somesentences 326 | 327 | 328 | # ブロック (begin) ####################################### 329 | block = '/parser/block = '{ 330 | [@: "terminal", string: "begin"]; 331 | sentences = /parser/somesentences([end: "end"]) 332 | result = [@: "datastore", object: [@: "begin", body: sentences]] 333 | 334 | result; 335 | } 336 | 337 | setblock = '/parser/sentence/children/block = /parser/block 338 | 339 | print block 340 | print setblock 341 | 342 | # 関数 ############################################## 343 | function = '/parser/function = '{ 344 | [@: "terminal", string: "fun"]; 345 | %/parser/whitespaces; 346 | [@: "terminal", string: "("]; 347 | %/parser/whitespaces; 348 | 349 | args = /sepby([parser: /parser/symbol, sep: '{ 350 | %/parser/whitespaces; 351 | [@: "terminal", string: ","]; 352 | %/parser/whitespaces; 353 | }]) 354 | [@: "terminal", string: ")"]; 355 | 356 | sts = /parser/somesentences([end: "end"]) 357 | 358 | setargs = [] 359 | 360 | "loop"; 361 | /foreach([store: args, func: ^{ 362 | [@: "set", object: ../setargs, key: key, value: 363 | [@: "datastore", object: 364 | [@: "set", object: '[@: "getenv"];, key: value, value: 365 | [@: "datastore", object: 366 | [@: "get", object: [@: "quote", value: [@: "getenv"]], key: key]]]]]; 367 | }]); 368 | 369 | body = [@: "datastore", object: 370 | [@: "begin", body: 371 | [setargs: [@: "datastore", object: [@: "begin", body: setargs]], 372 | thebody: [@: "datastore", object: [@: "begin", body: sts]]]]] 373 | 374 | x = [@: "datastore", object: [@: "lambda", env: [@: "quote", value: [@: "getenv"]], body: body]] 375 | x; 376 | } 377 | 378 | setfunction = '/parser/expression/children/function = /parser/function 379 | print function 380 | 381 | # パス ################################################ 382 | path = 'parser/path = '{ 383 | p = %/parser/symbol 384 | ps = [@: "many", parser: '{ 385 | [@: "terminal", string: "/"]; 386 | %/parser/symbol; 387 | }] 388 | 389 | result = [] 390 | result/head = p 391 | 392 | "hoge"; 393 | 394 | /foreach([store: ps, func: ^{ 395 | [@: "set", object: ../result, key: key, value: value]; 396 | }]); 397 | 398 | result; 399 | } 400 | 401 | print path 402 | 403 | # ルートから始まるパス ############ 404 | rootpath = 'parser/rootpath = '{ 405 | [@: "terminal", string: "/"]; 406 | 407 | rest = [@: "choice", children: [ 408 | path: %/parser/path, 409 | none: []]] 410 | 411 | result = [] 412 | result/root = "/" 413 | 414 | "loop"; 415 | 416 | /foreach([store: rest, func: ^{ 417 | [@: "set", object: ../result, key: key, value: value]; 418 | }]); 419 | result; 420 | } 421 | 422 | print rootpath 423 | 424 | # 変数 ################################################ 425 | variable = '/parser/variable = '{ 426 | [@: "terminal", string: ""]; 427 | name = [@: "choice", children: [ 428 | path: %/parser/path, 429 | root: %/parser/rootpath]] 430 | result = [@: "datastore", object: [@: "getenv"]] 431 | "hoge"; 432 | 433 | /foreach([store: name, func: ^{ 434 | ../result = [@: "datastore", object: [@: "get", object: ../result, key: value]] 435 | }]); 436 | 437 | result; 438 | } 439 | 440 | print variable 441 | 442 | loop = '/parser/while = '{ 443 | [@: "terminal", string: "while"]; 444 | %/parser/whitespaces; 445 | cond = %/parser/expression 446 | sts = /parser/somesentences([end: "end"]) 447 | body = [@: "datastore", object: [@: "begin", body: sts]] 448 | 449 | result = [@: "datastore", object: [@: "while", condition: cond, body: body]] 450 | 451 | result; 452 | } 453 | 454 | setloop = '[@: "insertprev", object: /parser/sentence/children, 455 | origin: [@: "firstkey", object: /parser/sentence/children], 456 | key: "while", value: /parser/while]; 457 | 458 | print loop 459 | print setloop 460 | 461 | 462 | branch = '/parser/if = '{ 463 | [@: "terminal", string: "if"]; 464 | %/parser/whitespaces; 465 | cond = %/parser/expression 466 | 467 | csq = /parser/somesentences([end: "else"]) 468 | 469 | alt = /parser/somesentences([end: "end"]) 470 | 471 | c = [@: "datastore", object: [@: "begin", body: csq]] 472 | a = [@: "datastore", object: [@: "begin", body: alt]] 473 | 474 | result = [@: "datastore", object: [@: "if", condition: cond, consequence: c, alternative: a]] 475 | result; 476 | } 477 | 478 | setbranch = '[@: "insertprev", object: /parser/sentence/children, 479 | origin: [@: "firstkey", object: /parser/sentence/children], 480 | key: "if", value: /parser/if]; 481 | 482 | print branch 483 | print setbranch 484 | 485 | # sepby: あるパーサに区切られたパーサを複数読み込む. 486 | # parser = "hoge", sep = ","なら, 487 | # "" (空白) 488 | # "hoge" 489 | # "hoge,hoge" 490 | # ... 491 | # にマッチする. 492 | # Haskellのパーサコンビネータにインスパイアされた 493 | 494 | sepbyone = '/sepbyone = ^{ 495 | head = %parser 496 | tail = [@: "many", parser: '{ 497 | %../sep; 498 | %../parser; 499 | }] 500 | 501 | result = [] 502 | [@: "set", object: result, key: 0, value: head]; 503 | 504 | i = 1 505 | "loop"; 506 | /foreach([store: tail, func: ^{ 507 | [@: "set", object: ../result, key: ../i, value: value]; 508 | ../i = [@: "add", left: ../i, right: 1] 509 | }]); 510 | 511 | result; 512 | } 513 | 514 | sepby = '/sepby = ^{ 515 | [@: "choice", children: 516 | [more: /sepbyone([parser: parser, sep: sep]), 517 | zero: []]]; 518 | } 519 | 520 | print sepbyone 521 | print sepby 522 | 523 | 524 | 525 | 526 | 527 | 528 | ### 呼び出し ##################### 529 | call = '/parser/call = '{ 530 | [@: "terminal", string: "!"]; 531 | f = %/parser/expression 532 | 533 | [@: "terminal", string: "("]; 534 | args = /sepby([parser: /parser/expression, sep: '{ 535 | %/parser/whitespaces; 536 | [@: "terminal", string: ","]; 537 | %/parser/whitespaces; 538 | }]) 539 | 540 | [@: "terminal", string: ")"]; 541 | 542 | [@: "datastore", object: [@: "call", func: f, args: args]]; 543 | } 544 | 545 | print call 546 | 547 | 548 | 549 | ### 優先順位付きexpression ############### 550 | exprtab = '/parser/exprtab = [@: "datastore", object: []] 551 | print exprtab 552 | 553 | 554 | numrule = '/parser/exprtab/numrule = 555 | [prec: 0, 556 | parser: /parser/integer] 557 | 558 | print numrule 559 | 560 | callrule = '/parser/exprtab/callrule = 561 | [prec: 30, 562 | parser: '{ 563 | f = [@: "precrule", table: /parser/exprtab, prec: 29] 564 | [@: "terminal", string: "("]; 565 | args = /sepby([parser: /parser/expression, sep: '{ 566 | %/parser/whitespaces; 567 | [@: "terminal", string: ","]; 568 | %/parser/whitespaces; 569 | }]) 570 | 571 | [@: "terminal", string: ")"]; 572 | [@: "datastore", object: [@: "call", func: f, args: args]]; 573 | }] 574 | 575 | print callrule 576 | 577 | truerule = '/parser/exprtab/truerule = 578 | [prec: 20, 579 | parser: /parser/true] 580 | 581 | falserule = '/parser/exprtab/falserule = 582 | [prec: 20, 583 | parser: /parser/false] 584 | 585 | print truerule 586 | print falserule 587 | 588 | varrule = '/parser/exprtab/varrule = 589 | [prec: 10, 590 | parser: /parser/variable] 591 | 592 | print varrule 593 | 594 | funrule = '/parser/exprtab/funrule = 595 | [prec: 20, 596 | parser: /parser/function] 597 | 598 | print funrule 599 | 600 | evalrule = '/parser/exprtab/evalrule = 601 | [prec: 40, 602 | parser: '{ 603 | [@: "terminal", string: "%"]; 604 | expr = [@: "precrule", table: /parser/exprtab, prec: 40] 605 | 606 | [@: "datastore", object: [@: "eval", env: [@: "quote", value: [@: "getenv"]], program: expr]]; 607 | }] 608 | 609 | print evalrule 610 | 611 | 612 | stringrule = '/parser/exprtab/stringrule = 613 | [prec: 0, 614 | parser: /parser/string] 615 | 616 | print stringrule 617 | 618 | datastorerule = '/parser/exprtab/datastorerule = 619 | [prec: 0, 620 | parser: /parser/datastore] 621 | 622 | print datastorerule 623 | 624 | 625 | # 代入 ############################################### 626 | assignrule = '/parser/exprtab/assignrule = 627 | [prec: 300, 628 | parser: '{ 629 | ws = /parser/whitespaces 630 | path = %/parser/variable 631 | 632 | %ws; 633 | [@: "terminal", string: "="]; 634 | 635 | %ws; 636 | rhe = [@: "precrule", table: /parser/exprtab, prec: 300] 637 | 638 | path/@ = "set" 639 | path/value = rhe 640 | 641 | path; 642 | }] 643 | 644 | print assignrule 645 | 646 | 647 | 648 | 649 | # 設定する 650 | newexpr = '/parser/newexpr = '{ 651 | [@: "withcache", table: /parser/exprtab]; 652 | } 653 | print newexpr 654 | 655 | setnewexpr = '[@: "insertprev", object: /parser/expression/children, 656 | origin: [@: "firstkey", object: /parser/expression/children], 657 | key: "newexpr", value: /parser/newexpr]; 658 | print setnewexpr 659 | 660 | # 不要なルールを削除 661 | deleteint = '[@: "remove", object: /parser/expression/children, key: "integer"]; 662 | deleteds = '[@: "remove", object: /parser/expression/children, key: "datastore"]; 663 | deletestr = '[@: "remove", object: /parser/expression/children, key: "string"]; 664 | 665 | print deleteint 666 | print deleteds 667 | print deletestr 668 | 669 | 670 | # ここで,<式> ; という記法を文とみなすこととする. 671 | exprsentence = '/parser/exprsentence = '{ 672 | e = %/parser/expression 673 | [@: "terminal", string: ";"]; 674 | e; 675 | } 676 | 677 | setexprsentence = '/parser/sentence/children/exprsentence = /parser/exprsentence 678 | 679 | print exprsentence 680 | print setexprsentence 681 | 682 | ### QUASIQUOTE ################## 683 | 684 | 685 | # 準クオート ##################################### 686 | quasiquote = '/parser/quasiquote = '{ 687 | [@: "terminal", string: "block"]; 688 | quoted = /parser/somesentences([end: "end"]) 689 | 690 | body = [@: "datastore", object: [@: "scope", body: quoted]] 691 | result = [@: "datastore", object: [@: "quasiquote", value: body]] 692 | 693 | result; 694 | } 695 | 696 | quote = '/parser/quote = '{ 697 | [@: "terminal", string: "quote"]; 698 | quoted = /parser/somesentences([end: "end"]) 699 | body = [@: "datastore", object: [@: "begin", body: quoted]] 700 | result = [@: "datastore", object: [@: "quasiquote", value: body]] 701 | 702 | result; 703 | } 704 | 705 | print quasiquote 706 | print quote 707 | 708 | quasiquoterule = '/parser/exprtab/quasiquote = 709 | [prec: 20, 710 | parser: /parser/quasiquote] 711 | 712 | quoterule = '/parser/exprtab/quote = 713 | [prec: 20, 714 | parser: /parser/quote] 715 | 716 | print quasiquoterule 717 | print quoterule 718 | 719 | unquoterule = '/parser/exprtab/unquote = 720 | [prec: 40, 721 | parser: '{ 722 | [@: "terminal", string: "$"]; 723 | expr = [@: "precrule", table: /parser/exprtab, prec: 40] 724 | 725 | [@: "datastore", object: [@: "unquote", value: expr]]; 726 | }] 727 | 728 | print unquoterule 729 | 730 | -------------------------------------------------------------------------------- /inter2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | cat predef.tmp.garb program.garb > test.tmp.garb 5 | 6 | ruby proto2.rb test.tmp.garb -i 7 | -------------------------------------------------------------------------------- /interpreter.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | =begin 3 | 幾つかのプロトタイプに共通する事項をまとめる. 4 | 5 | * コマンドライン引数の処理 6 | * デバッグ/プロファイリングなどのサポート 7 | 8 | =end 9 | 10 | 11 | require 'optparse' 12 | require 'readline' 13 | require 'stackprof' 14 | 15 | 16 | require './repr.rb' 17 | require './evaluator.rb' 18 | 19 | module Garbanzo 20 | class Interpreter 21 | attr_reader :evaluator, :name 22 | 23 | def initialize(name) 24 | @name = name 25 | @evaluator = Evaluator.new(self.construct_root) 26 | end 27 | 28 | def construct_root 29 | puts "not implemented #construct_root" 30 | Repr::store({}) 31 | end 32 | 33 | def execute(source) 34 | puts "not implemented #execute" 35 | end 36 | 37 | def show_parse_error(e) 38 | $stderr.puts "parse error, #{e.message}" 39 | $stderr.puts e.backtrace.map{|x| "\tfrom #{x}" } 40 | end 41 | 42 | def show_general_error(e) 43 | $stderr.puts "some error, #{e.message}" 44 | $stderr.puts e.backtrace.map{|x| "\tfrom #{x}" } 45 | end 46 | 47 | def start(args) 48 | inter = false 49 | 50 | opt = OptionParser.new 51 | opt.on('-i', '--interactive') {|v| inter = true } 52 | 53 | opt.parse!(args) 54 | 55 | 56 | source = File.open(args[0], "rb").read 57 | 58 | begin 59 | StackProf.run(mode: :cpu, out: "#{self.name}.dump") do 60 | execute(source) 61 | end 62 | rescue Rule::ParseError => e 63 | show_parse_error(e) 64 | raise 65 | rescue => e 66 | show_general_error(e) 67 | raise 68 | end 69 | 70 | 71 | if inter 72 | self.interactive() 73 | else 74 | evaluator.debug_print 75 | end 76 | end 77 | 78 | def with_readline 79 | tty_save = `stty -g`.chomp 80 | 81 | begin 82 | yield 83 | rescue Interrupt 84 | system('stty', tty_save) 85 | exit 86 | end 87 | end 88 | 89 | def interactive 90 | with_readline do 91 | while s = Readline.readline("> ", true) 92 | begin 93 | s.chomp! 94 | 95 | result = self.execute(s) 96 | puts result.inspect 97 | 98 | rescue Rule::ParseError => e 99 | show_parse_error(e) 100 | rescue => e 101 | show_general_error(e) 102 | end 103 | end 104 | end 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /lib.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | require './repr' 4 | 5 | module Garbanzo 6 | # いわゆる、言語の標準ライブラリに値するモジュール。 7 | module Lib 8 | HEAD = Repr::String.new("head") 9 | REST = Repr::String.new("rest") 10 | 11 | def self.null 12 | Repr::store({}) 13 | end 14 | 15 | def self.list_node?(obj) 16 | obj.class == Repr::Store && obj.exist(HEAD).value && obj.exist(REST).value 17 | end 18 | 19 | def self.head(obj) 20 | list_node?(obj) ? obj[HEAD] : Bool.new(false) 21 | end 22 | 23 | def self.rest(obj) 24 | list_node?(obj) ? obj[REST] : Bool.new(false) 25 | end 26 | 27 | def self.each_list(lst) 28 | while list_node?(lst) 29 | yield head(lst) 30 | lst = rest(lst) 31 | end 32 | end 33 | 34 | def self.cons(hd, rt) 35 | Repr::store({HEAD => hd, REST => rt}) 36 | end 37 | 38 | def self.make_list(*objs) 39 | l = null 40 | objs.each_with_index do |obj, i| 41 | l[i.to_s.to_repr] = obj 42 | end 43 | # objs.reduce(l) { |lst, obj| 44 | # lst[HEAD] = obj 45 | # lst[REST] = null 46 | # lst[REST] 47 | # } 48 | l 49 | end 50 | 51 | def self.add 52 | Repr::procedure( 53 | lambda {|e, a| 54 | (a['right'].num + a['left'].num).to_repr 55 | }) 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lisp.garb: -------------------------------------------------------------------------------- 1 | #{ 2 | grammar.rules[:sentence].children << Rule::whitespace 3 | #} 4 | #{ 5 | grammar.rules[:comment] = [";", Rule::many(!"\n".to_rule >> Rule::any), "\n"].sequence 6 | grammar.rules[:sentence].children << (Rule::call(:comment) >> Rule::success(true.to_repr)) 7 | #} 8 | 9 | ; Lisp風の言語を作成する。 10 | 11 | ; 数値を定義 12 | #{ 13 | grammar.rules[:number] = Rule::many_one(Rule::one_of("0123456789")).map {|as| 14 | as.map(&:value).join.to_i.to_repr 15 | } 16 | #} 17 | 18 | ; 識別子を定義する 19 | #{ 20 | grammar.rules[:symbol] = Rule::many_one(Rule::one_of("abcdefghijklmnopqrstuvwxyz.")).map {|as| 21 | as.map(&:value).join.to_repr 22 | } 23 | #} 24 | 25 | ; シンタックスシュガーを幾つか。 26 | #{ 27 | grammar.rules[:quote] = ["'", :expression].sequence {|_, e| 28 | s = Repr::store({}) 29 | s['@'] = 'quote'.to_repr 30 | s['value'] = e 31 | s 32 | } 33 | #} 34 | 35 | ; 変数へのアクセス 36 | #{ 37 | grammar.rules[:getvar] = ['@', :symbol].sequence {|_, e| 38 | s = Repr::store({}) 39 | s['@'] = 'get'.to_repr 40 | s['object'] = Repr::getenv 41 | s['key'] = e.to_repr 42 | s 43 | } 44 | #} 45 | 46 | ; 原始式を定義する 47 | #{ 48 | grammar.rules[:atom] = [:number, :symbol].choice 49 | #} 50 | 51 | #{ 52 | def make_app_parser(name, cmd, *args, &f) 53 | [Rule::token('('.to_rule), 54 | Rule::token(name.to_rule), 55 | Rule::many(Rule::token(Rule::call(:expression))), 56 | Rule::token(')'.to_rule)].sequence {|_, _, exprs, _| 57 | exprs 58 | }.bind {|exprs| 59 | if f != nil 60 | Rule::success(f.call(exprs)) 61 | elsif args.length == exprs.length 62 | h = Repr::store(args.map(&:to_repr).zip(exprs).to_h) 63 | h['@'.to_repr] = cmd.to_repr 64 | Rule::success(h) 65 | else 66 | Rule::fail("argument length mismatch") 67 | end 68 | } 69 | end 70 | 71 | def make_binary_parser(name, cmd) 72 | make_app_parser(name, cmd, 'left', 'right') 73 | end 74 | 75 | beginP = make_app_parser("begin", "begin") { |exprs| 76 | Repr::begin(Lib::make_list(*exprs)) 77 | } 78 | 79 | grammar.rules[:application] = [ 80 | make_binary_parser('+', 'add'), 81 | make_binary_parser(*%w(- sub)), 82 | make_binary_parser(*%w(* mult)), 83 | make_binary_parser(*%w(/ div)), 84 | make_binary_parser(*%w(% mod)), 85 | make_binary_parser(*%w(= equal)), 86 | make_binary_parser(*%w(/= notequal)), 87 | make_binary_parser(*%w(< lessthan)), 88 | make_binary_parser(*%w(and and)), 89 | make_binary_parser(*%w(or or)), 90 | make_app_parser(*%w(not not value)), 91 | make_app_parser(*%w(print print value)), 92 | make_app_parser(*%w(set set object key value)), 93 | make_app_parser(*%w(get get object key)), 94 | make_app_parser(*%w(size size object)), 95 | make_app_parser(*%w(if if condition consequence alternative)), 96 | make_app_parser(*%w(while while condition body)), 97 | make_app_parser(*%w(quote quote value)), 98 | make_app_parser(*%w(. getenv)), 99 | beginP 100 | ].choice 101 | 102 | #{ 103 | grammar.rules[:expression] = [:atom, :application, :getvar, :quote].choice 104 | #} 105 | 106 | #{ 107 | grammar.rules[:sentence] << Rule::call(:expression) 108 | #} 109 | 110 | #{ 111 | grammar.rules[:expression].children << ["[]"].sequence { Repr::store({}) } 112 | #} 113 | 114 | #{ 115 | grammar.rules[:boolean] = "#t".to_rule.map { Repr::bool(true) } | "#f".to_rule.map { Repr::bool(false) } 116 | grammar.rules[:expression].children << Rule::call(:boolean) 117 | #} 118 | 119 | (print eratosthenes) 120 | (set (.) i 0) 121 | (set (.) n 100) 122 | (set (.) q 34) 123 | (set (.) sieve []) 124 | 125 | (while (< @i @n) 126 | (begin 127 | (set @sieve @i #t) 128 | (set (.) i (+ @i 1)))) 129 | 130 | (set @sieve 0 #f) 131 | (set @sieve 1 #f) 132 | 133 | (set (.) i 2) 134 | (while (< @i @q) 135 | (begin 136 | (if (= (get @sieve @i) #t) 137 | (begin 138 | (set (.) a (* @i 2)) 139 | (while (< @a @n) 140 | (begin 141 | (set @sieve @a #f) 142 | (set (.) a (+ @a @i))))) 143 | #f) 144 | (set (.) i (+ 1 @i)))) 145 | 146 | (set (.) i 0) 147 | (while (< @i @n) 148 | (begin 149 | (if (get @sieve @i) 150 | (print @i) 151 | #f) 152 | (set (.) i (+ 1 @i)))) 153 | 154 | ; (set (.) hoge 3) 155 | ; (print ''(get (.) hoge)) 156 | ; (print (size (.))) 157 | ; 158 | ; (set (.) i 0) 159 | ; (set (.) a 10) 160 | ; (while (/= (get (.) i) 10) 161 | ; (begin 162 | ; (set (.) a (+ @a @i)) 163 | ; (set (.) i (+ @i 1)))) 164 | ; (print (get (.) a)) 165 | -------------------------------------------------------------------------------- /mark.garb: -------------------------------------------------------------------------------- 1 | #{ 2 | grammar.rules[:nonemptyline] = [Rule::many_one(!"\n".to_rule >> Rule::any), "\n"].sequence { |cs, _| 3 | r = cs.map(&:value).join.to_repr 4 | r 5 | } 6 | grammar.rules[:emptyline] = "\n".to_rule 7 | grammar.rules[:sentence].children << :emptyline.to_rule 8 | #} 9 | 10 | #{ 11 | grammar.rules[:paragraph] = Rule::many_one(:nonemptyline).map {|ss| 12 | str = Repr::append("

".to_repr, 13 | Repr::append(ss.map(&:value).join(' ').to_repr, 14 | "

".to_repr)) 15 | Repr::print(str) 16 | } 17 | grammar.rules[:sentence].children << :paragraph.to_rule 18 | #} 19 | 20 | #{ 21 | grammar.rules[:heading] = [Rule::token("#"), :nonemptyline].sequence {|_, l| 22 | str = Repr::append("

".to_repr, 23 | Repr::append(l, "

".to_repr)) 24 | Repr::print(str) 25 | } 26 | grammar.rules[:sentence].children.unshift(:heading.to_rule) 27 | #} 28 | 29 | 30 | 31 | # Hey Diddle Diddle 32 | Hey diddle diddle, 33 | 34 | The cat and the fiddle, 35 | The cow jumped over the moon. 36 | The little dog laughed, 37 | To see such sport, 38 | 39 | And the dish ran away with the spoon. 40 | 41 | https://en.wikipedia.org/wiki/Hey_Diddle_Diddle 42 | -------------------------------------------------------------------------------- /parser.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # このパーサオブジェクトは,プロトタイプ1でのみ使用している. 4 | 5 | require './repr.rb' 6 | require './rule.rb' 7 | 8 | module Garbanzo 9 | # 構文解析を行い、意味を持ったオブジェクトを返す。 10 | class Parser 11 | include Rule 12 | include Rule::Private 13 | 14 | attr_accessor :grammar 15 | 16 | def initialize(grammar = Grammar.new) 17 | @grammar = grammar 18 | end 19 | 20 | # 文字列を読み込み、ひとつの単位で実行する。 21 | def parse(source) 22 | self.parse_rule(grammar.start, source) 23 | end 24 | 25 | def parse_sequence(rule, source) 26 | es, rest = rule.children.reduce([[], source]) do |accum, c| 27 | e1, r1 = parse_rule(c, accum[1]) 28 | [accum[0] << e1, r1] 29 | end 30 | 31 | es = rule.func.call(*es) if rule.func != nil 32 | return es, rest 33 | end 34 | 35 | def parse_choice(rule, source) 36 | # errs = [] 37 | for c in rule.children 38 | begin 39 | return parse_rule(c, source) 40 | rescue ParseError => e 41 | # errs << e 42 | end 43 | end 44 | 45 | raise ParseError, "no choice " 46 | # raise errs.max_by {|a| a.line} 47 | end 48 | 49 | def parse_string(rule, source) 50 | if source.start_with?(rule.string) 51 | return Repr::String.new(rule.string), source[rule.string.length .. -1] 52 | else 53 | raise ParseError.new("#{rule.message}, around #{source[0..20]}", (10000 - source.length)) 54 | end 55 | end 56 | 57 | def parse_rule(rule, source) 58 | case rule 59 | when Choice 60 | parse_choice(rule, source) 61 | when String 62 | parse_string(rule, source) 63 | when Sequence 64 | parse_sequence(rule, source) 65 | when Function 66 | rule.function.call(source) 67 | when Call 68 | if r = grammar.rules[rule.rule_name] 69 | parse_rule(r, source) 70 | else 71 | raise ParseError.new("rule: #{rule.rule_name}", (10000 - source.length)) 72 | end 73 | when Bind 74 | x, rest = parse_rule(rule.rule, source) 75 | parse_rule(rule.func.call(x), rest) 76 | when And 77 | x, _ = parse_rule(rule.rule, source) 78 | [x, source] 79 | when Not 80 | begin 81 | parse_rule(rule.rule, source) 82 | rescue ParseError 83 | return [Repr::store({}), source] 84 | end 85 | raise ParseError.new("not #{rule.message}", (10000 - source.length)) 86 | else 87 | raise "PARSE_RULE: error, not a rule #{rule}" 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /program.garb: -------------------------------------------------------------------------------- 1 | {"@": "print", "value": "[done] define initial grammar set"} 2 | 3 | # keyが,単なるsymbolを認識するように変更 4 | tmp = {"@": "datastore", "object": {"@": "choice", "children": {}}}; 5 | tmp/children/string = parser/string; 6 | tmp/children/symbol = parser/symbol; 7 | parser/key = tmp; 8 | 9 | /parser/key/children/integer = /parser/integer; 10 | 11 | ##### 主にデータストア関連の命令のラッパー関数 12 | set = fun(obj, k, v) 13 | {@: "set", object: obj, key: k, value: v} 14 | end; 15 | 16 | get = fun(obj, k) 17 | {@: "get", object: obj, key: k} 18 | end; 19 | 20 | size = fun(obj) 21 | {@: "size", object: obj} 22 | end; 23 | 24 | remove = fun(obj, k) 25 | {@: "remove", object: obj, key: k} 26 | end; 27 | 28 | exist = fun(obj, k) 29 | {@: "exist", object: obj, key: k} 30 | end; 31 | 32 | getprevkey = fun(obj, orig) 33 | {@: "getprevkey", object: obj, origin: orig} 34 | end; 35 | 36 | getnextkey = fun(obj, orig) 37 | {@: "getnextkey", object: obj, origin: orig} 38 | end; 39 | 40 | firstkey = fun(obj) 41 | {@: "firstkey", object: obj} 42 | end; 43 | 44 | lastkey = fun(obj) 45 | {@: "lastkey", object: obj} 46 | end; 47 | 48 | isdatastore = fun(value) 49 | {@: "isdatastore", value: value} 50 | end; 51 | 52 | # おなじみのprint関数 53 | print = fun(message) 54 | {"@": "print", "value": message} 55 | end; 56 | 57 | exprprec = fun(prec) 58 | {@: "precrule", table: ../parser/exprtab, prec: prec} 59 | end; 60 | 61 | tokenize = fun(parser) 62 | block 63 | %/parser/whitespaces; 64 | result = %$parser; 65 | %/parser/whitespaces; 66 | result; 67 | end; 68 | end; 69 | 70 | terminal = fun(string) 71 | block 72 | {@: "terminal", string: $string} 73 | end; 74 | end; 75 | 76 | #a = 3; 77 | #foo = block 78 | # a = 2; 79 | # block 80 | # a = 1; 81 | # /print(a); 82 | # /print($a); 83 | # /print($$a); 84 | # end; 85 | #end; 86 | 87 | #bar = block 88 | # a = 6; 89 | # /print($a); 90 | #end; 91 | 92 | #a = 4; 93 | #%bar; 94 | 95 | binaryoperator = fun(name, op, prec, pleft, pright) 96 | {@: "set", object: /parser/exprtab, key: name, value: 97 | {prec: prec, 98 | parser: block 99 | left = /exprprec($pleft); 100 | %/tokenize(/terminal($op)); 101 | right = /exprprec($pright); 102 | {@: "datastore", object: {@: $name, left: left, right: right}} 103 | end}} 104 | end; 105 | 106 | rightassoc = fun(name, op, prec) 107 | /binaryoperator(name, op, prec, {@: "sub", left: prec, right: 1}, prec); 108 | end; 109 | 110 | nonassoc = fun(name, op, prec) 111 | p = {@: "sub", left: prec, right: 1}; 112 | /binaryoperator(name, op, prec, p, p); 113 | end; 114 | 115 | 116 | # 二項演算子の定義 117 | rightassoc("or", "||", 250); 118 | rightassoc("and", "&&", 200); 119 | 120 | nonassoc("equal", "==", 150); 121 | nonassoc("notequal", "!=", 150); 122 | nonassoc("lessthan", "<", 150); 123 | 124 | rightassoc("add", "+", 100); 125 | rightassoc("sub", "-", 100); 126 | rightassoc("mult", "*", 90); 127 | rightassoc("div", "%", 90); 128 | rightassoc("mod", "|", 90); 129 | 130 | 131 | # print(isdatastore(1245)); 132 | # print(isdatastore("hoge")); 133 | # print(isdatastore({})); 134 | 135 | #print(3 < 4 * 8 && 5 | 2 == 0 || 4 != 3); 136 | #print(true && true); 137 | 138 | ## whileのテスト 139 | #a = 1; 140 | #while a<10 141 | # print(a); 142 | # a = a + 1; 143 | #end 144 | # 145 | ## ifのテスト 146 | #if a<10 147 | # print("a != 10"); 148 | #else 149 | # print("a == 10"); 150 | #end 151 | # 152 | ## 関数のテスト 153 | #hoge = fun() 154 | # {@: "print", value: "abc"} 155 | # {@: "print", value: "def"} 156 | #end; 157 | # 158 | #{@: "print", value: hoge()} 159 | 160 | 161 | # fibonacci number 162 | fib = fun(n) 163 | if n < 2 164 | n; 165 | else 166 | ../fib(n - 1) + ../fib(n - 2); 167 | end 168 | end; 169 | 170 | 171 | # /print(fib(7)); 172 | 173 | 174 | #funca = function(arguments) 175 | #funca(argumentb) 176 | 177 | #@@func(arg)(arg2) 178 | 179 | #hoge.1 180 | #3,0 181 | 182 | 183 | #c = 30 184 | #(list '+ 'b c 'd) 185 | 186 | #=> (+ b 30 d) 187 | 188 | #{@: datastore, object: {@: hoge, b: poyo, c: foo, d: bar} } 189 | 190 | #=> {@: hoge, ... d: bar} 191 | 192 | 193 | ### データストアを操作する関数群 194 | empty = fun() 195 | {@: "datastore", object: {}} 196 | end; 197 | 198 | each = fun(store, f) 199 | if /size(store) == 0 200 | store; 201 | else 202 | key = /firstkey(store); 203 | last = /lastkey(store); 204 | 205 | while key != last 206 | f(key, /get(store, key)); 207 | key = /getnextkey(store, key); 208 | end 209 | 210 | f(last, /get(store, last)); 211 | store; 212 | end 213 | end; 214 | 215 | map = fun(f, store) 216 | r = /empty(); 217 | "mapping"; 218 | /each(store, fun(key, value) 219 | /set(../r, key, ../f(value)); 220 | end); 221 | 222 | r; 223 | end; 224 | 225 | #a = {a: 1, b: 2, c: 3}; 226 | 227 | #/print(/map(fun(v) v + 1; end, a)); 228 | 229 | ### パターンマッチ式の構文拡張用記法 230 | 231 | # シンボルのパターン 232 | sympat = block 233 | s = %/parser/symbol; 234 | %/terminal("@"); 235 | end; 236 | 237 | # define 238 | 239 | parser/sympat = block 240 | s = %/parser/symbol; 241 | %/terminal(":"); 242 | d = %/parser/integer; 243 | %/parser/whitespaces; 244 | 245 | fun(obj) 246 | e = /exprprec(../d); 247 | %/parser/whitespaces; 248 | /set(obj, ../s, e); 249 | end; 250 | end; 251 | 252 | # 文字列パターン 253 | parser/strpat = block 254 | s = %/parser/string; 255 | %/parser/whitespaces; 256 | 257 | fun(obj) 258 | %/tokenize(/terminal(../s)); 259 | end; 260 | end; 261 | 262 | parser/patternlist = block 263 | {@: "many", parser: block 264 | {@: "choice", children: 265 | {str: %/parser/strpat, 266 | sym: %/parser/sympat}} 267 | end} 268 | end; 269 | 270 | 271 | parser/patternmatch = block 272 | %/tokenize(/terminal("let")); 273 | %/tokenize(/terminal(":")); 274 | d = %/tokenize(/parser/integer); 275 | 276 | xs = %/parser/patternlist; 277 | %/tokenize(/terminal(":=")); 278 | 279 | body = %/parser/expression; 280 | %/tokenize(/terminal(";")); 281 | 282 | 283 | psr = block 284 | vars = /empty(); 285 | /each($xs, fun(k, f) 286 | f(../vars); 287 | end); 288 | 289 | quote 290 | /each($vars, fun(k, v) 291 | /set(.., k, v); 292 | end); 293 | 294 | ${@: "quote", value: $body}; 295 | end; 296 | end; 297 | 298 | key = /size(/parser/exprtab); 299 | 300 | entry = {prec: d, parser: psr}; 301 | /set(/parser/exprtab, key, entry); 302 | 303 | true; 304 | end; 305 | 306 | 307 | /parser/sentence/children/patternmatch = /parser/patternmatch; 308 | 309 | paren = block 310 | %/tokenize(/terminal("(")); 311 | inner = %/parser/expression; 312 | %/tokenize(/terminal(")")); 313 | 314 | inner; 315 | end; 316 | 317 | # /parser/expression/children/paren = paren; 318 | 319 | 320 | let:0 "(" inner:1000 ")" := inner; 321 | let:15 store:14 "[" key:1000 "]" := /get(store, key); 322 | 323 | let:20 "'" val:20 := {@: "quote", value: val}; 324 | 325 | # /print(4 + 2); 326 | # /print((5 + 8) * 2); 327 | 328 | #x = {0: 10, 1: 100, 2: 200}; 329 | 330 | #/print(x[1]); 331 | #/print('hoge); 332 | 333 | #/print((12345 % 100) | 10); 334 | 335 | 336 | /print("[done] define additional grammar set"); 337 | 338 | -------------------------------------------------------------------------------- /proto1.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | =begin rdoc 3 | 言語処理系の開発:拡張可能な構文を持つ言語のプロトタイプその1 4 | 1. ちょっとしたASTを評価するものと 5 | 2. パーサコンビネータっぽい構文解析装置と 6 | 3. デフォルトでは構文の拡張しかできない構文を持つ 7 | =end 8 | 9 | require './interpreter.rb' 10 | require './repr.rb' 11 | require './lib.rb' 12 | require './rule.rb' 13 | require './evaluator.rb' 14 | require './parser.rb' 15 | 16 | module Garbanzo 17 | # EvaluatorとParserをカプセルしたもの。 18 | 19 | class Proto1 < Interpreter 20 | def initialize 21 | super("proto1") 22 | 23 | @parser = install_grammar_extension 24 | end 25 | 26 | ## 親クラスのコンストラクタにより呼ばれる. 27 | def construct_root 28 | root = Repr::store({}) 29 | root['add'] = Lib::add 30 | root['/'] = root 31 | root 32 | end 33 | 34 | # 構文拡張のやつです。 35 | def install_grammar_extension 36 | parser = Parser.new 37 | nth ||= 0 38 | parser.grammar.rules[:sentence] = Rule::choice( 39 | ['#{', 40 | Rule::many(!'#}'.to_rule >> Rule::any).map {|cs| 41 | nth += 1 42 | to_eval = cs.map(&:value).join 43 | parser.instance_eval(to_eval, "(grammar_extension: #{nth})") 44 | Repr::Bool.new(false) 45 | }, 46 | '#}'].sequence >> Rule::success(false.to_repr)) 47 | 48 | parser 49 | end 50 | 51 | def evaluate(prog) 52 | evaluator.evaluate(prog) 53 | end 54 | 55 | def parse(src) 56 | @parser.parse(src) 57 | end 58 | 59 | def execute(src) 60 | while src != "" 61 | prog, src = parse(src) 62 | evaluate(prog) 63 | end 64 | end 65 | end 66 | end 67 | 68 | 69 | if __FILE__ == $0 70 | include Garbanzo 71 | Proto1.new.start(ARGV) 72 | end 73 | -------------------------------------------------------------------------------- /proto2.rb: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/ruby 2 | # coding: utf-8 3 | 4 | =begin 5 | 構文拡張可能な言語プロトタイプver.2 6 | Garbanzoの文法をGarbanzoのプログラムで記述できる. 7 | 8 | =end 9 | 10 | require './repr.rb' 11 | require './lib.rb' 12 | require './rule.rb' 13 | require './evaluator.rb' 14 | require './parser.rb' 15 | require './interpreter.rb' 16 | 17 | 18 | module Garbanzo 19 | include Repr 20 | 21 | class Proto2 < Interpreter 22 | attr_accessor :evaluator 23 | attr_accessor :debug 24 | 25 | def initialize(debug = true) 26 | super('proto2') 27 | @debug = debug 28 | end 29 | 30 | def evaluate(prog) 31 | @evaluator.evaluate(prog) 32 | end 33 | 34 | def parse 35 | evaluate(@evaluator.dot['/']['parser']['sentence']) 36 | end 37 | 38 | def execute(src) 39 | # 入力ソースコードを評価器にセットする。 40 | @evaluator.dot['/']['source'] = Repr::Store.create_source(src) 41 | 42 | # 設定したソースコードが残っている限り 43 | while !@evaluator.dot['/']['source'].is_eof? 44 | sentence = parse 45 | puts("> #{sentence.inspect}") if debug 46 | res = evaluate(sentence) 47 | puts("=> #{res.inspect}") if debug 48 | end 49 | 50 | res 51 | end 52 | 53 | def install_string_rule(root) 54 | stringp = Repr::procedure( 55 | lambda { |e, env| 56 | source = env["/"]["source"] 57 | source.parse_string.to_repr 58 | }) 59 | root['parser']['string'] = Repr::call( 60 | Repr::quote(stringp), {}.to_repr) 61 | end 62 | 63 | def install_whitespaces_rule(root) 64 | root['parser']['whitespaces'] = 65 | Repr::many( 66 | Repr::quote( 67 | Repr::oneof(" \t\n".to_repr))) 68 | end 69 | 70 | def install_pair_rule(root) 71 | pairp = Repr::Procedure.new( 72 | lambda { |e, env| 73 | keyp = env["/"]["parser"]["key"] 74 | exprp = env["/"]["parser"]["expression"] 75 | whitep = env["/"]["parser"]["whitespaces"] 76 | 77 | k = e.evaluate(keyp) 78 | e.evaluate(whitep) 79 | e.evaluate(Repr::terminal(":".to_repr)) 80 | e.evaluate(whitep) 81 | v = e.evaluate(exprp) 82 | e.evaluate(whitep) 83 | 84 | Repr::store({ k => v }) 85 | }) 86 | 87 | root['parser']['key'] = root['parser']['string'] 88 | root['parser']['pair'] = Repr::call( 89 | Repr::quote(pairp), {}.to_repr) 90 | end 91 | 92 | def install_datastore_rule(root) 93 | datastorep = Repr::Procedure.new( 94 | lambda { |e, env| 95 | pairp = env['/']['parser']['pair'] 96 | whitep = env["/"]["parser"]["whitespaces"] 97 | endp = Repr::terminal("}".to_repr) 98 | result = {}.to_repr 99 | 100 | e.evaluate(Repr::terminal("{".to_repr)) 101 | e.evaluate(whitep) 102 | 103 | head = e.evaluate( 104 | Repr::choice( 105 | { "pair" => pairp, 106 | "exit" => endp }.to_repr)) 107 | 108 | return result if head == "}".to_repr # {} 109 | 110 | result = head # { a: b 111 | 112 | rest = Repr::begin( 113 | { 114 | "comma" => Repr::terminal(",".to_repr), 115 | "whitespaces" => Repr::eval(Repr::getenv, Repr::quote(whitep)), 116 | "pair" => Repr::eval(Repr::getenv, Repr::quote(pairp)) 117 | }.to_repr) 118 | 119 | body = e.evaluate(Repr::many(Repr::quote(rest))) 120 | 121 | body.each_key do |_, entry| 122 | entry.each_key do |k, v| 123 | result[k] = v 124 | end 125 | end 126 | 127 | e.evaluate(Repr::terminal("}".to_repr)) 128 | 129 | result 130 | }) 131 | root['parser']['datastore'] = Repr::call( 132 | Repr::quote(datastorep), {}.to_repr) 133 | end 134 | 135 | def construct_root 136 | root = Repr::store({}) 137 | root['add'] = Lib::add 138 | root['/'] = root 139 | 140 | parser = Repr::store({}) 141 | parser['/'] = root 142 | 143 | root['parser'] = parser 144 | 145 | parser['sentence'] = Repr::choice(Repr::store({})) 146 | 147 | root['foreach'] = Repr::function( 148 | root, 149 | Repr::if(Repr::equal(Repr::size(Repr::get(Repr::getenv, "store".to_repr)), 150 | 0.to_repr), 151 | true.to_repr, 152 | Repr::begin( 153 | { "getlast" => Repr::set(Repr::getenv, "last".to_repr, 154 | Repr::lastkey(Repr::get(Repr::getenv, "store".to_repr))), 155 | "initkey" => Repr::set(Repr::getenv, "key".to_repr, 156 | Repr::firstkey(Repr::get(Repr::getenv, "store".to_repr))), 157 | "loop" => Repr::while( 158 | Repr::notequal(Repr::get(Repr::getenv, "last".to_repr), 159 | Repr::get(Repr::getenv, "key".to_repr)), 160 | Repr::begin( 161 | { "call" => Repr::call( 162 | Repr::get(Repr::getenv, "func".to_repr), 163 | Repr::datastore( 164 | { "key" => Repr::get(Repr::getenv, "key".to_repr), 165 | "value" => Repr::get(Repr::get(Repr::getenv, "store".to_repr), 166 | Repr::get(Repr::getenv, "key".to_repr)) 167 | }.to_repr)), 168 | "updatekey" => Repr::set(Repr::getenv, "key".to_repr, 169 | Repr::getnextkey( 170 | Repr::get(Repr::getenv, "store".to_repr), 171 | Repr::get(Repr::getenv, "key".to_repr))) 172 | }.to_repr)), 173 | "calllast" => Repr::call( 174 | Repr::get(Repr::getenv, "func".to_repr), 175 | Repr::datastore( 176 | { "key" => Repr::get(Repr::getenv, "key".to_repr), 177 | "value" => Repr::get(Repr::get(Repr::getenv, "store".to_repr), 178 | Repr::get(Repr::getenv, "key".to_repr)) 179 | }.to_repr)), 180 | "return" => true.to_repr 181 | }.to_repr))) 182 | 183 | install_string_rule(root) 184 | install_whitespaces_rule(root) 185 | install_pair_rule(root) 186 | install_datastore_rule(root) 187 | 188 | 189 | ## an expression is either a datastore or a string 190 | parser['expression'] = Repr::choice( 191 | { "datastore" => parser['datastore'], 192 | "string" => parser['string'] 193 | }.to_repr) 194 | 195 | ## sentence ::= datastore 196 | parser['sentence']['children']['datastore'] = parser['datastore'] 197 | root 198 | end 199 | 200 | def get_current_line 201 | linum = self.evaluator.dot['/']['source'].line.num 202 | self.evaluator.dot['/']['source']['source'].value.split("\n")[linum - 1] 203 | end 204 | 205 | def generate_caret_mark 206 | column = self.evaluator.dot['/']['source'].column.num 207 | " " * (column - 1) + "^" 208 | end 209 | 210 | def show_parse_error(e) 211 | super(e) 212 | puts get_current_line 213 | puts generate_caret_mark 214 | end 215 | 216 | def show_general_error(e) 217 | super(e) 218 | puts get_current_line 219 | puts generate_caret_mark 220 | end 221 | end 222 | end 223 | 224 | 225 | if __FILE__ == $0 226 | include Garbanzo 227 | Proto2.new(false).start(ARGV) 228 | end 229 | -------------------------------------------------------------------------------- /repr.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # 内部表現 4 | module Garbanzo 5 | # 内部表現 6 | module Repr 7 | class ::Object 8 | def is_repr? 9 | return false 10 | end 11 | end 12 | 13 | ## Garbanzoのオブジェクトとしてのトップ 14 | ## サブクラスでは,属性宣言とinitializeとeql?とcopyを実装. 15 | class GarbObject 16 | def to_repr; self; end 17 | def is_repr?; true; end 18 | 19 | def eql?(other) 20 | raise "eql? is not defined for #{self.class}" 21 | end 22 | 23 | def hash 24 | raise "hash is not defined for #{self.class}. It cannot be a key." 25 | end 26 | end 27 | 28 | def self.define_command(name, *arguments) 29 | str = <<"EOS" 30 | def self.#{name.downcase}(#{arguments.join(', ')}) 31 | s = Repr::store({}) 32 | s['@'] = '#{name.downcase}'.to_repr 33 | #{arguments.map { |arg| 34 | 's[\''+ arg + '\'] = ' + arg 35 | }.join("\n")} 36 | s 37 | end 38 | EOS 39 | puts str if $DEBUG 40 | self.module_eval(str, name) 41 | end 42 | 43 | def self.define_binary_command(name) 44 | self.define_command(name, "left", "right") 45 | end 46 | 47 | ## 主にデータを表すオブジェクト 48 | # define_repr_class("Num", Object, "num") # 言語の内部表現としての整数 49 | # define_repr_class("String", Object, "value") # 内部表現としての文字列 50 | # define_repr_class("Bool", Object, "value") # 内部表現としての文字列 51 | # define_repr_class("Store", Object, "table") # データストアオブジェクト 52 | # define_repr_class("Callable", Object) # 呼び出し可能なオブジェクト、という意味で 53 | # define_repr_class("Function", Callable, "env", "body") # 関数 54 | # define_repr_class("Procedure", Callable, "proc") # ネイティブの関数 55 | 56 | class Num < GarbObject 57 | attr_accessor :num 58 | 59 | def initialize(num) 60 | @num = num 61 | end 62 | 63 | def copy 64 | self 65 | end 66 | 67 | def inspect 68 | self.num.inspect 69 | end 70 | 71 | def eql?(other) 72 | other.class == Num && self.num.eql?(other.num) 73 | end 74 | 75 | def ==(other) 76 | other.class == Num && self.num == other.num 77 | end 78 | 79 | def hash 80 | num.hash 81 | end 82 | end 83 | 84 | def self.num(num) 85 | Num.new(num) 86 | end 87 | 88 | class String < GarbObject 89 | attr_accessor :value 90 | 91 | # @@creation = 0 92 | # @@created_in = Hash.new { 0 } 93 | 94 | # at_exit { 95 | # $stderr.puts "string is created #{@@creation} times" 96 | 97 | # locations = @@created_in.keys.sort { |a, b| 98 | # @@created_in[a] <=> @@created_in[b] 99 | # } 100 | 101 | # locations.each do |k| 102 | # $stderr.puts "#{@@created_in[k]} => #{k}" 103 | # end 104 | # } 105 | 106 | def initialize(value) 107 | @value = value 108 | 109 | # @@creation += 1 110 | 111 | # if @@creation % 113 == 0 112 | # @@created_in[caller(1..5)] += 1 113 | # end 114 | 115 | # if @@creation % 113 == 0 116 | 117 | end 118 | 119 | def copy; String.new(::String.new(self.value)); end 120 | def ==(other); other.class == String && other.value == self.value; end 121 | def eql?(other); other.class == String && other.value.eql?(self.value); end 122 | def inspect; self.value.inspect; end 123 | def to_s; inspect; end 124 | 125 | def hash; value.hash; end 126 | end 127 | 128 | def self.string(value) 129 | String.new(value) 130 | end 131 | 132 | class Bool < GarbObject 133 | attr_accessor :value 134 | 135 | def initialize(value); @value = value; end 136 | def copy; self; end 137 | def inspect; self.value.inspect; end 138 | 139 | def self.true_object; @@true_object; end 140 | def self.false_object; @@false_object; end 141 | 142 | def hash; self.value.hash; end 143 | 144 | def ==(other) 145 | other.class == Bool && self.value == other.value 146 | end 147 | 148 | @@true_object = self.new(true) 149 | @@false_object = self.new(false) 150 | end 151 | 152 | def self.bool(value) 153 | value ? Bool.true_object : Bool.false_object 154 | end 155 | 156 | class Callable < GarbObject 157 | end 158 | 159 | class Function < Callable 160 | attr_accessor :env, :body 161 | 162 | def initialize(env, body) 163 | @env = env; @body = body 164 | end 165 | 166 | def copy 167 | Function.new(self.env.copy, self.body.copy) 168 | end 169 | 170 | def inspect 171 | "^ (#{self.body.inspect})" 172 | end 173 | 174 | def ==(other) 175 | other.class == Function && other.env == self.env && other.body == self.body 176 | end 177 | end 178 | 179 | def self.function(env, body) 180 | Function.new(env, body) 181 | end 182 | 183 | class Procedure < Callable 184 | attr_accessor :procedure 185 | 186 | def initialize(procedure) 187 | @procedure = procedure 188 | end 189 | 190 | def copy 191 | Procedure.new(self.procedure) 192 | end 193 | 194 | def inspect 195 | "#" 196 | end 197 | 198 | def ==(other) 199 | other.class == Procedure && other.procedure == self.procedure 200 | end 201 | end 202 | 203 | def self.procedure(procedure) 204 | Procedure.new(procedure) 205 | end 206 | 207 | class Store < GarbObject 208 | attr_accessor :keys, :table 209 | 210 | def initialize(obj) 211 | # @@creation_count += 1 212 | # ca = caller[3] 213 | # @@callers[ca] ||= 0 214 | # @@callers[ca] += 1 215 | 216 | case obj 217 | when Hash 218 | @keys = obj.keys.map {|k| as_key k } 219 | @table = obj.map {|k, v| [as_key(k), v] }.to_h 220 | when Array 221 | @keys = obj.map {|k| as_key k[0] } 222 | @table = obj.map {|k| [as_key(k[0]), k[1]] }.to_h 223 | else 224 | raise "cannot construct a datastore from: #{obj}" 225 | end 226 | end 227 | 228 | def copy 229 | Store.new(@keys.map {|k| [from_key(k), @table[k].copy] }) 230 | end 231 | 232 | def as_key(r) 233 | case r 234 | when ::String 235 | r 236 | when Repr::String 237 | r.value 238 | when Repr::Num 239 | r.num 240 | when Repr::Bool 241 | r.value 242 | when ::Symbol 243 | r.to_s 244 | when ::Numeric 245 | r 246 | when ::Bool 247 | r 248 | else 249 | raise "this is not a key #{key.class}" 250 | end 251 | end 252 | 253 | def from_key(inner) 254 | case inner 255 | when ::String 256 | inner.to_repr 257 | when ::Numeric 258 | inner.to_repr 259 | when ::Symbol 260 | inner.to_s.to_repr 261 | when true, false 262 | inner.to_repr 263 | else 264 | raise "this is not a key #{inner.class.ancestors}" 265 | end 266 | end 267 | 268 | def find_index(key) 269 | @keys.index(as_key key) 270 | end 271 | 272 | def lookup(key) 273 | realkey = as_key key 274 | result = @table[realkey] 275 | 276 | raise "no entry found: #{realkey}:#{realkey.class} in #{@keys.inspect}" if result == nil 277 | 278 | result 279 | end 280 | 281 | def [](key) 282 | if key.is_a? Store 283 | if key.size.num == 0 284 | self 285 | else 286 | k = key.first_key 287 | path = key.copy 288 | path.remove k 289 | 290 | actualkey = key[k] 291 | nextds = self[actualkey] 292 | 293 | if path.size.num == 0 294 | nextds 295 | else 296 | nextds[path] 297 | end 298 | end 299 | else 300 | lookup(key) 301 | end 302 | end 303 | 304 | def []=(key, value) 305 | realkey = as_key key 306 | 307 | if @keys.include?(realkey) 308 | @table[realkey] = value 309 | else 310 | @table[realkey] = value 311 | @keys << realkey 312 | end 313 | 314 | refresh_analyzed 315 | end 316 | 317 | def size 318 | @table.size.to_repr 319 | end 320 | 321 | def remove(key) 322 | realkey = as_key key 323 | result = lookup(key) 324 | 325 | @table.delete(realkey) 326 | @keys.delete(realkey) 327 | 328 | refresh_analyzed 329 | 330 | result 331 | end 332 | 333 | def exist(key) 334 | @table.include?(as_key key).to_repr 335 | end 336 | 337 | def exist_path(*args) 338 | ds = self 339 | args.each { |k| 340 | return false unless ds.exist(k) 341 | ds = ds[k] 342 | } 343 | return true 344 | end 345 | 346 | def get_prev_key(origin) 347 | index = find_index(origin) 348 | return from_key @keys[index - 1] 349 | end 350 | 351 | def get_next_key(origin) 352 | index = find_index(origin) 353 | return from_key @keys[index + 1] 354 | end 355 | 356 | def insert_prev(origin, key, value) 357 | index = find_index(origin) 358 | realkey = as_key key 359 | @table[realkey] = value.to_repr 360 | 361 | refresh_analyzed 362 | 363 | @keys.insert(index, realkey) 364 | end 365 | 366 | def insert_next(origin, key, value) 367 | index = find_index(origin) 368 | realkey = as_key key 369 | @table[realkey] = value.to_repr 370 | 371 | refresh_analyzed 372 | 373 | @keys.insert(index + 1, realkey) 374 | end 375 | 376 | def first_key 377 | from_key @keys.first 378 | end 379 | 380 | def last_key 381 | from_key @keys.last 382 | end 383 | 384 | def each_key 385 | @keys.each do |k| 386 | yield(from_key(k), @table[k]) 387 | end 388 | end 389 | 390 | # wants realkey a string 391 | def get_raw(realkey) 392 | @table[realkey] 393 | end 394 | 395 | # recには,もうすでにでてきたデータストアを記録し,再帰的に出力しないようにする. 396 | # indentは,そのデータストアの字下げ. 397 | def inspect_rec(rec = [], indent = 0) 398 | return ' ' * indent + "<>" if rec.include?(self) 399 | contents = [] 400 | 401 | self.each_key do |k, v| 402 | value = 403 | if v.is_a? Store 404 | ":\n#{v.inspect_rec([self] + rec, indent + 2)}" 405 | else 406 | ": #{v.inspect}" 407 | end 408 | 409 | contents << k.inspect + value 410 | end 411 | 412 | return ' ' * indent + '{' + contents.join(",\n #{' ' * indent}") + "\n" + ' ' * indent + '}' 413 | end 414 | 415 | def inspect 416 | inspect_rec() 417 | end 418 | 419 | def ==(other) 420 | other.class == Store && self.table == other.table && self.keys == other.keys 421 | end 422 | end 423 | 424 | def self.store(store) 425 | Store.new(store) 426 | end 427 | 428 | ## 算術演算 429 | define_binary_command("Add") # 言語の内部表現としての足し算 430 | define_binary_command("Sub") # 引き算 431 | define_binary_command("Mult") # 言語の内部表現としての掛け算 432 | define_binary_command("Div") # 割り算 433 | define_binary_command("Mod") # 割ったあまり 434 | 435 | ## 比較 436 | define_binary_command("Equal") # 同じかどうかを判定 437 | define_binary_command("NotEqual") # 違うかどうかを判定 438 | define_binary_command("LessThan") # < 439 | 440 | ## 論理演算 441 | define_binary_command("And") 442 | define_binary_command("Or") 443 | define_command("Not", "value") 444 | 445 | define_command("Print", "value") # print式を意味する内部表現 446 | 447 | ## データストア関連 448 | # * get by key 449 | # * get by index 450 | # * size 451 | # insert to index 452 | # set to key 453 | # set to index 454 | # remove by key 455 | # remove by index 456 | # ? indexがout of rangeになったらどうしよう? 457 | # => parse error みたいに? 458 | # ? set to key, set to indexって、存在してない場合はどうするんだ? 459 | # ?? set to indexの場合、indexが存在しないならエラーで良さそう 460 | # ?? set to keyの場合、存在していなかったら追加でいいか? 461 | # ??? 追加ということにすると、キーが重複したらどうするのか? 462 | ## しかしだ、順序付けられたマップは欲しい、すごく欲しい。 463 | ## この際、配列のどこどこを指定して〜とかできなくてもいいから欲しい。 464 | ## シーケンシャルアクセスくらいしかしないだろうから欲しい。 465 | 466 | ## ユースケース 467 | ### 末尾に、"hoge"を追加する 468 | ### 先頭に、"hoge"を追加する。 469 | ### "poyo" の後に、 "hoge"を追加する。 470 | ### "hoge"を削除 471 | ### "hoge"を置き換える。 472 | ### 純粋に配列として使う場合は、添え字そのものを数値にすると思う。 473 | ### じゃあ、ランダムアクセスまでできなくても良いのでは? 474 | 475 | ## とりあえず作りたい 476 | # そのキーがあるか 477 | # キーより取得 478 | # あるキーの前/次のキーを取得 479 | # あるキーの前/後に追加 480 | # 最初/最後のキーを取得 481 | # あるキーを削除 482 | # サイズを取得 483 | # * イテレータだとかを考えるのは後。 484 | # * アクセスできない場合は容赦なくエラーを飛ばす。 485 | 486 | 487 | define_command("Set", "object", "key", "value") # データストアへの代入を表す 488 | define_command("Get", "object", "key") # データストアからの読み出しを表す 489 | define_command("Size", "object") # データストアのサイズを取得 490 | 491 | define_command("Remove", "object", "key") # キーを削除 492 | define_command("Exist", "object", "key") # キーが存在するか 493 | 494 | define_command("GetPrevKey", "object", "origin") # あるキーの前のキーを取得 495 | define_command("GetNextKey", "object", "origin") # あるキーの次のキーを取得 496 | 497 | define_command("InsertPrev", "object", "origin", "key", "value") # あるキーの前に挿入 498 | define_command("InsertNext", "object", "origin", "key", "value") # 後に 499 | 500 | define_command("FirstKey", "object") # 最初のキーを取得 501 | define_command("LastKey", "object") # 最後のキーを取得 502 | 503 | define_command("DataStore", "object") # データストアオブジェクトを作成。 504 | define_command("IsDataStore", "value") # データストアオブジェクトかどうか判定。 505 | 506 | define_command("QuasiQuote", "value") # いわゆる準クオート 507 | 508 | ## 制御構文 509 | define_command("Quote", "value"); # 評価の抑制 510 | define_command("While", "condition", "body") # ループ命令 511 | define_command("If", "condition", "consequence", "alternative") # 条件分岐 512 | define_command("Lambda", "env", "body") # 関数を作る 513 | define_command("Begin", "body") # 逐次実行命令 514 | define_command("Scope", "body") # スコープを導入しての逐次実行 515 | 516 | ## 環境 517 | define_command("GetEnv") # 現在の環境を取得 518 | define_command("SetEnv", "env") # 拡張 519 | 520 | define_command("Call", "func", "args") # 呼び出し 521 | 522 | define_command("Eval", "env", "program") # 禁断のやつ 523 | 524 | ## 文字列処理 525 | define_command("Append", "left", "right") 526 | define_command("CharAt", "string", "index") 527 | define_command("Length", "string") 528 | 529 | # 文字列の先頭の文字コードを取得する 530 | define_command("ToCode", "string") 531 | 532 | # 数値を文字コードに対応する文字列に変換する 533 | define_command("FromCode", "num") 534 | 535 | 536 | ## パース関連 537 | # token: 現在の環境の、sourceという名前の文字列の先頭から1文字切り出し返却する。 538 | # sourceが空文字列ならエラーを投げる。 539 | define_command("token") 540 | 541 | # raise: いわゆる例外を投げる。 542 | define_command("fail", "message") 543 | 544 | # choice: いくつかの選択肢を順番に試す 545 | define_command("choice", "children") 546 | 547 | # terminal: 終端記号をパース 548 | define_command("terminal", "string") 549 | 550 | # many: 任意個数のマッチ、要は例外が飛ぶまでそのプログラムを繰り替えす。 551 | define_command("many", "parser") 552 | 553 | define_command("parsestring") 554 | 555 | # 与えられた文字列のうち,どれか一文字に一致 556 | define_command("oneof", "string") 557 | 558 | # 与えられた文字列ではない,どれか一文字に一致 559 | define_command("noneof", "string") 560 | 561 | # 与えられた正規表現に沿ってパースするコマンド. 562 | # マッチした箇所全体の文字列を返す. 563 | # 今のところ,正規表現の記法はRubyに依存することとする. 564 | define_command("regex", "regex") 565 | 566 | # ルールの優先度付きテーブルを元に,パースを行う. 567 | # 優先順位は,低いほど硬く,高いほど柔らかい. 568 | # 例: expr + expr は柔らかく,優先的にルールが適用される. 569 | # ( expr ) は硬く,そこまで優先的には働かない. 570 | 571 | # テーブルに指定するデータストアの形式は,次のようなものとする. 572 | # { name: {prec: n, parser: p } * } 573 | define_command("precrule", "table", "prec") 574 | 575 | # あるテーブルでの構文解析を,キャッシュ(メモ)を有効にしつつ実行する 576 | define_command("withcache", "table") 577 | 578 | 579 | class ::Integer 580 | def to_repr; Garbanzo::Repr::num(self); end 581 | end 582 | 583 | class ::String 584 | def to_repr; Garbanzo::Repr::string(self); end 585 | end 586 | 587 | class ::Hash 588 | def to_repr 589 | Garbanzo::Repr::store(self.map {|k, v| [k.to_repr, v.to_repr] }) 590 | end 591 | end 592 | 593 | class ::TrueClass 594 | def to_repr; Garbanzo::Repr::bool(true); end 595 | end 596 | 597 | class ::FalseClass 598 | def to_repr; Garbanzo::Repr::bool(false); end 599 | end 600 | end 601 | end 602 | -------------------------------------------------------------------------------- /rule.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # これらのルールも,プロトタイプバージョン1でのみ用いている. 3 | 4 | module Garbanzo 5 | module Rule 6 | # 構文。つまり、名前をつけたルールの集合を持つもの。 7 | class Grammar 8 | attr_accessor :rules 9 | attr_accessor :start_rule 10 | 11 | def initialize(rules = {}, start = :sentence) 12 | @rules = rules 13 | @start_rule = start 14 | end 15 | 16 | def start 17 | return rules[start_rule] 18 | end 19 | end 20 | 21 | # 構文解析に失敗した時は、例外を投げて伝えることにする。 22 | class ParseError < StandardError 23 | attr_reader :line, :column 24 | 25 | def initialize(message="parse error", line=1, column=1) 26 | super("#{message}, on line #{line}, column #{column}") 27 | @line = line 28 | @column = column 29 | end 30 | end 31 | 32 | # ルール、パーサコンビネータで言う所のParser 33 | class Rule 34 | def to_rule 35 | return self 36 | end 37 | 38 | def >>(other) 39 | Garbanzo::Rule::sequence(self, other.to_rule) { |a, b| b } 40 | end 41 | 42 | def |(other) 43 | Garbanzo::Rule::choice(self, other.to_rule) 44 | end 45 | 46 | def ~ 47 | Garbanzo::Rule::and(self) 48 | end 49 | 50 | def ! 51 | Garbanzo::Rule::not(self) 52 | end 53 | 54 | def map(&f) 55 | Garbanzo::Rule::bind(self) { |result| 56 | Garbanzo::Rule::success(f.call(result)) 57 | } 58 | end 59 | 60 | def bind(&f) 61 | Garbanzo::Rule::bind(self, &f) 62 | end 63 | 64 | def message 65 | "" 66 | end 67 | 68 | def token 69 | Garbanzo::Rule::token(self) 70 | end 71 | end 72 | 73 | module Private 74 | # 連続 75 | class Sequence < Rule 76 | attr_accessor :children 77 | attr_accessor :func 78 | 79 | def initialize(*children, &func) 80 | @children = children 81 | @func = func 82 | end 83 | 84 | def message 85 | children[0].message || "nop" 86 | end 87 | end 88 | 89 | # 選択 90 | class Choice < Rule 91 | attr_accessor :children 92 | 93 | def initialize(*children) 94 | @children = children 95 | end 96 | 97 | def <<(rule) 98 | @children << rule 99 | end 100 | 101 | def message 102 | children.map(&:message).join(", or ") || "impossible" 103 | end 104 | end 105 | 106 | # 終端記号。ある文字列。 107 | class String < Rule 108 | attr_accessor :string 109 | 110 | def initialize(string) 111 | @string = string 112 | end 113 | 114 | def message 115 | string 116 | end 117 | end 118 | 119 | # 他のルールを呼び出す 120 | class Call < Rule 121 | attr_accessor :rule_name 122 | 123 | def initialize(rule_name) 124 | @rule_name = rule_name 125 | end 126 | 127 | def message 128 | rule_name.to_s 129 | end 130 | end 131 | 132 | # ルールを結合する 133 | class Bind < Rule 134 | attr_accessor :rule 135 | attr_accessor :func 136 | 137 | def initialize(rule, &func) 138 | @rule = rule 139 | @func = func 140 | end 141 | 142 | def message 143 | rule.message 144 | end 145 | end 146 | 147 | # 関数で処理する。 148 | class Function < Rule 149 | attr_accessor :function 150 | attr_reader :message 151 | 152 | def initialize(message = "some func", &function) 153 | @function = function 154 | @message = message 155 | end 156 | end 157 | 158 | # 先読みするやつ。 159 | class And < Rule 160 | attr_accessor :rule 161 | 162 | def initialize(rule) 163 | @rule = rule 164 | end 165 | 166 | def message 167 | "LAH " + rule.message 168 | end 169 | end 170 | 171 | # 同じく先読み、 172 | class Not < Rule 173 | attr_accessor :rule 174 | 175 | def initialize(rule) 176 | @rule = rule 177 | end 178 | 179 | def message 180 | "NOT " + rule.message 181 | end 182 | end 183 | end 184 | 185 | # オープンクラス。クラスのみんなには、内緒だよ! 186 | class ::Array 187 | def sequence(&func); Garbanzo::Rule::sequence(*self.map(&:to_rule), &func); end 188 | def choice(&func); Garbanzo::Rule::choice(*self.map(&:to_rule), &func); end 189 | def to_rule; raise "Array#to_rule is ambiguous operation"; end 190 | end 191 | 192 | class ::String 193 | def to_rule; Garbanzo::Rule::string(self); end 194 | end 195 | 196 | class ::Symbol 197 | def to_rule; Garbanzo::Rule::call(self); end 198 | end 199 | 200 | # リファクタリングして、Successなどのクラスを除去したい。 201 | # そのために、一旦既存のクラスをメソッドに置き換えることとした。 202 | def self.success(result) 203 | function("success") {|s| [result, s] } 204 | end 205 | 206 | def self.fail(message = "failure") 207 | function("fail") {|s| throw ParseError, message } 208 | end 209 | 210 | def self.any 211 | function("any") {|s| 212 | if s.size > 0 213 | [s[0].to_repr, s[1..-1]] 214 | else 215 | raise ParseError, "any: empty input string" 216 | end 217 | } 218 | end 219 | 220 | def self.sequence(*children, &func) 221 | Private::Sequence.new(*children, &func) 222 | end 223 | 224 | def self.choice(*children) 225 | Private::Choice.new(*children) 226 | end 227 | 228 | def self.string(str) 229 | Private::String.new(str) 230 | end 231 | 232 | def self.call(rule_name) 233 | Private::Call.new(rule_name) 234 | end 235 | 236 | def self.bind(rule, &func) 237 | Private::Bind.new(rule.to_rule, &func) 238 | end 239 | 240 | def self.function(message = "some func", &func) 241 | Private::Function.new(message, &func) 242 | end 243 | 244 | def self.and(rule) 245 | Private::And.new(rule.to_rule) 246 | end 247 | 248 | def self.not(rule) 249 | Private::Not.new(rule.to_rule) 250 | end 251 | 252 | def self.optional(rule, default = nil) 253 | rule.to_rule | success(default) 254 | end 255 | 256 | def self.many(rule) 257 | many_rec = lambda {|accum| 258 | optional(bind(rule) { |result| 259 | many_rec.call(accum + [result]) 260 | }, accum) 261 | } 262 | many_rec.call([]) 263 | end 264 | 265 | def self.many_one(rule) 266 | [rule, many(rule)].sequence {|r, rs| 267 | [r] + rs 268 | } 269 | end 270 | 271 | # 与えられた文字列中のある一文字にマッチする 272 | def self.one_of(chars) 273 | chars.split('').choice 274 | end 275 | 276 | def self.whitespace 277 | one_of(" \n\r\t") 278 | end 279 | 280 | def self.whitespaces 281 | many(whitespace).map { " ".to_repr } 282 | end 283 | 284 | def self.token(rule) 285 | [rule, whitespaces].sequence {|result, _| result} 286 | end 287 | 288 | def self.separate_by(rule, separator) 289 | optional([rule, many(separator >> rule)].sequence { |a, rest| 290 | [a] + rest 291 | }, []) 292 | end 293 | 294 | def self.split_by_spaces(rule, *rules) 295 | rules.reduce([rule]) { | accum, r| 296 | accum + [whitespaces, r] 297 | }.sequence.map {|results| 298 | a = [] 299 | results.each_with_index {|item, idx| 300 | a << item if idx % 2 == 0 301 | } 302 | a 303 | } 304 | end 305 | end 306 | end 307 | -------------------------------------------------------------------------------- /run2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | cat predef.tmp.garb program.garb > test.tmp.garb 5 | 6 | ruby proto2.rb test.tmp.garb 7 | -------------------------------------------------------------------------------- /stream.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # ストリームを定義する。 3 | # ストリームは,単なるデータストアオブジェクトに幾つかのキーをもたせたものとして扱う. 4 | 5 | require './repr' 6 | require './rule' 7 | require './evaluator.rb' 8 | 9 | module Garbanzo 10 | module Repr 11 | class Store 12 | def self.linum_array(str) 13 | arr = Array.new(str.size + 1) 14 | 15 | l, c = 1, 1 16 | 0.upto(str.size) do |i| 17 | arr[i] = [l.to_repr, c.to_repr] 18 | 19 | if str[i] == "\n" 20 | l += 1 21 | c = 1 22 | else 23 | c += 1 24 | end 25 | end 26 | 27 | arr 28 | end 29 | 30 | def install_source(str) 31 | self['source'] = str.to_repr 32 | self['index'] = 0.to_repr 33 | self['token_called'] = 0.to_repr 34 | self['line_numbers'] = Store.linum_array(str) 35 | self 36 | end 37 | 38 | def self.create_source(str) 39 | store = Repr::store({}) 40 | store.install_source(str) 41 | store 42 | end 43 | 44 | def is_source 45 | end 46 | 47 | def index 48 | self['index'] 49 | end 50 | 51 | def line 52 | linums = self['line_numbers'] 53 | linums[index.num][0] 54 | end 55 | 56 | def column 57 | linums = self['line_numbers'] 58 | linums[index.num][1] 59 | end 60 | 61 | def source_vars 62 | if exist('source') and exist('index') 63 | return self['source'].value, self.index.num, 64 | self.line.num, self.column.num 65 | else 66 | raise "this is not a source" 67 | end 68 | end 69 | 70 | def debug_log(kind) 71 | return 72 | s, i, l, c = source_vars 73 | 74 | return unless i % 100 == 0 75 | 76 | lines = self['source'].value.split("\n") 77 | 78 | if lines[l] 79 | printf("[%10s] %4d, %4d:%4d: %s\n", kind, i, l, c, lines[l]) 80 | printf("[%10s] %4d, %4d:%4d:%s\n", kind, i, l, c, " " * c + "^") 81 | sleep 0.001 82 | end 83 | end 84 | 85 | def parse_token 86 | s, i, l, c = source_vars 87 | 88 | self['token_called'] = (self['token_called'].num + 1).to_repr 89 | 90 | if i >= s.length 91 | self.fail("there is no character left".to_repr, "token") 92 | else 93 | tok = s[i] 94 | i += 1 95 | 96 | self['index'] = i.to_repr 97 | 98 | debug_log("ADVANCE") 99 | 100 | tok.to_repr 101 | end 102 | end 103 | 104 | def fail(msg, trace) 105 | ex = Rule::ParseError.new(msg.value, self.line.num, self.column.num) 106 | ex.set_backtrace(trace) 107 | 108 | raise ex 109 | end 110 | 111 | def parse_terminal(str) 112 | str.value.length.times do |k| 113 | t = parse_token 114 | 115 | if t.value != str.value[k] 116 | fail(str.to_repr, "\"#{str}\"") 117 | end 118 | end 119 | 120 | str.to_repr 121 | end 122 | 123 | def parse_string 124 | t = parse_token 125 | s = "" 126 | 127 | self.fail("beginning of string".to_repr, "string") if t.value != '"' 128 | 129 | while true 130 | t = parse_token 131 | # $stderr.puts "escape" 132 | 133 | return s.to_repr if t.value == '"' 134 | 135 | if t.value == "\\" 136 | t2 = parse_token 137 | 138 | case t2.value 139 | when 'n' 140 | s += "\n" 141 | else 142 | self.fail("unknown escape sequence: #{t2.value}".to_repr, "string") 143 | end 144 | 145 | else 146 | s += t.value 147 | end 148 | end 149 | end 150 | 151 | def regex_match(re) 152 | offset = self['index'].num 153 | str = self['source'].value 154 | 155 | if md = re.match(str[offset..-1]) 156 | newoffset = offset + md.end(0) # マッチした部分の全体を表すオフセット,らしい. 157 | self['index'] = newoffset.to_repr 158 | 159 | # p md.to_a 160 | md[0].to_repr 161 | else 162 | self.fail(('regexp %s doesnot match' % re.to_s).to_repr, "regex") 163 | end 164 | end 165 | 166 | 167 | def copy_state 168 | return self.index 169 | end 170 | 171 | def set_state(state) 172 | debug_log("BACKTRACK") 173 | self['index'] = state 174 | end 175 | 176 | def is_eof? 177 | return self['source'].value.length == self['index'].num 178 | end 179 | 180 | def satisfy?(message = "doesn't satisfy", trace) 181 | t = parse_token 182 | 183 | if yield t.value 184 | return t 185 | else 186 | self.fail(message, trace) 187 | end 188 | end 189 | 190 | def one_of(string) 191 | self.satisfy?("expected one of #{string}".to_repr, "[#{string}]") {|c| 192 | string.value.index(c) != nil 193 | } 194 | end 195 | 196 | def none_of(string) 197 | self.satisfy?("expected none of #{string}".to_repr, "[^#{string}]") {|c| 198 | string.value.index(c) == nil 199 | } 200 | end 201 | end 202 | end 203 | end 204 | -------------------------------------------------------------------------------- /test_evaluator.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require 'test/unit' 3 | require './evaluator.rb' 4 | 5 | class TC_Evaluator < Test::Unit::TestCase 6 | include Garbanzo 7 | 8 | def test_analyze 9 | num = Repr::num(3) 10 | str = Repr::string("hoge") 11 | 12 | add = Repr::add(num, Repr::num(5)) 13 | 14 | assert_equal(num, num.analyzed.call()) 15 | assert_equal(str, str.analyzed.call()) 16 | assert_equal(8.to_repr, add.analyzed.call(nil)) 17 | 18 | assert_raise { 19 | num.analyze 20 | } 21 | end 22 | 23 | def test_change_comname 24 | one = 1.to_repr 25 | two = 2.to_repr 26 | add = Repr::add(one, two) 27 | 28 | ev = Evaluator.new 29 | 30 | assert_equal(3.to_repr, ev.evaluate(add)) # まあいいでしょう. 31 | add['@'] = "sub".to_repr 32 | 33 | assert_equal(-1.to_repr, ev.evaluate(add)) # analyzeのキャッシュが正しく破棄されていないと,この部分がおかしくなるはず 34 | 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test_garbanzo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/ruby 2 | 3 | require 'test/unit' 4 | require './test_parser.rb' 5 | require './test_repr.rb' 6 | require './test_lib.rb' 7 | require './test_proto1.rb' 8 | require './test_proto2.rb' 9 | require './test_stream.rb' 10 | require './test_evaluator.rb' 11 | 12 | require './test_grammar2.rb' 13 | -------------------------------------------------------------------------------- /test_grammar2.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | require 'test/unit' 4 | require './repr.rb' 5 | require './evaluator.rb' 6 | require './proto2.rb' 7 | 8 | class TC_Grammar2 < Test::Unit::TestCase 9 | include Garbanzo 10 | 11 | def setup 12 | # 初期文法を読み込む. 13 | @int = Proto2.new(false) 14 | @int.start(["predef.tmp.garb"]) 15 | end 16 | 17 | def get_rule(rule_name) 18 | path = rule_name.split('.') 19 | dot = @int.evaluator.dot 20 | 21 | path.reduce(dot) { |obj, key| obj[key] } 22 | end 23 | 24 | def install_source(input) 25 | dst = @int.evaluator 26 | src = Repr::Store.create_source(input) 27 | 28 | %w(source index token_called line_numbers).each do |attr| 29 | dst.dot['source'][attr] = src[attr] 30 | end 31 | end 32 | 33 | ## rule_name は,'parser.sentence'のようにドット区切り 34 | ## inputはある文字列. 35 | ## resultは何かしら. 36 | def rule(rule_name, input, expected) 37 | rule = get_rule(rule_name) 38 | install_source(input) 39 | 40 | result = @int.evaluator.evaluate(rule) 41 | 42 | msg = build_message('Parse Error:', "? doesn't matches ?", rule_name, input) 43 | assert_equal(expected, result, msg) 44 | end 45 | 46 | def test_integer 47 | rule 'parser.integer', '0', 0.to_repr 48 | rule 'parser.expression', '1', 1.to_repr 49 | rule 'parser.integer', '142857', 142857.to_repr 50 | end 51 | 52 | def test_bool 53 | rule 'parser.expression', 'true', true.to_repr 54 | rule 'parser.expression', 'false', false.to_repr 55 | end 56 | 57 | def test_string 58 | rule 'parser.string', '"hogehoge"', "hogehoge".to_repr 59 | rule 'parser.string', '""', "".to_repr 60 | rule 'parser.string', '"string with \newline"', "string with \newline".to_repr 61 | end 62 | 63 | def test_symbol 64 | rule 'parser.symbol', 'homuhomu', 'homuhomu'.to_repr 65 | end 66 | 67 | def test_variable 68 | rule 'parser.expression', 'a/b/c', Repr::get(Repr::get(Repr::get(Repr::getenv, 'a'.to_repr), 69 | 'b'.to_repr), 70 | 'c'.to_repr) 71 | 72 | end 73 | 74 | def test_block 75 | result = Repr::begin( 76 | { 0 => Repr::print("hoge".to_repr), 77 | 1 => Repr::print("poyo".to_repr) 78 | }.to_repr) 79 | 80 | rule 'parser.block', < Repr::get(Repr::getenv, "foo".to_repr), 117 | 1 => Repr::get(Repr::getenv, "bar".to_repr) }.to_repr) 118 | 119 | rule 'parser.expression', "func(foo, bar)", r1 120 | 121 | r2 = Repr::call( 122 | Repr::get(Repr::getenv, "func".to_repr), {}.to_repr) 123 | 124 | rule 'parser.expression', "func()", r2 125 | end 126 | 127 | def test_function 128 | result = "" 129 | rule 'parser.expression', < 3, 1 => 4 }.to_repr) 155 | rule 'parser.expression', '2+3<4+5', Repr::lessthan(Repr::add(2.to_repr, 3.to_repr), 156 | Repr::add(4.to_repr, 5.to_repr)) 157 | end 158 | 159 | def test_rootpath 160 | rule 'parser.rootpath', '/', {"root" => "/"}.to_repr 161 | rule 'parser.rootpath', '/a', {"root" => "/", "head" => "a"}.to_repr 162 | rule 'parser.rootpath', '/a/b', {"root" => "/", "head" => "a", 0 => "b"}.to_repr 163 | 164 | end 165 | 166 | def test_qqbody 167 | rule 'parser.quasiquote', < "quasiquote", "value" => Repr::scope({}.to_repr)}.to_repr 168 | block 169 | end 170 | END 171 | end 172 | 173 | def test_manytill 174 | rule 'manytilltest', '"hoge""hige"3', { 0 => "hoge", 1 => "hige" }.to_repr 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /test_lib.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require './repr.rb' 3 | require './lib.rb' 4 | 5 | 6 | class TC_Lib < Test::Unit::TestCase 7 | include Garbanzo 8 | include Garbanzo::Repr 9 | 10 | =begin 11 | def test_list_miscs 12 | hoge = Lib::make_list(String.new("mado"), 13 | String.new("homu"), 14 | String.new("saya")) 15 | expected = Store.new({ String.new("head") => String.new("mado"), 16 | String.new("rest") => Store.new({ String.new("head") => String.new("homu"), 17 | String.new("rest") => Store.new({ String.new("head") => String.new("saya"), 18 | String.new("rest") => Store.new({})})})}) 19 | assert_equal(expected, hoge) 20 | assert_equal(String.new("mado"), Lib::head(hoge)) 21 | 22 | elms = [] 23 | Lib::each_list(hoge) { |x| 24 | elms << x 25 | } 26 | 27 | assert_equal([String.new("mado"), 28 | String.new("homu"), 29 | String.new("saya")], 30 | elms) 31 | end 32 | =end 33 | end 34 | -------------------------------------------------------------------------------- /test_parser.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require './rule.rb' 3 | require './parser.rb' 4 | 5 | class TC_Parser < Test::Unit::TestCase 6 | include Garbanzo 7 | 8 | def build_parser(rule) 9 | Parser.new(Rule::Grammar.new({ start: rule }, :start)) 10 | end 11 | 12 | def test_sequence 13 | s1 = build_parser(Rule::sequence(Rule::string("mado"), 14 | Rule::string("homu"), 15 | Rule::string("saya"))) 16 | assert_equal("kyou", s1.parse("madohomusayakyou")[1]) 17 | assert_raise(Rule::ParseError) { 18 | s1.parse("madohomumami") 19 | } 20 | 21 | s2 = build_parser(Rule::sequence(Rule::string("mado"), Rule::string("homu")) { |*args| 22 | args.length 23 | }) 24 | 25 | assert_equal([2, ""], s2.parse("madohomu")) 26 | end 27 | 28 | def test_choice 29 | s2 = build_parser(Rule::choice(Rule::string("mado"), 30 | Rule::string("homu"), 31 | Rule::string("saya"))) 32 | assert_equal("ka", s2.parse("madoka")[1]) 33 | assert_equal("ra", s2.parse("homura")[1]) 34 | assert_equal("ka", s2.parse("sayaka")[1]) 35 | assert_raise(Rule::ParseError) { 36 | s2.parse("mami") 37 | } 38 | end 39 | 40 | def test_open_class 41 | s1 = build_parser(["mado", "homu", "saya"].sequence { |m, h, s| 42 | "OK" 43 | }) 44 | s2 = build_parser(["mado", "mami"].choice) 45 | 46 | 47 | assert_equal(["OK", ""], s1.parse("madohomusaya")) 48 | assert_equal("saya", s2.parse("mamisaya")[1]) 49 | end 50 | 51 | def test_optional 52 | s1 = build_parser(Rule::optional("homu".to_rule, nil)) 53 | assert_equal([nil, "mado"], s1.parse("mado")) 54 | end 55 | 56 | def test_one_of 57 | s1 = build_parser(Rule::many_one(Rule::one_of("1234567890"))) 58 | assert_equal([%w(1 4 2 8 5 7).map{|x| x.to_repr}, "hoge"], s1.parse("142857hoge")) 59 | assert_raise(Rule::ParseError) { 60 | s1.parse("hoge") 61 | } 62 | end 63 | 64 | def test_split_by_spaces 65 | s1 = build_parser(Rule::split_by_spaces("mado".to_rule, "homu".to_rule)) 66 | assert_equal([%w(mado homu).map(&:to_repr), "saya"], s1.parse("mado homusaya")) 67 | end 68 | 69 | def test_and 70 | s1 = build_parser(Rule::and("homu".to_rule) >> "homu".to_rule) 71 | assert_equal(["homu".to_repr, "saya"], s1.parse("homusaya")) 72 | end 73 | 74 | def test_any 75 | s = build_parser(Rule::any >> Rule::any) 76 | assert_equal(["a".to_repr, "do"], s.parse("mado")) 77 | end 78 | 79 | def test_not 80 | r = "/*".to_rule >> Rule::many(Rule::not("*/".to_rule) >> Rule::any) >> "*/".to_rule 81 | s1 = build_parser(r) 82 | 83 | assert_equal(["*/".to_repr, "hogetara"], s1.parse("/* this is comment line */hogetara")) 84 | end 85 | end 86 | 87 | -------------------------------------------------------------------------------- /test_proto1.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require './repr.rb' 3 | require './lib.rb' 4 | require './rule.rb' 5 | require './evaluator.rb' 6 | require './parser.rb' 7 | require './proto1.rb' 8 | 9 | class TC_Interpreter < Test::Unit::TestCase 10 | include Garbanzo 11 | 12 | def test_native_proc 13 | inter = Proto1.new 14 | 15 | prog = Repr::call(Repr::get(Repr::getenv, "add".to_repr), 16 | Repr::store({ 'right'.to_repr => 3.to_repr, 17 | 'left'.to_repr => 5.to_repr })) 18 | 19 | assert_equal(8.to_repr, inter.evaluate(prog)) 20 | assert_not_equal(7.to_repr, inter.evaluate(prog)) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test_proto2.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require './repr.rb' 3 | require './lib.rb' 4 | require './rule.rb' 5 | require './evaluator.rb' 6 | require './parser.rb' 7 | require './proto2.rb' 8 | 9 | class TC_Proto2 < Test::Unit::TestCase 10 | include Garbanzo 11 | 12 | # def test_oneof 13 | # int = Proto2.new(false) 14 | 15 | # int.evaluator.dot['/']['parser']['sentence'] = 16 | # int.evaluator.evaluate( 17 | # Repr::call(int.evaluator.dot['/']['oneof'], 18 | # { "string" => "ABCDE" }.to_repr)) 19 | 20 | # assert_equal("B".to_repr, int.execute("B")) 21 | # end 22 | 23 | def test_string 24 | int = Proto2.new(false) 25 | 26 | int.evaluator.dot['/']['parser']['sentence']['children']['white'] = 27 | int.evaluator.dot['/']['parser']['whitespaces'] 28 | assert_equal(3.to_repr, int.execute(" ").size) 29 | end 30 | 31 | def test_pair 32 | int = Proto2.new(false) 33 | 34 | int.evaluator.dot['/']['parser']['sentence'] = 35 | int.evaluator.dot['/']['parser']['pair'] 36 | 37 | assert_equal({ 'hoge' => 'hige' }.to_repr, 38 | int.execute('"hoge":"hige"')) 39 | end 40 | 41 | def test_datastore 42 | int = Proto2.new(false) 43 | 44 | assert_equal({}.to_repr, 45 | int.execute('{ }')) 46 | 47 | assert_equal({ 'hoge' => 'hige' }.to_repr, 48 | int.execute('{"hoge" : "hige"}')) 49 | 50 | assert_equal({ 'hoge' => 'hige', 51 | 'homu' => 'mado' }.to_repr, 52 | int.execute('{"hoge" : "hige", "homu" : "mado" }')) 53 | 54 | assert_equal({ 'hoge' => {'homu' => 'mado'}.to_repr }.to_repr, 55 | int.execute('{"hoge":{"homu": "mado" }}')) 56 | 57 | assert_raise(Rule::ParseError) { 58 | int.execute('{"hoge":"hige",,}') 59 | } 60 | end 61 | end 62 | 63 | -------------------------------------------------------------------------------- /test_repr.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require './repr.rb' 3 | require './evaluator.rb' 4 | require './rule.rb' 5 | 6 | 7 | class TC_Repr < Test::Unit::TestCase 8 | include Garbanzo 9 | include Garbanzo::Repr 10 | 11 | def test_wrapper 12 | assert_equal(Num.new(1), 1.to_repr) 13 | assert_equal(String.new("hoge"), "hoge".to_repr) 14 | assert_equal(Store.new({ "num".to_repr => Num.new(1), 15 | "str".to_repr => String.new("hoge"), 16 | "true".to_repr => Bool.new(true) }), 17 | Store.new({ "num".to_repr => 1.to_repr, 18 | "str".to_repr => "hoge".to_repr, 19 | "true".to_repr => true.to_repr })) 20 | end 21 | 22 | def test_equal 23 | ev = Evaluator.new 24 | 25 | assert_equal(true, Num.new(3) == Num.new(3)) 26 | assert_equal(Num.new(3), Num.new(3)) 27 | assert_equal(Num.new(4), ev.evaluate(Repr::add(Num.new(1), 28 | Num.new(3)))) 29 | assert_not_equal(Bool.new(true), Bool.new(false)) 30 | 31 | assert_equal(Bool.new(true), 32 | ev.evaluate(Repr::equal(String.new("homu"), 33 | String.new("homu")))) 34 | 35 | ds = Store.new({}) 36 | key = String.new("saya") 37 | val = Num.new("38") 38 | 39 | ev.evaluate(Repr::set(Repr::quote(ds), key, val)) 40 | assert_equal(val, ev.evaluate(Repr::get(ds, key))) 41 | 42 | assert_equal(true.to_repr, ev.evaluate(Repr::lessthan(3.to_repr, 10.to_repr))) 43 | end 44 | 45 | 46 | def test_while_repr 47 | ev = Evaluator.new 48 | 49 | sum = String.new("sum") 50 | a = String.new("a") 51 | env = Store.new({ sum => Num.new(0), a => Num.new(0) }) 52 | 53 | # cond: (10 == a) == false 54 | cond = Repr::equal(Repr::equal(Num.new(10), Repr::get(env, a)), Bool.new(false)) 55 | body = Repr::equal(Repr::set(Repr::quote(env), sum, 56 | Repr::add(Repr::get(Repr::quote(env), sum), 57 | Repr::get(Repr::quote(env), a))), 58 | Repr::set(Repr::quote(env), a, 59 | Repr::add(Num.new(1), Repr::get(Repr::quote(env), a)))) 60 | expr = Repr::while(cond, body) 61 | ev.evaluate(expr) 62 | 63 | assert_equal(Num.new(45), ev.evaluate(Repr::get(Repr::quote(env), sum))) 64 | end 65 | 66 | def test_begin 67 | ev = Evaluator.new 68 | 69 | command = Repr::begin(Lib::make_list(Repr::set(Repr::getenv, String.new("a"), Num.new(3)), 70 | Repr::set(Repr::getenv, String.new("a"), Repr::add(Num.new(2), 71 | Repr::get(Repr::getenv, String.new("a")))))) 72 | 73 | ev.evaluate(command) 74 | assert_equal(Num.new(5), ev.evaluate(Repr::get(Repr::getenv, String.new("a"))), ev.show(command)) 75 | end 76 | 77 | def test_function 78 | ev = Evaluator.new({'/' => {}}.to_repr) 79 | 80 | func = Repr::function(Store.new({}), Repr::add(Repr::get(Repr::getenv, String.new("a")), Num.new(10))) 81 | c = Repr::call(func, Store.new({String.new('a') => Num.new(32)})) 82 | 83 | assert_equal(Num.new(42), ev.evaluate(c)) 84 | end 85 | 86 | def test_store 87 | store = Repr::store({}) 88 | store["hoge"] = "hige".to_repr 89 | 90 | assert_equal("hige".to_repr, store["hoge".to_repr]) 91 | end 92 | 93 | def test_quote 94 | ev = Evaluator.new 95 | store = Repr::quote(Repr.add(3.to_repr, 4.to_repr)) 96 | assert_equal(Repr.add(3.to_repr, 4.to_repr), 97 | ev.evaluate(store)) 98 | end 99 | 100 | def test_string_manip 101 | ev = Evaluator.new 102 | 103 | ap = Repr::append("mado".to_repr, "homu".to_repr) 104 | at = Repr::charat("homu".to_repr, 2.to_repr) 105 | le = Repr::length("homuhomu".to_repr) 106 | 107 | assert_equal("madohomu".to_repr, ev.evaluate(ap)) 108 | assert_equal("m".to_repr, ev.evaluate(at)) 109 | assert_equal(8.to_repr, ev.evaluate(le)) 110 | end 111 | 112 | def test_proc 113 | ev = Evaluator.new({'/' => {}}.to_repr) 114 | 115 | pr = Repr::procedure(lambda {|e, a| 116 | (a['right'].num + a['left'].num).to_repr 117 | }) 118 | 119 | args = Repr::store({}) 120 | args['right'] = 1.to_repr 121 | args['left'] = 3.to_repr 122 | 123 | assert_equal(4.to_repr, ev.evaluate(Repr::call(pr, args))) 124 | 125 | assert_equal(pr, ev.evaluate(pr)) 126 | end 127 | 128 | def test_type_check 129 | ev = Evaluator.new 130 | 131 | pr = Repr::add(3.to_repr, "hoge".to_repr) 132 | 133 | assert_raise { 134 | ev.evaluate(pr) 135 | } 136 | 137 | end 138 | 139 | def test_remove 140 | ev = Evaluator.new 141 | 142 | pr = Repr::remove(Repr::quote(Repr::store({3.to_repr => 12.to_repr})), 3.to_repr) 143 | 144 | assert_equal(12.to_repr, ev.evaluate(pr)) 145 | end 146 | 147 | def test_datastore 148 | st = Repr::store({}) 149 | st['hoge'] = 3.to_repr 150 | 151 | assert_equal(3.to_repr, st['hoge']) 152 | assert_equal(1.to_repr, st.size) 153 | 154 | st['hoge'] = 5.to_repr 155 | 156 | assert_equal(5.to_repr, st['hoge']) 157 | 158 | assert_equal(true.to_repr, st.exist('hoge')) 159 | 160 | st['piyo'] = 8.to_repr 161 | st['fuga'] = 9.to_repr 162 | 163 | assert_equal('piyo'.to_repr, st.get_prev_key('fuga')) 164 | assert_equal('piyo'.to_repr, st.get_next_key('hoge')) 165 | 166 | assert_equal('hoge'.to_repr, st.first_key) 167 | assert_equal('fuga'.to_repr, st.last_key) 168 | end 169 | 170 | def test_eval 171 | ev = Evaluator.new 172 | 173 | env = Repr::store({ "hoge".to_repr => 3.to_repr }) 174 | prog = Repr::store({ "@".to_repr => "eval".to_repr, 175 | "env".to_repr => Repr::quote(env), 176 | "program".to_repr => 177 | Repr::quote( 178 | Repr::add(33.to_repr, 179 | Repr::get(Repr::getenv, 180 | "hoge".to_repr))) }) 181 | assert_equal(36.to_repr, ev.evaluate(prog)) 182 | end 183 | 184 | def test_copy 185 | st = Repr::store({ "apple".to_repr => 55.to_repr, 186 | "banana".to_repr => true.to_repr, 187 | "chocolate".to_repr => "kinoko".to_repr }) 188 | st2 = st.copy 189 | 190 | st.remove("apple".to_repr) 191 | st["banana"] = false.to_repr 192 | st["chocolate"].value[3] = "a" 193 | 194 | assert_equal(Repr::store({ "banana".to_repr => false.to_repr, 195 | "chocolate".to_repr => "kinako".to_repr }), 196 | st) 197 | assert_equal(Repr::store({ "apple".to_repr => 55.to_repr, 198 | "banana".to_repr => true.to_repr, 199 | "chocolate".to_repr => "kinoko".to_repr }), 200 | st2) 201 | end 202 | 203 | def construct_test_root(string) 204 | source = Repr::store( 205 | { 'source'.to_repr => 206 | Repr::Store.create_source(string) }) 207 | source['/'] = source 208 | 209 | return source 210 | end 211 | 212 | def test_token 213 | source = construct_test_root("homuhomu") 214 | 215 | prog = Repr::token 216 | 217 | ev = Evaluator.new(source) 218 | 219 | assert_equal("h".to_repr, ev.evaluate(prog)) 220 | assert_equal("o".to_repr, ev.evaluate(prog)) 221 | assert_equal("m".to_repr, ev.evaluate(prog)) 222 | assert_equal("u".to_repr, ev.evaluate(prog)) 223 | assert_equal("h".to_repr, ev.evaluate(prog)) 224 | assert_equal("o".to_repr, ev.evaluate(prog)) 225 | assert_equal("m".to_repr, ev.evaluate(prog)) 226 | assert_equal("u".to_repr, ev.evaluate(prog)) 227 | 228 | assert_raise(Rule::ParseError) { 229 | ev.evaluate(prog) 230 | } 231 | end 232 | 233 | def test_fail 234 | ev = Evaluator.new(construct_test_root("dummy")) 235 | 236 | prog = Repr::fail("error".to_repr) 237 | 238 | assert_raise(Rule::ParseError) { 239 | ev.evaluate(prog) 240 | } 241 | end 242 | 243 | def test_choice 244 | prog = Repr::choice( 245 | { 'hoge' => Repr::fail('hoge'.to_repr), 246 | 'hige' => Repr::fail('hige'.to_repr), 247 | 'hage' => Repr::token 248 | }.to_repr) 249 | 250 | st = construct_test_root("homu") 251 | ev = Evaluator.new(st) 252 | assert_equal('h'.to_repr, ev.evaluate(prog)) 253 | end 254 | 255 | def test_terminal 256 | st = construct_test_root("madohomu") 257 | ev = Evaluator.new(st) 258 | 259 | prog = Repr::begin( 260 | Repr::store({ 261 | "mado".to_repr => Repr::terminal('mado'.to_repr), 262 | "homu".to_repr => Repr::terminal('homu'.to_repr) 263 | })) 264 | assert_equal('homu'.to_repr, ev.evaluate(prog)) 265 | end 266 | 267 | def test_datastore_cmd 268 | ev = Evaluator.new 269 | 270 | prog = Repr::datastore( 271 | Repr::store({ "hoge".to_repr => Repr::add(3.to_repr, 4.to_repr) })) 272 | 273 | assert_equal(Repr::store({ "hoge".to_repr => 7.to_repr }), ev.evaluate(prog)) 274 | end 275 | 276 | def test_many 277 | prog = Repr::many(Repr::quote(Repr::terminal("homu".to_repr))) 278 | st = construct_test_root("homuhomuhomu") 279 | ev = Evaluator.new(st) 280 | result = Repr::store({ 0.to_repr => "homu".to_repr, 281 | 1.to_repr => "homu".to_repr, 282 | 2.to_repr => "homu".to_repr }) 283 | 284 | assert_equal(result, ev.evaluate(prog)) 285 | 286 | st = construct_test_root("hoge") 287 | 288 | ev = Evaluator.new(st) 289 | assert_equal(Repr::store({}), ev.evaluate(prog)) 290 | end 291 | 292 | def test_code 293 | ev = Evaluator.new 294 | 295 | prog1 = Repr::tocode("a".to_repr) 296 | prog2 = Repr::fromcode(65.to_repr) 297 | 298 | assert_equal(97.to_repr, ev.evaluate(prog1)) 299 | assert_equal("A".to_repr, ev.evaluate(prog2)) 300 | end 301 | 302 | def test_scope 303 | st = { "a" => "hoge", "/" => {} }.to_repr 304 | ev = Evaluator.new(st) 305 | 306 | prog1 = Repr::scope(Repr::set(Repr::getenv, "a".to_repr, 42.to_repr)) 307 | prog2 = Repr::get(Repr::getenv, "a".to_repr) 308 | ev.evaluate(prog1) 309 | 310 | assert_equal("hoge".to_repr, ev.evaluate(prog2)) 311 | end 312 | 313 | def test_datastore_subscription 314 | st = { "a" => { "b" => "correct" } }.to_repr 315 | ix = { "fst" => "a", "snd" => "b" }.to_repr 316 | 317 | assert_equal("correct".to_repr, st[ix]) 318 | end 319 | 320 | def test_precrule 321 | ev = Evaluator.new(construct_test_root("h*h+h*h*h+h")) 322 | tab = { "hoge" => { "prec" => 0, "parser" => Repr::terminal("h".to_repr) } 323 | }.to_repr 324 | 325 | tab['plus'] = { "prec" => 100, 326 | "parser" => 327 | Repr::begin( 328 | { "h1" => Repr::precrule(Repr::quote(tab), Repr::num(99)), 329 | "pl" => Repr::terminal("+".to_repr), 330 | "h2" => Repr::precrule(Repr::quote(tab), Repr::num(100)) 331 | }.to_repr) }.to_repr 332 | 333 | tab['mult'] = { "prec" => 80, 334 | "parser" => 335 | Repr::begin( 336 | { "h1" => Repr::precrule(Repr::quote(tab), Repr::num(79)), 337 | "pl" => Repr::terminal("*".to_repr), 338 | "h2" => Repr::precrule(Repr::quote(tab), Repr::num(80)) 339 | }.to_repr) }.to_repr 340 | 341 | assert_nothing_raised { 342 | ev.evaluate(Repr::withcache(Repr::quote(tab))) 343 | } 344 | 345 | p ev.dot['source'] 346 | end 347 | end 348 | -------------------------------------------------------------------------------- /test_stream.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require 'test/unit' 3 | require './stream.rb' 4 | require './rule.rb' 5 | 6 | 7 | class TC_Stream < Test::Unit::TestCase 8 | include Garbanzo 9 | include Garbanzo::Repr 10 | 11 | 12 | def test_source_token 13 | s1 = Store.create_source("h\nge") 14 | 15 | t = s1.parse_token 16 | 17 | assert_equal("h".to_repr, t) 18 | assert_equal(1.to_repr, s1.line) 19 | assert_equal(2.to_repr, s1.column) 20 | assert_equal(1.to_repr, s1.index) 21 | 22 | s1.parse_token # \n 23 | 24 | assert_equal(2.to_repr, s1.line) 25 | assert_equal(1.to_repr, s1.column) 26 | 27 | s2 = Store.create_source("h") 28 | t = s2.parse_token 29 | 30 | assert_equal("h".to_repr, t) 31 | end 32 | 33 | def test_source_terminal 34 | s1 = Store.create_source("homuhomu") 35 | 36 | t = s1.parse_terminal("homu".to_repr) 37 | 38 | assert_equal("homu".to_repr, t) 39 | assert_equal(1.to_repr, s1.line) 40 | assert_equal(5.to_repr, s1.column) 41 | assert_equal(4.to_repr, s1.index) 42 | 43 | assert_raise(Rule::ParseError) do 44 | s1.parse_terminal("mado".to_repr) 45 | end 46 | 47 | s2 = Store.create_source("homuhomu") 48 | t = s2.parse_terminal("homuhomu".to_repr) 49 | assert_equal("homuhomu".to_repr, t) 50 | end 51 | 52 | def test_parse_string 53 | s1 = Store.create_source('"homuhomu"madomado') 54 | 55 | t = s1.parse_string 56 | 57 | assert_equal("homuhomu".to_repr, t) 58 | assert_equal(10.to_repr, s1.index) 59 | assert_equal(11.to_repr, s1.column) 60 | end 61 | 62 | def test_parse_state 63 | s1 = Store.create_source("homuhomu") 64 | st = s1.copy_state 65 | 66 | begin 67 | s1.parse_terminal("sayasaya".to_repr) 68 | rescue 69 | s1.set_state(st) 70 | 71 | t = s1.parse_terminal("homuhomu".to_repr) 72 | 73 | assert_equal("homuhomu".to_repr, t) 74 | assert_equal(8.to_repr, s1.index) 75 | end 76 | end 77 | 78 | def test_one_of 79 | s1 = Store.create_source("homuhomu") 80 | 81 | t1 = s1.one_of("hoge".to_repr) 82 | assert_equal("h".to_repr, t1) 83 | 84 | t2 = s1.one_of("aoeui".to_repr) 85 | assert_equal("o".to_repr, t2) 86 | 87 | assert_raise(Rule::ParseError) { 88 | s1.one_of("abcdefg".to_repr) 89 | } 90 | 91 | end 92 | 93 | def test_regex 94 | s1 = Store.create_source("aaabbcccc") 95 | 96 | t1 = s1.regex_match(/^a*/) 97 | assert_equal("aaa".to_repr, t1) 98 | 99 | t2 = s1.regex_match(/^b*/) 100 | assert_equal("bb".to_repr, t2) 101 | 102 | assert_raise(Rule::ParseError) { 103 | s1.regex_match(/^d+/) 104 | } 105 | end 106 | end 107 | --------------------------------------------------------------------------------