UNPKG

ncr

Version:

A parser and converter for Unicode Characters, including (standard and extended) Numerical Character References

215 lines (185 loc) 9.62 kB
"use strict" ############################################################################################################ CND = require 'cnd' rpr = CND.rpr badge = 'CLOAK' log = CND.get_logger 'plain', badge info = CND.get_logger 'info', badge whisper = CND.get_logger 'whisper', badge alert = CND.get_logger 'alert', badge debug = CND.get_logger 'debug', badge warn = CND.get_logger 'warn', badge help = CND.get_logger 'help', badge urge = CND.get_logger 'urge', badge echo = CND.echo.bind CND rainbow = CND.rainbow.bind CND types = require './types' { isa validate type_of } = types.export() ### Cloaking characters by chained replacements: Assuming an alphabet `/[0-9]/`, cloaking characters starting from `0`. To cloak only `0`, we have to free the string of all occurrences of that character. In order to do so, we choose a primary escapement character, '1', and a secondary escapement character, conveniently also `1`. With those, we can replace all occurrences of `0` as `11`. However, that alone would produce ambiguous sequences. For example, the string `011` results in `1111`, but so does the string `1111` itself (because it does not contain a `0`, it remains unchanged when replacing `0`). Therefore, we have to escape the secondary escapement character itself, too; we choose the secondary replacement `1 -> 12` which has to come *first* when cloaking and *second* when uncloaking. This results in the following cloaking chain: CLOAK.new '012' 0123456789 1 -> 12: 01223456789 0 -> 11: 111223456789 The resulting string is free of `0`s. Because all original `0`s and `1`s have been preserved in disguise, we are now free to insert additional data into the string. Let's assume we have a text transformer `f`, say, `f ( x ) -> x.replace /456/g, '15'`, and a more comprehensive text transformer `g` which includes calls to `f` and other elementary transforms. Now, we would like to apply `g` to our text `0123456789`, but specifically omit the transformation performed by `f` (which would turn `0123456789` into `012315789`). We can do so by choosing a cloaking character—`0` in this example—and one or more signal characters that will pass unmodified through `g`. Assuming we cloak `456` as `01`, we first escape `0123456789` to `111223456789` so that all `0`s are removed. Then, we symbolize all occurrances of `456` as `01`, leading to `11122301789`. This string may be fed to `g` and will pass through `f` untouched. We can then reverse our steps: `11122301789` ... `111223456789` ... `01223456789` ... `0123456789`—which is indeed the string we're started with. Of course, this could not have worked if `g` had somehow transformed any of our cloaking devices; therefore, it is important to choose codepoints that are certain to be transparent to the intended text transformation. In case more primary escapement characters are needed, the chain may be expanded to include more replacement steps. In particular, it is interesting to use exactly two primary escapements; that way, we can define cloaked sequences of arbitrary lengths, using the two escapements—`0` and `1` in this example—as start and stop brackets: CLOAK.new '01234' 0123456789 2 -> 24: 01243456789 1 -> 23: 023243456789 0 -> 22: 2223243456789 Using more than two primary escapements is possible: CLOAK.new '0123456' 0123456789 3 -> 36: 01236456789 2 -> 35: 013536456789 1 -> 34: 0343536456789 0 -> 33: 33343536456789 CLOAK.new '012345678' 0123456789 4 -> 48: 01234856789 3 -> 47: 012474856789 2 -> 46: 0146474856789 1 -> 45: 04546474856789 0 -> 44: 444546474856789 ### #----------------------------------------------------------------------------------------------------------- ### from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions ### esc_re = ( text ) -> text.replace /[.*+?^${}()|[\]\\]/g, "\\$&" #----------------------------------------------------------------------------------------------------------- @new = ( chrs ) -> #......................................................................................................... unless chrs? chrs = [ '\x10', '\x11', '\x12', '\x13', '\x14', ] #......................................................................................................... else if isa.text chrs chrs = Array.from chrs #......................................................................................................... else unless isa.list chrs throw new Error "expected a text or a list, got a #{type_of chrs}" #......................................................................................................... unless ( chr_count = chrs.length ) >= 3 throw new Error "expected at least 3 characters, got #{chr_count}" #......................................................................................................... if chr_count % 2 is 0 throw new Error "expected an odd number of characters, got #{chr_count}" #......................................................................................................... delta = ( chr_count + 1 ) / 2 - 1 master = chrs[ chr_count - delta - 1 ] meta_chr_patterns = ( /// #{esc_re chrs[ idx ]} ///g for idx in [ 0 .. delta ] ) target_seq_chrs = ( "#{master}#{chrs[ idx + delta ]}" for idx in [ 0 .. delta ] ) target_seq_patterns = ( /// #{esc_re target_seq_chrs[ idx ]} ///g for idx in [ 0 .. delta ] ) cloaked = chrs[ 0 ... delta ] #--------------------------------------------------------------------------------------------------------- hide = ( text ) => R_ = text R_ = R_.replace meta_chr_patterns[ idx ], target_seq_chrs[ idx ] for idx in [ delta .. 0 ] by -1 return R_ #......................................................................................................... reveal = ( text ) => R_ = text R_ = R_.replace target_seq_patterns[ idx ], chrs[ idx ] for idx in [ 0 .. delta ] return R_ #--------------------------------------------------------------------------------------------------------- R = { '~isa': 'CLOAK/cloak', hide, reveal, cloaked, master, } @_mixin_backslashed R return R #----------------------------------------------------------------------------------------------------------- @_mixin_backslashed = ( cloak ) -> { cloaked } = cloak if cloaked.length < 2 then start_chr = stop_chr = cloaked[ 0 ] else [ start_chr, stop_chr, ] = cloaked base = 10 ### `oc`: 'original character' ### _oc_backslash = '\\' ### `op`: 'original pattern' ### _oce_backslash = esc_re _oc_backslash _mcp_backslash = /// #{esc_re _oc_backslash} ( (?: [ \ud800-\udbff ] [ \udc00-\udfff ] ) | . ) ///g _tsp_backslash = /// #{esc_re start_chr} ( [ 0-9 a-z ]+ ) #{esc_re stop_chr} ///g ### `rm`: 'remove' ### _rm_backslash = /// #{esc_re _oc_backslash} ( . ) ///g #--------------------------------------------------------------------------------------------------------- hide = ( text ) => R = text R = R.replace _mcp_backslash, ( _, $1 ) -> cid_hex = ( $1.codePointAt 0 ).toString base return "#{start_chr}#{cid_hex}#{stop_chr}" return R #......................................................................................................... reveal = ( text ) => R = text R = R.replace _tsp_backslash, ( _, $1 ) -> chr = String.fromCodePoint parseInt $1, base return "#{_oc_backslash}#{chr}" return R #......................................................................................................... remove = ( text ) => return text.replace _rm_backslash, '$1' #--------------------------------------------------------------------------------------------------------- cloak[ 'backslashed' ] = { hide, reveal, remove, } return null ############################################################################################################ unless module.parent? CLOAK = @ DIFF = require 'coffeenode-diff' cloak = CLOAK.new '()LTX' cloak = CLOAK.new '*+?^$' help cloak text = """ % & ! ; some <<unlicensed>> (stuff here). \\𠄨 *20128+? &%!%A&123; some more \\\\<<unlicensed\\\\>> (stuff here). some \\<<licensed\\>> stuff here, and <\\< The <<<\\LaTeX{}>>> Logo: `<<<\\LaTeX{}>>>` """ # debug '©94643', @_mcp_backslash # text = "% ; 2 3 \\ \\\\ \\𠄨" # text = "0 1 2 3 4 5 6 7 8" # text = "<<" log '(1) -', CND.rainbow ( text ) cloaked_text = text log '(2) -', CND.rainbow ( cloaked_text = cloak.hide cloaked_text ) log '(3) -', CND.rainbow ( cloaked_text = cloak.backslashed.hide cloaked_text ) uncloaked_text = cloaked_text log '(4) -', CND.rainbow ( uncloaked_text = cloak.backslashed.reveal uncloaked_text ) log '(5) -', CND.rainbow ( uncloaked_text = cloak.reveal uncloaked_text ) log '(7) -', CND.rainbow '©79011', cloak.backslashed.remove uncloaked_text if uncloaked_text isnt text log DIFF.colorize text, uncloaked_text log CND.steel '########################################################################'