Permalink
Browse files

Rudimentary RegExp support.

  • Loading branch information...
1 parent cd8544c commit 105812f8c7707a19129e22431d7475bc477cb7f5 @matthewd committed Dec 21, 2010
Showing with 119 additions and 7 deletions.
  1. +66 −0 lib/capuchin/kernel.rb
  2. +53 −7 lib/capuchin/visitor.rb
View
@@ -234,6 +234,17 @@ def js_cmp; to_f; end
js_def :indexOf do |needle|
index(needle) || -1
end
+ js_def :match do |needle|
+ Capuchin::RegExp === needle ? needle.exec(self) : index(needle) ? needle : nil
+ end
+ js_def :replace do |needle, replacement|
+ case needle
+ when Capuchin::RegExp
+ needle.replace(self, replacement)
+ else
+ sub(needle, replacement)
+ end
+ end
def js_key; intern; end
def js_truthy?; size > 0; end
def js_typeof; 'string'; end
@@ -371,6 +382,59 @@ def to_proc
@block
end
end
+class Capuchin::RegExp
+ def initialize(pattern, flag_str=nil)
+ @pattern = pattern
+
+ flags = (flag_str ||= '').split('')
+
+ @i = flags.include?('i')
+ @g = flags.include?('g')
+ @m = flags.include?('m')
+
+ @regexp = build_regexp
+
+ @last = 0
+ end
+ def build_regexp
+ # There are probably translations that need to be done here
+ pattern = @pattern
+
+ flags = 0
+ flags |= Regexp::IGNORECASE if @i
+ flags |= Regexp::MULTILINE if @m
+
+ Regexp.new(pattern, flags)
+ end
+ js_attr :source do @pattern end
+ js_attr :ignoreCase do @i end
+ js_attr :global do @g end
+ js_attr :multiline do @m end
+ js_attr :lastIndex do @last end
+ js_def :test do |str|
+ @regexp.match(str) ? true : false
+ end
+ def replace(haystack, replacement)
+ if @g
+ haystack.gsub(@regexp, replacement)
+ else
+ haystack.sub(@regexp, replacement)
+ end
+ end
+ def exec(str)
+ if m = @regexp.match(str)
+ @last = m.offset(0).last
+ m.to_a
+ else
+ @last = 0
+ nil
+ end
+ end
+ js_expose_method :exec
+ def js_call(str)
+ exec(str)
+ end
+end
module Capuchin::DateMethods
attr :t
@@ -384,6 +448,8 @@ def -(other)
Capuchin::Globals[:Array] = Array
Capuchin::Globals[:String] = String
Capuchin::Globals[:Object] = Capuchin::Obj
+Capuchin::Globals[:Function] = Capuchin::Function
+Capuchin::Globals[:RegExp] = Capuchin::RegExp
Capuchin::Globals[:Date] = Capuchin::Function.new('Date', {}, Capuchin::DateMethods) {|| @t = Time.new }
Capuchin::Globals[:print] = Capuchin::Function.new {|x| puts x }
Capuchin::Globals[:p] = Capuchin::Function.new {|x| p [x, x.methods.grep(/^js:/)] }
View
@@ -285,17 +285,63 @@ def visit_BracketAccessorNode(o)
o.get_bytecode(self, @g)
end
def visit_RegexpNode(o)
- # o.value
- raise NotImplementedError, "regexp"
+ unless o.value =~ %r{^/(.*)/([a-z]*)$}
+ raise ArgumentError, "Unexpected RegexpNode format"
+ end
+
+ regexp_string = $1
+ flags = $2
+
+
+ slot = @g.add_literal(nil)
+ done = @g.new_label
+
+ @g.push_literal_at(slot)
+ @g.dup
+ @g.git done
+ @g.pop
+
+ @g.push_const :Capuchin
+ @g.find_const :Globals
+ @g.push_literal :RegExp
+ @g.send :[], 1
+ @g.push_literal regexp_string
+ if flags != ''
+ @g.push_literal flags
+ @g.send :js_new, 2
+ else
+ @g.send :js_new, 1
+ end
+ @g.set_literal slot
+
+ done.set!
+ @g.send :dup, 0
end
def visit_StringNode(o)
pos(o)
str = o.value[1, o.value.size - 2]
- # FIXME: Escapes: \\, \", \n, \u..., \0..
- str.gsub!(/\\([n'"\\])/) do
- case $1
- when 'n'; "\n"
- else; $1
+ str.gsub!(/\\(?:([bfnrtv'"\\])|([0-3][0-7]{0,2}|[4-7][0-7]?)|x([A-Fa-f0-9]{2})|u([A-Fa-f0-9]{4}))/) do
+ if $1
+ case $1
+ when 'b'; "\b"
+ when 'f'; "\f"
+ when 'n'; "\n"
+ when 'r'; "\r"
+ when 't'; "\t"
+ when 'v'; "\v"
+ else; $1
+ end
+ elsif $2
+ $2.to_i(8).chr
+ elsif $3
+ $3.to_i(16).chr
+ else
+ codepoint = $4.to_i(16)
+ if codepoint > 255
+ raise NotImplementedError, "utf-16"
+ else
+ codepoint.chr
+ end
end
end
@g.push_literal str

0 comments on commit 105812f

Please sign in to comment.