Mercurial Hosting > luan
comparison core/src/luan/modules/parsers/Json.java @ 720:bdd766df1c17
add json parser
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 30 May 2016 21:27:17 -0600 |
parents | |
children | c29d11d675fd |
comparison
equal
deleted
inserted
replaced
719:53fc65ffa65f | 720:bdd766df1c17 |
---|---|
1 package luan.modules.parsers; | |
2 | |
3 import java.util.List; | |
4 import java.util.ArrayList; | |
5 import java.util.Map; | |
6 import java.util.LinkedHashMap; | |
7 import luan.LuanTable; | |
8 | |
9 | |
10 public final class Json { | |
11 | |
12 public static Object parse(String text) throws ParseException { | |
13 return new Json(text).parse(); | |
14 } | |
15 | |
16 private final Parser parser; | |
17 | |
18 private Json(String text) { | |
19 this.parser = new Parser(text); | |
20 } | |
21 | |
22 private ParseException exception(String msg) { | |
23 return new ParseException(parser,msg); | |
24 } | |
25 | |
26 private Object parse() throws ParseException { | |
27 spaces(); | |
28 Object value = value(); | |
29 spaces(); | |
30 if( !parser.endOfInput() ) | |
31 throw exception("unexpected text"); | |
32 return value; | |
33 } | |
34 | |
35 private Object value() throws ParseException { | |
36 if( parser.match("null") ) | |
37 return null; | |
38 if( parser.match("true") ) | |
39 return Boolean.TRUE; | |
40 if( parser.match("false") ) | |
41 return Boolean.FALSE; | |
42 String s = string(); | |
43 if( s != null ) | |
44 return s; | |
45 Number n = number(); | |
46 if( n != null ) | |
47 return n; | |
48 LuanTable a = array(); | |
49 if( a != null ) | |
50 return a; | |
51 LuanTable o = object(); | |
52 if( o != null ) | |
53 return o; | |
54 throw exception("invalid value"); | |
55 } | |
56 | |
57 private String string() throws ParseException { | |
58 parser.begin(); | |
59 if( !parser.match('"') ) | |
60 return parser.failure(null); | |
61 StringBuilder sb = new StringBuilder(); | |
62 while( parser.anyChar() ) { | |
63 char c = parser.lastChar(); | |
64 switch(c) { | |
65 case '"': | |
66 return parser.success(sb.toString()); | |
67 case '\\': | |
68 if( parser.anyChar() ) { | |
69 c = parser.lastChar(); | |
70 switch(c) { | |
71 case '"': | |
72 case '\\': | |
73 case '/': | |
74 sb.append(c); | |
75 continue; | |
76 case 'b': | |
77 sb.append('\b'); | |
78 continue; | |
79 case 'f': | |
80 sb.append('\f'); | |
81 continue; | |
82 case 'n': | |
83 sb.append('\n'); | |
84 continue; | |
85 case 'r': | |
86 sb.append('\r'); | |
87 continue; | |
88 case 't': | |
89 sb.append('\t'); | |
90 continue; | |
91 case 'u': | |
92 int n = 0; | |
93 for( int i=0; i<4; i++ ) { | |
94 int d; | |
95 if( parser.inCharRange('0','9') ) { | |
96 d = parser.lastChar() - '0'; | |
97 } else if( parser.inCharRange('a','f') ) { | |
98 d = parser.lastChar() - 'a' + 10; | |
99 } else if( parser.inCharRange('A','F') ) { | |
100 d = parser.lastChar() - 'A' + 10; | |
101 } else { | |
102 throw exception("invalid hex digit"); | |
103 } | |
104 n = 16*n + d; | |
105 } | |
106 sb.append((char)n); | |
107 continue; | |
108 } | |
109 } | |
110 throw exception("invalid escape char"); | |
111 default: | |
112 sb.append(c); | |
113 } | |
114 } | |
115 parser.failure(); | |
116 throw exception("unclosed string"); | |
117 } | |
118 | |
119 private Number number() { | |
120 int start = parser.begin(); | |
121 parser.match('-'); | |
122 if( !parser.match('0') ) { | |
123 if( !parser.inCharRange('1','9') ) | |
124 return parser.failure(null); | |
125 while( parser.inCharRange('0','9') ); | |
126 } | |
127 if( parser.match('.') ) { | |
128 if( !parser.inCharRange('0','9') ) | |
129 return parser.failure(null); | |
130 while( parser.inCharRange('0','9') ); | |
131 } | |
132 if( parser.anyOf("eE") ) { | |
133 parser.anyOf("+-"); | |
134 if( !parser.inCharRange('0','9') ) | |
135 return parser.failure(null); | |
136 while( parser.inCharRange('0','9') ); | |
137 } | |
138 String s = parser.textFrom(start); | |
139 return parser.success(Double.valueOf(s)); | |
140 } | |
141 | |
142 private LuanTable array() throws ParseException { | |
143 parser.begin(); | |
144 if( !parser.match('[') ) | |
145 return parser.failure(null); | |
146 spaces(); | |
147 if( parser.match(']') ) | |
148 return parser.success(new LuanTable()); | |
149 List list = new ArrayList(); | |
150 list.add( value() ); | |
151 spaces(); | |
152 while( parser.match(',') ) { | |
153 spaces(); | |
154 list.add( value() ); | |
155 spaces(); | |
156 } | |
157 if( parser.match(']') ) | |
158 return parser.success(new LuanTable(list)); | |
159 if( parser.endOfInput() ) { | |
160 parser.failure(); | |
161 throw exception("unclosed array"); | |
162 } | |
163 throw exception("unexpected text in array"); | |
164 } | |
165 | |
166 private LuanTable object() throws ParseException { | |
167 parser.begin(); | |
168 if( !parser.match('{') ) | |
169 return parser.failure(null); | |
170 spaces(); | |
171 if( parser.match('}') ) | |
172 return parser.success(new LuanTable()); | |
173 Map map = new LinkedHashMap(); | |
174 addEntry(map); | |
175 while( parser.match(',') ) { | |
176 spaces(); | |
177 addEntry(map); | |
178 } | |
179 if( parser.match('}') ) | |
180 return parser.success(new LuanTable(map)); | |
181 if( parser.endOfInput() ) { | |
182 parser.failure(); | |
183 throw exception("unclosed object"); | |
184 } | |
185 throw exception("unexpected text in object"); | |
186 } | |
187 | |
188 private void addEntry(Map map) throws ParseException { | |
189 String key = string(); | |
190 if( key==null ) | |
191 throw exception("invalid object key"); | |
192 spaces(); | |
193 if( !parser.match(':') ) | |
194 throw exception("':' expected"); | |
195 spaces(); | |
196 Object value = value(); | |
197 spaces(); | |
198 map.put(key,value); | |
199 } | |
200 | |
201 private void spaces() { | |
202 while( parser.anyOf(" \t\r\n") ); | |
203 } | |
204 } |