1%- 2% Copyright (c) 2012-2014 Yakaz 3% Copyright (c) 2016-2018 Jean-Sébastien Pédron <jean-sebastien.pedron@dumbbell.fr> 4% All rights reserved. 5% 6% Redistribution and use in source and binary forms, with or without 7% modification, are permitted provided that the following conditions 8% are met: 9% 1. Redistributions of source code must retain the above copyright 10% notice, this list of conditions and the following disclaimer. 11% 2. Redistributions in binary form must reproduce the above copyright 12% notice, this list of conditions and the following disclaimer in the 13% documentation and/or other materials provided with the distribution. 14% 15% THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16% ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18% ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19% FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20% DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21% OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24% OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25% SUCH DAMAGE. 26 27%% @private 28 29-module(yamerl_node_int_ext). 30 31-include("yamerl_errors.hrl"). 32-include("yamerl_tokens.hrl"). 33-include("yamerl_nodes.hrl"). 34-include("internal/yamerl_constr.hrl"). 35 36%% Public API. 37-export([ 38 tags/0, 39 try_construct_token/3, 40 construct_token/3, 41 node_pres/1, 42 string_to_integer/1 43 ]). 44 45%% Internal use only. 46-export([ 47 base2_to_integer/2, 48 base8_to_integer/2, 49 base10_to_integer/2, 50 base16_to_integer/2, 51 base60_to_integer/3 52 ]). 53 54-define(TAG, "tag:yaml.org,2002:int"). 55 56%% ------------------------------------------------------------------- 57%% Public API. 58%% ------------------------------------------------------------------- 59 60tags() -> [?TAG]. 61 62try_construct_token(Constr, Node, 63 #yamerl_scalar{tag = #yamerl_tag{uri = {non_specific, "?"}}} = Token) -> 64 try 65 construct_token(Constr, Node, Token) 66 catch 67 _:#yamerl_parsing_error{name = not_an_integer} -> 68 unrecognized 69 end; 70try_construct_token(_, _, _) -> 71 unrecognized. 72 73construct_token(#yamerl_constr{detailed_constr = false}, 74 undefined, #yamerl_scalar{text = Text} = Token) -> 75 case string_to_integer(Text) of 76 error -> 77 exception(Token); 78 Int -> 79 {finished, Int} 80 end; 81construct_token(#yamerl_constr{detailed_constr = true}, 82 undefined, #yamerl_scalar{text = Text} = Token) -> 83 case string_to_integer(Text) of 84 error -> 85 exception(Token); 86 Int -> 87 Pres = yamerl_constr:get_pres_details(Token), 88 Node = #yamerl_int{ 89 module = ?MODULE, 90 tag = ?TAG, 91 pres = Pres, 92 value = Int 93 }, 94 {finished, Node} 95 end; 96 97construct_token(_, _, Token) -> 98 exception(Token). 99 100node_pres(Node) -> 101 ?NODE_PRES(Node). 102 103%% ------------------------------------------------------------------- 104%% Internal functions. 105%% ------------------------------------------------------------------- 106 107%% Sign. 108string_to_integer([$+ | Text]) -> 109 string_to_integer2(Text); 110string_to_integer([$- | Text]) -> 111 case string_to_integer2(Text) of 112 error -> error; 113 Int -> -Int 114 end; 115string_to_integer(Text) -> 116 string_to_integer2(Text). 117 118%% Base. 119string_to_integer2("0b" ++ Text) -> 120 base2_to_integer(Text, 0); 121string_to_integer2("0x" ++ Text) -> 122 base16_to_integer(Text, 0); 123string_to_integer2("0" ++ Text) -> 124 base8_to_integer(Text, 0); 125string_to_integer2(Text) -> 126 Opts = [{capture, none}, unicode], 127 case re:run(Text, "^[1-9][0-9_]*(:[0-5]?[0-9])+$", Opts) of 128 match -> base60_to_integer(Text, 0, 0); 129 nomatch -> base10_to_integer(Text, 0) 130 end. 131 132%% Parsing. 133base10_to_integer([C | Rest], Int) when C >= $0 andalso C =< $9 -> 134 Int1 = (Int * 10) + (C - $0), 135 base10_to_integer(Rest, Int1); 136base10_to_integer([$_ | Rest], Int) -> 137 base10_to_integer(Rest, Int); 138base10_to_integer([], Int) -> 139 Int; 140base10_to_integer(_, _) -> 141 error. 142 143base2_to_integer([C | Rest], Int) when C == $0 orelse C == $1 -> 144 Int1 = (Int * 2) + (C - $0), 145 base2_to_integer(Rest, Int1); 146base2_to_integer([$_ | Rest], Int) -> 147 base2_to_integer(Rest, Int); 148base2_to_integer([], Int) -> 149 Int; 150base2_to_integer(_, _) -> 151 error. 152 153base8_to_integer([C | Rest], Int) when C >= $0 andalso C =< $7 -> 154 Int1 = (Int * 8) + (C - $0), 155 base8_to_integer(Rest, Int1); 156base8_to_integer([$_ | Rest], Int) -> 157 base8_to_integer(Rest, Int); 158base8_to_integer([], Int) -> 159 Int; 160base8_to_integer(_, _) -> 161 error. 162 163base16_to_integer([C | Rest], Int) when C >= $0 andalso C =< $9 -> 164 Int1 = (Int * 16) + (C - $0), 165 base16_to_integer(Rest, Int1); 166base16_to_integer([C | Rest], Int) when C >= $a andalso C =< $f -> 167 Int1 = (Int * 16) + (C - $a + 10), 168 base16_to_integer(Rest, Int1); 169base16_to_integer([C | Rest], Int) when C >= $A andalso C =< $F -> 170 Int1 = (Int * 16) + (C - $A + 10), 171 base16_to_integer(Rest, Int1); 172base16_to_integer([$_ | Rest], Int) -> 173 base16_to_integer(Rest, Int); 174base16_to_integer([], Int) -> 175 Int; 176base16_to_integer(_, _) -> 177 error. 178 179base60_to_integer([C | Rest], Current, Int) when C >= $0 andalso C =< $9 -> 180 Current1 = (Current * 10) + (C - $0), 181 base60_to_integer(Rest, Current1, Int); 182base60_to_integer([$: | Rest], Current, Int) -> 183 Int1 = (Int * 60) + Current, 184 base60_to_integer(Rest, 0, Int1); 185base60_to_integer([$_ | Rest], Current, Int) -> 186 base60_to_integer(Rest, Current, Int); 187base60_to_integer([], Current, Int) -> 188 (Int * 60) + Current; 189base60_to_integer(_, _, _) -> 190 error. 191 192exception(Token) -> 193 Error = #yamerl_parsing_error{ 194 name = not_an_integer, 195 token = Token, 196 text = "Invalid integer", 197 line = ?TOKEN_LINE(Token), 198 column = ?TOKEN_COLUMN(Token) 199 }, 200 throw(Error). 201