diff --git a/Rakefile b/Rakefile index 016ffce8..04e653a8 100644 --- a/Rakefile +++ b/Rakefile @@ -267,6 +267,8 @@ namespace "jruby" do ensure_ragel_version("RedclothScanService.java") do puts "compiling with ragel version #{@ragel_v}" sh %{ragel -J -o ext/redcloth_scan/RedclothScanService.java ext/redcloth_scan/redcloth_scan.java.rl} + sh %{ragel -J -o ext/redcloth_scan/RedclothAttributes.java ext/redcloth_scan/redcloth_attributes.java.rl} + sh %{ragel -J -o ext/redcloth_scan/RedclothInline.java ext/redcloth_scan/redcloth_inline.java.rl} end end @@ -282,14 +284,14 @@ namespace "jruby" do classpath ? "-cp #{classpath}" : "" end - def compile_java(filename, jarname) - sh %{javac -source 1.5 -target 1.5 #{java_classpath_arg} #{filename}} + def compile_java(filenames, jarname) + sh %{javac -source 1.5 -target 1.5 #{java_classpath_arg} #{filenames.join(" ")}} sh %{jar cf #{jarname} *.class} end task :redcloth_scan_java => [:ragel_java] do Dir.chdir "ext/redcloth_scan" do - compile_java("RedclothScanService.java", "redcloth_scan.jar") + compile_java(["RedclothAttributes.java", "RedclothInline.java", "RedclothScanService.java"], "redcloth_scan.jar") end cp "ext/redcloth_scan/redcloth_scan.jar", "lib" end diff --git a/ext/redcloth_scan/redcloth_attributes.java.rl b/ext/redcloth_scan/redcloth_attributes.java.rl new file mode 100644 index 00000000..7db3e379 --- /dev/null +++ b/ext/redcloth_scan/redcloth_attributes.java.rl @@ -0,0 +1,111 @@ +/* + * redcloth_attributes.rl + * + * Copyright (C) 2008 Jason Garber + */ +import java.io.IOException; + +import org.jruby.Ruby; +import org.jruby.RubyArray; +import org.jruby.RubyClass; +import org.jruby.RubyHash; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.RubySymbol; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.CallbackFactory; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.exceptions.RaiseException; +import org.jruby.runtime.load.BasicLibraryService; + +import org.jruby.util.ByteList; + +public class RedclothAttributes extends RedclothScanService.Base { + +%%{ + + machine redcloth_attributes; + include redcloth_common "redcloth_common.java.rl"; + + C2_CLAS = ( "(" ( [^)#]+ >A %{ STORE("class_buf"); } )? ("#" [^)]+ >A %{STORE("id_buf");} )? ")" ) ; + C2_LNGE = ( "[" [^\]]+ >A %{ STORE("lang_buf"); } "]" ) ; + C2_STYL = ( "{" [^}]+ >A %{ STORE("style_buf"); } "}" ) ; + C2 = ( C2_CLAS | C2_STYL | C2_LNGE )+ ; + + mtext_with_attributes = ( C2 mtext >A %T ) >X ; + + inline := |* + + mtext_with_attributes { SET_ATTRIBUTES(); } ; + + *|; + + link_text_with_attributes = C2 "."* " "* ( mtext+ ) >A %{ STORE("name"); } ; + link_text_without_attributes = ( mtext+ ) >B %{ STORE_B("name_without_attributes"); } ; + + link_says := |* + + link_text_with_attributes { SET_ATTRIBUTES(); } ; + link_text_without_attributes { SET_ATTRIBUTE("name_without_attributes", "name"); } ; + + *|; + +}%% + +%% write data nofinal; + + public void SET_ATTRIBUTES() { + SET_ATTRIBUTE("class_buf", "class"); + SET_ATTRIBUTE("id_buf", "id"); + SET_ATTRIBUTE("lang_buf", "lang"); + SET_ATTRIBUTE("style_buf", "style"); + } + + public void SET_ATTRIBUTE(String B, String A) { + buf = ((RubyHash)regs).aref(runtime.newSymbol(B)); + if(!buf.isNil()) { + ((RubyHash)regs).aset(runtime.newSymbol(A), buf); + } + } + + private int machine; + private IRubyObject buf; + + public RedclothAttributes(int machine, IRubyObject self, byte[] data, int p, int pe) { +// System.err.println("RedclothAttributes(data.len: " + data.length + ", p: " + p + ", pe: " + pe + ")"); + this.runtime = self.getRuntime(); + this.self = self; + this.data = data; + this.p = p; + this.pe = p+pe; + this.eof = p+pe; + this.regs = RubyHash.newHash(runtime); + this.buf = runtime.getNil(); + this.machine = machine; + } + + public IRubyObject parse() { + %% write init; + + cs = machine; + + %% write exec; + + return regs; + } + + public static IRubyObject attributes(IRubyObject self, IRubyObject str) { + ByteList bl = str.convertToString().getByteList(); + int cs = redcloth_attributes_en_inline; + return new RedclothAttributes(cs, self, bl.bytes, bl.begin, bl.realSize).parse(); + } + + public static IRubyObject link_attributes(IRubyObject self, IRubyObject str) { + ByteList bl = str.convertToString().getByteList(); + int cs = redcloth_attributes_en_link_says; + return new RedclothAttributes(cs, self, bl.bytes, bl.begin, bl.realSize).parse(); + } +} diff --git a/ext/redcloth_scan/redcloth_common.java.rl b/ext/redcloth_scan/redcloth_common.java.rl index e2952322..a1c21116 100644 --- a/ext/redcloth_scan/redcloth_common.java.rl +++ b/ext/redcloth_scan/redcloth_common.java.rl @@ -112,10 +112,10 @@ # conditionals action starts_line { - p == orig_p || *(p-1) == '\r' || *(p-1) == '\n' || *(p-1) == '\f' + p == orig_p || data[(p-1)] == '\r' || data[(p-1)] == '\n' || data[(p-1)] == '\f' } action starts_phrase { - p == orig_p || *(p-1) == '\r' || *(p-1) == '\n' || *(p-1) == '\f' || *(p-1) == ' ' + p == orig_p || data[(p-1)] == '\r' || data[(p-1)] == '\n' || data[(p-1)] == '\f' || data[(p-1)] == ' ' } }%%; diff --git a/ext/redcloth_scan/redcloth_inline.java.rl b/ext/redcloth_scan/redcloth_inline.java.rl new file mode 100644 index 00000000..fd51a7c3 --- /dev/null +++ b/ext/redcloth_scan/redcloth_inline.java.rl @@ -0,0 +1,247 @@ +/* + * redcloth_inline.rl + * + * Copyright (C) 2008 Jason Garber + */ +import java.io.IOException; + +import org.jruby.Ruby; +import org.jruby.RubyArray; +import org.jruby.RubyClass; +import org.jruby.RubyHash; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.RubyObject; +import org.jruby.RubyString; +import org.jruby.RubySymbol; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.CallbackFactory; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.exceptions.RaiseException; +import org.jruby.runtime.load.BasicLibraryService; + +import org.jruby.util.ByteList; + +public class RedclothInline extends RedclothScanService.Base { + +%%{ + + machine redcloth_inline; + include redcloth_common "redcloth_common.java.rl"; + + # links + mtext_noquotes = mtext -- '"' ; + quoted_mtext = '"' mtext_noquotes '"' ; + mtext_including_quotes = (mtext_noquotes ' "' mtext_noquotes '" ' mtext_noquotes?)+ ; + link_says = ( C_noactions "."* " "* ((quoted_mtext | mtext_including_quotes | mtext_noquotes) -- '":') ) >A %{ STORE("link_text"); } ; + link_says_noquotes_noactions = ( C_noquotes_noactions "."* " "* ((mtext_noquotes) -- '":') ) ; + link = ( '"' link_says '":' %A uri %{ STORE_URL("href"); } ) >X ; + link_noquotes_noactions = ( '"' link_says_noquotes_noactions '":' uri ) ; + bracketed_link = ( '["' link_says '":' %A uri %{ STORE("href"); } :> "]" ) >X ; + + # images + image_src = ( uri ) >A %{ STORE("src"); } ; + image_is = ( A2 C ". "? image_src :> title? ) ; + image_link = ( ":" uri >A %{ STORE_URL("href"); } ) ; + image = ( "["? "!" image_is "!" %A image_link? "]"? ) >X ; + + # footnotes + footno = "[" >X %A digit+ %T "]" ; + + # markup + end_markup_phrase = (" " | PUNCT | EOF | LF) @{ fhold; }; + code = "["? "@" >X mtext >A %T :> "@" "]"? ; + code_tag_start = "]* ">" ; + code_tag_end = "" ; + script_tag = ( "]* ">" (default+ -- "") "" LF? ) >X >A %T ; + notextile = "" >X (default+ -- "") >A %T ""; + strong = "["? "*" >X mtext >A %T :> "*" "]"? ; + b = "["? "**" >X mtext >A %T :> "**" "]"? ; + em = "["? "_" >X mtext >A %T :> "_" "]"? ; + i = "["? "__" >X mtext >A %T :> "__" "]"? ; + del = "[-" >X C ( mtext ) >A %T :>> "-]" ; + emdash_parenthetical_phrase_with_spaces = " -- " mtext " -- " ; + del_phrase = (( " " >A %{ STORE("beginning_space"); } "-") >X C ( mtext ) >A %T :>> ( "-" end_markup_phrase )) - emdash_parenthetical_phrase_with_spaces ; + ins = "["? "+" >X mtext >A %T :> "+" "]"? ; + sup = "[^" >X mtext >A %T :> "^]" ; + sup_phrase = ( "^" when starts_phrase) >X ( mtext ) >A %T :>> ( "^" end_markup_phrase ) ; + sub = "[~" >X mtext >A %T :> "~]" ; + sub_phrase = ( "~" when starts_phrase) >X ( mtext ) >A %T :>> ( "~" end_markup_phrase ) ; + span = "[%" >X mtext >A %T :> "%]" ; + span_phrase = (("%" when starts_phrase) >X ( mtext ) >A %T :>> ( "%" end_markup_phrase )) ; + cite = "["? "??" >X mtext >A %T :> "??" "]"? ; + ignore = "["? "==" >X %A mtext %T :> "==" "]"? ; + snip = "["? "```" >X %A mtext %T :> "```" "]"? ; + + # quotes + quote1 = "'" >X %A mtext %T :> "'" ; + non_quote_chars_or_link = (chars -- '"') | link_noquotes_noactions ; + mtext_inside_quotes = ( non_quote_chars_or_link (mspace non_quote_chars_or_link)* ) ; + html_tag_up_to_attribute_quote = "<" Name space+ NameAttr space* "=" space* ; + quote2 = ('"' >X %A ( mtext_inside_quotes - (mtext_inside_quotes html_tag_up_to_attribute_quote ) ) %T :> '"' ) ; + multi_paragraph_quote = (('"' when starts_line) >X %A ( chars -- '"' ) %T ); + + # html + start_tag = ( "<" Name space+ AttrSet* (AttrEnd)? ">" | "<" Name ">" ) >X >A %T ; + empty_tag = ( "<" Name space+ AttrSet* (AttrEnd)? "/>" | "<" Name "/>" ) >X >A %T ; + end_tag = ( "" ) >X >A %T ; + html_comment = ("") >X >A %T; + + # glyphs + ellipsis = ( " "? >A %T "..." ) >X ; + emdash = "--" ; + arrow = "->" ; + endash = " - " ; + acronym = ( [A-Z] >A [A-Z0-9]{2,} %T "(" default+ >A %{ STORE("title"); } :> ")" ) >X ; + caps_noactions = upper{3,} ; + caps = ( caps_noactions >A %*T ) >X ; + dim_digit = [0-9.]+ ; + prime = ("'" | '"')?; + dim_noactions = dim_digit prime (("x" | " x ") dim_digit prime) %T (("x" | " x ") dim_digit prime)? ; + dim = dim_noactions >X >A %T ; + tm = [Tt] [Mm] ; + trademark = " "? ( "[" tm "]" | "(" tm ")" ) ; + reg = [Rr] ; + registered = " "? ( "[" reg "]" | "(" reg ")" ) ; + cee = [Cc] ; + copyright = ( "[" cee "]" | "(" cee ")" ) ; + entity = ( "&" %A ( '#' digit+ | ( alpha ( alpha | digit )+ ) ) %T ';' ) >X ; + + # info + redcloth_version = "[RedCloth::VERSION]" ; + + other_phrase = phrase -- dim_noactions; + + code_tag := |* + code_tag_end { CAT(block); fgoto main; }; + default => esc_pre; + *|; + + main := |* + + image { INLINE(block, "image"); }; + + link { PARSE_LINK_ATTR("link_text"); PASS(block, "name", "link"); }; + bracketed_link { PARSE_LINK_ATTR("link_text"); PASS(block, "name", "link"); }; + + code { PARSE_ATTR("text"); PASS_CODE(block, "text", "code", opts); }; + code_tag_start { CAT(block); fgoto code_tag; }; + notextile { INLINE(block, "notextile"); }; + strong { PARSE_ATTR("text"); PASS(block, "text", "strong"); }; + b { PARSE_ATTR("text"); PASS(block, "text", "b"); }; + em { PARSE_ATTR("text"); PASS(block, "text", "em"); }; + i { PARSE_ATTR("text"); PASS(block, "text", "i"); }; + del { PASS(block, "text", "del"); }; + del_phrase { PASS(block, "text", "del_phrase"); }; + ins { PARSE_ATTR("text"); PASS(block, "text", "ins"); }; + sup { PARSE_ATTR("text"); PASS(block, "text", "sup"); }; + sup_phrase { PARSE_ATTR("text"); PASS(block, "text", "sup_phrase"); }; + sub { PARSE_ATTR("text"); PASS(block, "text", "sub"); }; + sub_phrase { PARSE_ATTR("text"); PASS(block, "text", "sub_phrase"); }; + span { PARSE_ATTR("text"); PASS(block, "text", "span"); }; + span_phrase { PARSE_ATTR("text"); PASS(block, "text", "span_phrase"); }; + cite { PARSE_ATTR("text"); PASS(block, "text", "cite"); }; + ignore => ignore; + snip { PASS(block, "text", "snip"); }; + quote1 { PASS(block, "text", "quote1"); }; + quote2 { PASS(block, "text", "quote2"); }; + multi_paragraph_quote { PASS(block, "text", "multi_paragraph_quote"); }; + + ellipsis { INLINE(block, "ellipsis"); }; + emdash { INLINE(block, "emdash"); }; + endash { INLINE(block, "endash"); }; + arrow { INLINE(block, "arrow"); }; + caps { INLINE(block, "caps"); }; + acronym { INLINE(block, "acronym"); }; + dim { INLINE(block, "dim"); }; + trademark { INLINE(block, "trademark"); }; + registered { INLINE(block, "registered"); }; + copyright { INLINE(block, "copyright"); }; + footno { PASS(block, "text", "footno"); }; + entity { INLINE(block, "entity"); }; + + script_tag { INLINE(block, "inline_html"); }; + start_tag { INLINE(block, "inline_html"); }; + end_tag { INLINE(block, "inline_html"); }; + empty_tag { INLINE(block, "inline_html"); }; + html_comment { INLINE(block, "inline_html"); }; + + redcloth_version { INLINE(block, "inline_redcloth_version"); }; + + other_phrase => esc; + PUNCT => esc; + space => esc; + + EOF; + + *|; + +}%% + +%% write data nofinal; + + public IRubyObject red_pass_code(IRubyObject self, IRubyObject regs, IRubyObject ref, String meth) { + IRubyObject txt = ((RubyHash)regs).aref(ref); + if(!txt.isNil()) { + IRubyObject txt2 = RubyString.newEmptyString(runtime); + strCatEscapedForPreformatted(self, txt2, ((RubyString)txt).getByteList().bytes, ((RubyString)txt).getByteList().begin, ((RubyString)txt).getByteList().begin + ((RubyString)txt).getByteList().realSize); + ((RubyHash)regs).aset(ref, txt2); + } + return self.callMethod(runtime.getCurrentContext(), meth, regs); + } + + public IRubyObject red_parse_attr(IRubyObject self, IRubyObject regs, IRubyObject ref) { + IRubyObject txt = ((RubyHash)regs).aref(ref); + IRubyObject new_regs = RedclothAttributes.attributes(self, txt); + return regs.callMethod(runtime.getCurrentContext(), "update", new_regs); + } + + public IRubyObject red_parse_link_attr(IRubyObject self, IRubyObject regs, IRubyObject ref) { + IRubyObject txt = ((RubyHash)regs).aref(ref); + IRubyObject new_regs = RedclothAttributes.link_attributes(self, txt); + return regs.callMethod(runtime.getCurrentContext(), "update", new_regs); + } + + public void PASS_CODE(IRubyObject H, String A, String T, int O) { + ((RubyString)H).append(red_pass_code(self, regs, runtime.newSymbol(A), T)); + } + + public void PARSE_ATTR(String A) { + red_parse_attr(self, regs, runtime.newSymbol(A)); + } + + public void PARSE_LINK_ATTR(String A) { + red_parse_link_attr(self, regs, runtime.newSymbol(A)); + } + + private int opts; + private IRubyObject buf; + + public RedclothInline(IRubyObject self, byte[] data, int p, int pe, IRubyObject refs) { +// System.err.println("RedclothInline(data.len: " + data.length + ", p: " + p + ", pe: " + pe + ")"); + this.runtime = self.getRuntime(); + this.self = self; + this.data = data; + this.p = p; + this.pe = p+pe; + this.eof = p+pe; + this.refs = refs; + this.block = RubyString.newEmptyString(runtime); + this.regs = runtime.getNil(); + this.opts = 0; + this.buf = runtime.getNil(); + } + + + public IRubyObject inline() { + %% write init; + %% write exec; + return block; + } + + public static IRubyObject inline2(IRubyObject self, IRubyObject str, IRubyObject refs) { + ByteList bl = str.convertToString().getByteList(); + return new RedclothInline(self, bl.bytes, bl.begin, bl.realSize, refs).inline(); + } +} diff --git a/ext/redcloth_scan/redcloth_scan.java.rl b/ext/redcloth_scan/redcloth_scan.java.rl index c7537c79..b4456eb4 100644 --- a/ext/redcloth_scan/redcloth_scan.java.rl +++ b/ext/redcloth_scan/redcloth_scan.java.rl @@ -15,15 +15,277 @@ import org.jruby.RubyObject; import org.jruby.RubyString; import org.jruby.RubySymbol; import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.CallbackFactory; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.load.BasicLibraryService; +import org.jruby.util.ByteList; public class RedclothScanService implements BasicLibraryService { - private static class Transformer { + public static class Base { + public void LIST_ITEM() { + int aint = 0; + IRubyObject aval = ((RubyArray)list_index).entry(nest-1); + if(!aval.isNil()) { aint = RubyNumeric.fix2int(aval); } + if(list_type.equals("ol")) { + ((RubyArray)list_index).store(nest-1, runtime.newFixnum(aint + 1)); + } + + if(nest > ((RubyArray)list_layout).getLength()) { + listm = list_type + "_open"; + if(list_continue == 1) { + list_continue = 0; + ((RubyHash)regs).aset(runtime.newSymbol("start"), ((RubyArray)list_index).entry(nest-1)); + } else { + IRubyObject start = ((RubyHash)regs).aref(runtime.newSymbol("start")); + if(start.isNil()) { + ((RubyArray)list_index).store(nest-1, runtime.newFixnum(1)); + } else { + IRubyObject start_num = start.callMethod(runtime.getCurrentContext(), "to_i"); + ((RubyArray)list_index).store(nest-1, start_num); + } + } + ((RubyHash)regs).aset(runtime.newSymbol("nest"), runtime.newFixnum(nest)); + ((RubyString)html).append(self.callMethod(runtime.getCurrentContext(), listm, regs)); + ((RubyArray)list_layout).store(nest-1, runtime.newString(list_type)); + CLEAR_REGS(); + ASET("first", "true"); + } + LIST_CLOSE(); + ((RubyHash)regs).aset(runtime.newSymbol("nest"), ((RubyArray)list_layout).length()); + ASET("type", "li_open"); + } + + public void LIST_CLOSE() { + while(nest < ((RubyArray)list_layout).getLength()) { + ((RubyHash)regs).aset(runtime.newSymbol("nest"), ((RubyArray)list_layout).length()); + IRubyObject end_list = ((RubyArray)list_layout).pop(runtime.getCurrentContext()); + if(!end_list.isNil()) { + String s = end_list.convertToString().toString(); + listm = s + "_close"; + ((RubyString)html).append(self.callMethod(runtime.getCurrentContext(), listm, regs)); + } + } + } + + public void TRANSFORM(String T) { + if(p > reg && reg >= ts) { + IRubyObject str = RedclothScanService.transform(self, data, reg, p-reg, refs); + ((RubyHash)regs).aset(runtime.newSymbol(T), str); + } else { + ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.getNil()); + } + } + + public IRubyObject red_pass(IRubyObject self, IRubyObject regs, IRubyObject ref, String meth, IRubyObject refs) { + IRubyObject txt = ((RubyHash)regs).aref(ref); + if(!txt.isNil()) { + ((RubyHash)regs).aset(ref, inline2(self, txt, refs)); + } + return self.callMethod(self.getRuntime().getCurrentContext(), meth, regs); + } + + + public void PASS(IRubyObject H, String A, String T) { + ((RubyString)H).append(red_pass(self, regs, runtime.newSymbol(A), T, refs)); + } + + public void STORE_URL(String T) { + if(p > reg && reg >= ts) { + boolean punct = true; + while(p > reg && punct) { + switch(data[p - 1]) { + case '!': case '"': case '#': case '$': case '%': case ']': case '[': case '&': case '\'': + case '*': case '+': case ',': case '-': case '.': case ')': case '(': case ':': + case ';': case '=': case '?': case '@': case '\\': case '^': case '_': + case '`': case '|': case '~': p--; break; + default: punct = false; + } + } + te = p; + } + STORE(T); + if(!refs.isNil() && refs.callMethod(runtime.getCurrentContext(), "has_key?", ((RubyHash)regs).aref(runtime.newSymbol(T))).isTrue()) { + ((RubyHash)regs).aset(runtime.newSymbol(T), ((RubyHash)refs).aref(((RubyHash)refs).aref(runtime.newSymbol(T)))); + } + } + + public void red_inc(IRubyObject regs, IRubyObject ref) { + int aint = 0; + IRubyObject aval = ((RubyHash)regs).aref(ref); + if(!aval.isNil()) { + aint = RubyNumeric.fix2int(aval); + } + ((RubyHash)regs).aset(ref, regs.getRuntime().newFixnum(aint+1)); + } + + public IRubyObject red_blockcode(IRubyObject self, IRubyObject regs, IRubyObject block) { + Ruby runtime = self.getRuntime(); + IRubyObject btype = ((RubyHash)regs).aref(runtime.newSymbol("type")); + block = block.callMethod(runtime.getCurrentContext(), "strip"); + if(((RubyString)block).getByteList().realSize > 0) { + ((RubyHash)regs).aset(runtime.newSymbol("text"), block); + block = self.callMethod(runtime.getCurrentContext(), btype.asJavaString(), regs); + } + return block; + } + + public IRubyObject red_block(IRubyObject self, IRubyObject regs, IRubyObject block, IRubyObject refs) { + Ruby runtime = self.getRuntime(); + RubySymbol method; + IRubyObject sym_text = runtime.newSymbol("text"); + IRubyObject btype = ((RubyHash)regs).aref(runtime.newSymbol("type")); + block = block.callMethod(runtime.getCurrentContext(), "strip"); + if(!block.isNil() && !btype.isNil()) { + method = btype.convertToString().intern(); + if(method == runtime.newSymbol("notextile")) { + ((RubyHash)regs).aset(sym_text, block); + } else { + ((RubyHash)regs).aset(sym_text, inline2(self, block, refs)); + } + if(self.respondsTo(method.asJavaString())) { + block = self.callMethod(runtime.getCurrentContext(), method.asJavaString(), regs); + } else { + IRubyObject fallback = ((RubyHash)regs).aref(runtime.newSymbol("fallback")); + if(!fallback.isNil()) { + ((RubyString)fallback).append(((RubyHash)regs).aref(sym_text)); + CLEAR_REGS(); + ((RubyHash)regs).aset(sym_text, fallback); + } + block = self.callMethod(runtime.getCurrentContext(), "p", regs); + } + } + + return block; + } + + public void strCatEscaped(IRubyObject self, IRubyObject str, byte[] data, int ts, int te) { + IRubyObject sourceStr = RubyString.newString(self.getRuntime(), data, ts, te-ts); + IRubyObject escapedStr = self.callMethod(self.getRuntime().getCurrentContext(), "escape", sourceStr); + ((RubyString)str).concat(escapedStr); + } + + public void strCatEscapedForPreformatted(IRubyObject self, IRubyObject str, byte[] data, int ts, int te) { + IRubyObject sourceStr = RubyString.newString(self.getRuntime(), data, ts, te-ts); + IRubyObject escapedStr = self.callMethod(self.getRuntime().getCurrentContext(), "escape_pre", sourceStr); + ((RubyString)str).concat(escapedStr); + } + + public void CLEAR(IRubyObject obj) { + if(block == obj) { + block = RubyString.newEmptyString(runtime); + } else if(html == obj) { + html = RubyString.newEmptyString(runtime); + } else if(table == obj) { + table = RubyString.newEmptyString(runtime); + } + } + + public void ADD_BLOCK() { + ((RubyString)html).append(red_block(self, regs, block, refs)); + extend = runtime.getNil(); + CLEAR(block); + CLEAR_REGS(); + } + + public void CLEAR_REGS() { + regs = RubyHash.newHash(runtime); + } + + public void CAT(IRubyObject H) { + ((RubyString)H).cat(data, ts, te-ts); + } + + public void INLINE(IRubyObject H, String T) { + ((RubyString)H).append(self.callMethod(runtime.getCurrentContext(), T, regs)); + } + + public void DONE(IRubyObject H) { + ((RubyString)html).append(H); + CLEAR(H); + CLEAR_REGS(); + } + + public void ADD_EXTENDED_BLOCK() { + ((RubyString)html).append(red_block(self, regs, block, refs)); + CLEAR(block); + } + + public void ADD_BLOCKCODE() { + ((RubyString)html).append(red_blockcode(self, regs, block)); + CLEAR(block); + CLEAR_REGS(); + } + + public void ADD_EXTENDED_BLOCKCODE() { + ((RubyString)html).append(red_blockcode(self, regs, block)); + CLEAR(block); + } + + public void AINC(String T) { + red_inc(regs, runtime.newSymbol(T)); + } + + public void END_EXTENDED() { + extend = runtime.getNil(); + CLEAR_REGS(); + } + + public void ASET(String T, String V) { + ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.newString(V)); + } + + public void STORE(String T) { + if(p > reg && reg >= ts) { + IRubyObject str = RubyString.newString(runtime, data, reg, p-reg); + ((RubyHash)regs).aset(runtime.newSymbol(T), str); + } else { + ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.getNil()); + } + } + + public void STORE_B(String T) { + if(p > bck && bck >= ts) { + IRubyObject str = RubyString.newString(runtime, data, bck, p-bck); + ((RubyHash)regs).aset(runtime.newSymbol(T), str); + } else { + ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.getNil()); + } + } + + public IRubyObject self; + public byte[] data; + public int p, pe; + public IRubyObject refs; + + public Ruby runtime; + public int orig_p, orig_pe; + public int cs, act, nest; + public int ts = -1; + public int te = -1; + public int reg = -1; + public int bck = -1; + public int eof = -1; + + public IRubyObject html; + public IRubyObject table; + public IRubyObject block; + public IRubyObject regs; + + public IRubyObject list_layout; + public String list_type = null; + public IRubyObject list_index; + public int list_continue = 0; + public IRubyObject plain_block; + public IRubyObject extend; + public String listm = ""; + public IRubyObject refs_found; + } + + private static class Transformer extends Base { %%{ machine redcloth_scan; @@ -348,271 +610,18 @@ public class RedclothScanService implements BasicLibraryService { %% write data nofinal; - public void LIST_ITEM() { - int aint = 0; - IRubyObject aval = ((RubyArray)list_index).entry(nest-1); - if(!aval.isNil()) { aint = RubyNumeric.fix2int(aval); } - if(list_type.equals("ol")) { - ((RubyArray)list_index).store(nest-1, runtime.newFixnum(aint + 1)); - } - - if(nest > ((RubyArray)list_layout).getLength()) { - listm = list_type + "_open"; - if(list_continue == 1) { - list_continue = 0; - ((RubyHash)regs).aset(runtime.newSymbol("start"), ((RubyArray)list_index).entry(nest-1)); - } else { - IRubyObject start = ((RubyHash)regs).aref(runtime.newSymbol("start")); - if(start.isNil()) { - ((RubyArray)list_index).store(nest-1, runtime.newFixnum(1)); - } else { - IRubyObject start_num = start.callMethod(runtime.getCurrentContext(), "to_i"); - ((RubyArray)list_index).store(nest-1, start_num); - } - } - ((RubyHash)regs).aset(runtime.newSymbol("nest"), runtime.newFixnum(nest)); - ((RubyString)html).append(self.callMethod(runtime.getCurrentContext(), listm, regs)); - ((RubyArray)list_layout).store(nest-1, runtime.newString(list_type)); - CLEAR_REGS(); - ASET("first", "true"); - } - LIST_CLOSE(); - ((RubyHash)regs).aset(runtime.newSymbol("nest"), ((RubyArray)list_layout).length()); - ASET("type", "li_open"); - } - - public void LIST_CLOSE() { - while(nest < ((RubyArray)list_layout).getLength()) { - ((RubyHash)regs).aset(runtime.newSymbol("nest"), ((RubyArray)list_layout).length()); - IRubyObject end_list = ((RubyArray)list_layout).pop(runtime.getCurrentContext()); - if(!end_list.isNil()) { - String s = end_list.convertToString().toString(); - listm = s + "_close"; - ((RubyString)html).append(self.callMethod(runtime.getCurrentContext(), listm, regs)); - } - } - } - - public void TRANSFORM(String T) { - if(p > reg && reg >= ts) { - IRubyObject str = RedclothScanService.transform(self, data, reg, p, refs); - ((RubyHash)regs).aset(runtime.newSymbol(T), str); - } else { - ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.getNil()); - } - } - - public IRubyObject red_pass(IRubyObject self, IRubyObject regs, IRubyObject ref, String meth, IRubyObject refs) { - IRubyObject txt = ((RubyHash)regs).aref(ref); - if(!txt.isNil()) { - ((RubyHash)regs).aset(ref, inline2(self, txt, refs)); - } - return self.callMethod(self.getRuntime().getCurrentContext(), meth, regs); - } - - - public void PASS(IRubyObject H, String A, String T) { - ((RubyString)H).append(red_pass(self, regs, runtime.newSymbol(A), T, refs)); - } - - public void STORE_URL(String T) { - if(p > reg && reg >= ts) { - boolean punct = true; - while(p > reg && punct) { - switch(data[p - 1]) { - case '!': case '"': case '#': case '$': case '%': case ']': case '[': case '&': case '\'': - case '*': case '+': case ',': case '-': case '.': case ')': case '(': case ':': - case ';': case '=': case '?': case '@': case '\\': case '^': case '_': - case '`': case '|': case '~': p--; break; - default: punct = false; - } - } - te = p; - } - STORE(T); - if(!refs.isNil() && refs.callMethod(runtime.getCurrentContext(), "has_key?", ((RubyHash)regs).aref(runtime.newSymbol(T))).isTrue()) { - ((RubyHash)regs).aset(runtime.newSymbol(T), ((RubyHash)refs).aref(((RubyHash)refs).aref(runtime.newSymbol(T)))); - } - } - - public void red_inc(IRubyObject regs, IRubyObject ref) { - int aint = 0; - IRubyObject aval = ((RubyHash)regs).aref(ref); - if(!aval.isNil()) { - aint = RubyNumeric.fix2int(aval); - } - ((RubyHash)regs).aset(ref, regs.getRuntime().newFixnum(aint+1)); - } - - public IRubyObject red_blockcode(IRubyObject self, IRubyObject regs, IRubyObject block) { - Ruby runtime = self.getRuntime(); - IRubyObject btype = ((RubyHash)regs).aref(runtime.newSymbol("type")); - block = block.callMethod(runtime.getCurrentContext(), "strip"); - if(((RubyString)block).getByteList().realSize > 0) { - ((RubyHash)regs).aset(runtime.newSymbol("text"), block); - block = self.callMethod(runtime.getCurrentContext(), btype.asJavaString(), regs); - } - return block; - } - - public IRubyObject red_block(IRubyObject self, IRubyObject regs, IRubyObject block, IRubyObject refs) { - Ruby runtime = self.getRuntime(); - RubySymbol method; - IRubyObject sym_text = runtime.newSymbol("text"); - IRubyObject btype = ((RubyHash)regs).aref(runtime.newSymbol("type")); - block = block.callMethod(runtime.getCurrentContext(), "strip"); - if(!block.isNil() && !btype.isNil()) { - method = btype.convertToString().intern(); - if(method == runtime.newSymbol("notextile")) { - ((RubyHash)regs).aset(sym_text, block); - } else { - ((RubyHash)regs).aset(sym_text, inline2(self, block, refs)); - } - if(self.respondsTo(method.asJavaString())) { - block = self.callMethod(runtime.getCurrentContext(), method.asJavaString(), regs); - } else { - IRubyObject fallback = ((RubyHash)regs).aref(runtime.newSymbol("fallback")); - if(!fallback.isNil()) { - ((RubyString)fallback).append(((RubyHash)regs).aref(sym_text)); - CLEAR_REGS(); - ((RubyHash)regs).aset(sym_text, fallback); - } - block = self.callMethod(runtime.getCurrentContext(), "p", regs); - } - } - - return block; - } - - public void strCatEscaped(IRubyObject self, IRubyObject str, byte[] data, int ts, int te) { - IRubyObject sourceStr = RubyString.newString(self.getRuntime(), data, ts, te-ts); - IRubyObject escapedStr = self.callMethod(self.getRuntime().getCurrentContext(), "escape", sourceStr); - ((RubyString)str).concat(escapedStr); - } - - public void strCatEscapedForPreformatted(IRubyObject self, IRubyObject str, byte[] data, int ts, int te) { - IRubyObject sourceStr = RubyString.newString(self.getRuntime(), data, ts, te-ts); - IRubyObject escapedStr = self.callMethod(self.getRuntime().getCurrentContext(), "escape_pre", sourceStr); - ((RubyString)str).concat(escapedStr); - } - - public void CLEAR(IRubyObject obj) { - if(block == obj) { - block = RubyString.newEmptyString(runtime); - } else if(html == obj) { - html = RubyString.newEmptyString(runtime); - } else if(table == obj) { - table = RubyString.newEmptyString(runtime); - } - } - - public void ADD_BLOCK() { - ((RubyString)html).append(red_block(self, regs, block, refs)); - extend = runtime.getNil(); - CLEAR(block); - CLEAR_REGS(); - } - - public void CLEAR_REGS() { - regs = RubyHash.newHash(runtime); - } - - public void CAT(IRubyObject H) { - ((RubyString)H).cat(data, ts, te-ts); - } - - public void INLINE(IRubyObject H, String T) { - ((RubyString)H).append(self.callMethod(runtime.getCurrentContext(), T, regs)); - } - - public void DONE(IRubyObject H) { - ((RubyString)html).append(H); - CLEAR(H); - CLEAR_REGS(); - } - - public void ADD_EXTENDED_BLOCK() { - ((RubyString)html).append(red_block(self, regs, block, refs)); - CLEAR(block); - } - - public void ADD_BLOCKCODE() { - ((RubyString)html).append(red_blockcode(self, regs, block)); - CLEAR(block); - CLEAR_REGS(); - } - - public void ADD_EXTENDED_BLOCKCODE() { - ((RubyString)html).append(red_blockcode(self, regs, block)); - CLEAR(block); - } - - public void AINC(String T) { - red_inc(regs, runtime.newSymbol(T)); - } - - public void END_EXTENDED() { - extend = runtime.getNil(); - CLEAR_REGS(); - } - - public void ASET(String T, String V) { - ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.newString(V)); - } - - public void STORE(String T) { - if(p > reg && reg >= ts) { - IRubyObject str = RubyString.newString(runtime, data, reg, p-reg); - ((RubyHash)regs).aset(runtime.newSymbol(T), str); - } else { - ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.getNil()); - } - } - - public void STORE_B(String T) { - if(p > bck && bck >= ts) { - IRubyObject str = RubyString.newString(runtime, data, bck, p-bck); - ((RubyHash)regs).aset(runtime.newSymbol(T), str); - } else { - ((RubyHash)regs).aset(runtime.newSymbol(T), runtime.getNil()); - } - } - - private IRubyObject self; - private byte[] data; - private int p, pe; - private IRubyObject refs; - - private Ruby runtime; - private int orig_p, orig_pe; - private int cs, act, nest; - private int ts = -1; - private int te = -1; - private int reg = -1; - private int bck = -1; - private int eof = -1; - - private IRubyObject html; - private IRubyObject table; - private IRubyObject block; - private IRubyObject regs; - - private IRubyObject list_layout; - private String list_type = null; - private IRubyObject list_index; - private int list_continue = 0; - private IRubyObject plain_block; - private IRubyObject extend; - private String listm = ""; - private IRubyObject refs_found; - public Transformer(IRubyObject self, byte[] data, int p, int pe, IRubyObject refs) { +// System.err.println("Transformer(data.len: " + data.length + ", p: " + p + ", pe: " + pe + ")"); + if(p+pe > data.length) { + throw new RuntimeException("BLAHAHA"); + } this.self = self; this.data = data; this.p = p; - this.pe = pe; + this.pe = p+pe; + this.eof = p+pe; this.refs = refs; - + runtime = self.getRuntime(); orig_p = p; orig_pe = pe; @@ -633,9 +642,15 @@ public class RedclothScanService implements BasicLibraryService { %% write init; %% write exec; + +// System.err.println("gah: p: " + p + " pe: " + pe); if(((RubyString)block).getByteList().realSize > 0) { -// ADD_BLOCK(); + ADD_BLOCK(); } +// System.err.println("gah2: p: " + p + " pe: " + pe); +// System.err.println(" html: " + html); +// System.err.println(" table: " + table); +// System.err.println(" block: " + block); if(refs.isNil() && !refs_found.callMethod(runtime.getCurrentContext(), "empty?").isTrue()) { return RedclothScanService.transform(self, data, orig_p, orig_pe, refs_found); @@ -651,7 +666,7 @@ public class RedclothScanService implements BasicLibraryService { } public static IRubyObject inline2(IRubyObject workingCopy, IRubyObject self, IRubyObject refs) { - return workingCopy.getRuntime().getNil(); + return RedclothInline.inline2(workingCopy, self, refs); } public static IRubyObject transform2(IRubyObject self, IRubyObject str) { @@ -678,12 +693,137 @@ public class RedclothScanService implements BasicLibraryService { @JRubyMethod(rest=true) public static IRubyObject html_esc(IRubyObject self, IRubyObject[] args) { - return self.getRuntime().getNil(); + Ruby runtime = self.getRuntime(); + IRubyObject str = runtime.getNil(), + level = runtime.getNil(); + if(Arity.checkArgumentCount(runtime, args, 1, 2) == 2) { + level = args[1]; + } + str = args[0]; +//System.err.println("html_esc called: " + self + ",,, args: " +str + ", " + level); + + IRubyObject new_str = RubyString.newEmptyString(runtime); + if(str.isNil()) { +// System.err.println("isnil1"); + return new_str; + } + + ByteList bl = str.convertToString().getByteList(); + + if(bl.realSize == 0) { +// System.err.println("isnil2"); + return new_str; + } + + byte[] bytes = bl.bytes; + int ts = bl.begin; + int te = ts + bl.realSize; + int t = ts, t2 = ts; + String ch = null; + + if(te <= ts) { + return new_str; + } + + while(t2 < te) { + ch = null; + // normal + pre + switch(bytes[t2]) { + case '&': ch = "amp"; break; + case '>': ch = "gt"; break; + case '<': ch = "lt"; break; + } + + // normal (non-pre) + if(level != runtime.newSymbol("html_escape_preformatted")) { + switch(bytes[t2]) { + case '\n': ch = "br"; break; + case '"' : ch = "quot"; break; + case '\'': + ch = (level == runtime.newSymbol("html_escape_attributes")) ? "apos" : "squot"; + break; + } + } + + if(ch != null) { + if(t2 > t) { + ((RubyString)new_str).cat(bytes, t, t2-t); + } + ((RubyString)new_str).concat(self.callMethod(runtime.getCurrentContext(), ch, RubyHash.newHash(runtime))); + t = t2 + 1; + } + + t2++; + } + + + if(t2 > t) { + ((RubyString)new_str).cat(bytes, t, t2-t); + } + +// System.err.println(" returning: " + new_str); + return new_str; } @JRubyMethod public static IRubyObject latex_esc(IRubyObject self, IRubyObject str) { - return self.getRuntime().getNil(); + Ruby runtime = self.getRuntime(); + IRubyObject new_str = RubyString.newEmptyString(runtime); + + if(str.isNil()) { + return new_str; + } + + ByteList bl = str.convertToString().getByteList(); + + if(bl.realSize == 0) { + return new_str; + } + + byte[] bytes = bl.bytes; + int ts = bl.begin; + int te = ts + bl.realSize; + int t = ts; + int t2 = ts; + String ch = null; + + while(t2 < te) { + ch = null; + + switch(bytes[t2]) { + case '{': ch = "#123"; break; + case '}': ch = "#125"; break; + case '\\': ch = "#92"; break; + case '#': ch = "#35"; break; + case '$': ch = "#36"; break; + case '%': ch = "#37"; break; + case '&': ch = "amp"; break; + case '_': ch = "#95"; break; + case '^': ch = "circ"; break; + case '~': ch = "tilde"; break; + case '<': ch = "lt"; break; + case '>': ch = "gt"; break; + case '\n': ch = "#10"; break; + } + + if(ch != null) { + if(t2 > t) { + ((RubyString)new_str).cat(bytes, t, t2-t); + } + IRubyObject opts = RubyHash.newHash(runtime); + ((RubyHash)opts).aset(runtime.newSymbol("text"), runtime.newString(ch)); + ((RubyString)new_str).concat(self.callMethod(runtime.getCurrentContext(), "entity", opts)); + t = t2 + 1; + } + + t2++; + } + + if(t2 > t) { + ((RubyString)new_str).cat(bytes, t, t2-t); + } + + return new_str; } public boolean basicLoad(final Ruby runtime) throws IOException {