├── .gitignore ├── README.md └── src └── com └── ll └── JParsec ├── lib ├── AtomOperator.java ├── CombinatorOperator.java ├── Global.java ├── Handler.java ├── Monand.java ├── Parser.java ├── State.java ├── TextOperator.java └── TextState.java └── test ├── AtomOperatorTest.java ├── CombinatorOperatorTest.java ├── TestUtil.java ├── TextOperatorTest.java └── TextStateTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | *.class 4 | # Mobile Tools for Java (J2ME) 5 | .mtj.tmp/ 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 11 | hs_err_pid* 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JParsec 2 | JParsec is a lightweight stream parse library from Haskell language. 3 | the stream include the text , the stream from java.io. and other streamlike data. 4 | you just need to implement the State interface and call the parse method then pass your data, 5 | then the Operator will serve for you. I have support a default implementation of the State , the most frequently-used Text State. 6 | 7 | I hava write very exhaustive notation to indicate the usage of the operators.And the test package includes almost every operator's test case . if you don't know how to use it , refer to the test cases. 8 | 9 | As an entire example for the JParsec , the [JPJson project](https://github.com/androidfans/JPJson) will give supporting for you. 10 | 11 | This library is a member of the series of parsecs in [Dwarftisan](https://github.com/Dwarfartisan). 12 | 13 | # JParsec 14 | JParsec 是一个来自于Haskell语言的轻量级的**流**解析库. 15 | 这里的**流**包括文本,java.io中的流以及其他的流式的数据.你只需要实现State接口并且调用parse方法传入你构造的State数据.然后你所构造的各种算子就会开始为你服务了.JParsec库中已经有了一个默认的State实现,用于描述最广泛需要使用到解析功能的TextState. 16 | 17 | 我为每个算子都编写了详尽的注释以指导你算子的使用方法.并且代码中的Test包中包含几乎每个算子的测试用例.如果你看了注释还是不知道怎么使用这些算子,你可以参考这些测试用例. 18 | 19 | 并且[JPJson 项目](https://github.com/androidfans/JPJson)作为一个使用JParsec实现的json解析库,也将为您演示JParsec的一次在稍大的项目中的完整使用. 20 | 21 | 本库也是[Dwarftisan 组织](https://github.com/Dwarfartisan)中Parsec系列的Java版本实现. 22 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/AtomOperator.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | 4 | /** 5 | * Created by liuli on 15-12-3. 6 | */ 7 | public class AtomOperator { 8 | 9 | /** 10 | * equal operator expect value equals the parameter. 11 | * @param chr the given value 12 | * @return the Parser 13 | */ 14 | public static Parser equal(char chr) { 15 | class EqualParser extends Parser { 16 | @Override 17 | public Character parse(State state){ 18 | char data = state.next(); 19 | if (data == chr) { 20 | return data; 21 | } 22 | throw new RuntimeException("expect a value equal " + chr + " but get" + data); 23 | } 24 | } 25 | return new EqualParser(); 26 | } 27 | 28 | /** 29 | * notEqual operator expect value differentiates from the parameter 30 | * @param chr the given value 31 | * @return the Parser 32 | */ 33 | public static Parser notEqual(char chr) { 34 | class NotEqualParser extends Parser{ 35 | @Override 36 | public Character parse(State state) { 37 | char data = state.next(); 38 | if (data != chr) { 39 | return data; 40 | } 41 | throw new RuntimeException("expect a value not equal" + chr + " but get" + data); 42 | } 43 | } 44 | return new NotEqualParser(); 45 | } 46 | 47 | /** 48 | * oneOf operator expect value is a member of parameters 49 | * @param chars several value may include the state's next value 50 | * @return the Parser 51 | */ 52 | public static Parser oneOf(char...chars){ 53 | class OneOfParser extends Parser{ 54 | @Override 55 | public Character parse(State state){ 56 | char data = state.next(); 57 | for (char c : chars) { 58 | if(data == c) 59 | return data; 60 | } 61 | throw new RuntimeException("expect one of" + chars); 62 | } 63 | } 64 | return new OneOfParser(); 65 | } 66 | 67 | /** 68 | * noneOf operator expect value isn't a member of parameters 69 | * @param chars several value may not include the state's next value 70 | * @return the Parser 71 | */ 72 | public static Parser noneOf(char... chars) { 73 | class NoneOfParser extends Parser{ 74 | 75 | @Override 76 | public Character parse(State state){ 77 | char data = state.next(); 78 | for (char c : chars) { 79 | if (data == c) { 80 | throw new RuntimeException("expect none of" + chars ); 81 | } 82 | } 83 | return data; 84 | } 85 | } 86 | return new NoneOfParser(); 87 | } 88 | 89 | /** 90 | * the Return operator always success and return the given value 91 | * @param val a value to return 92 | * @return the Parser 93 | */ 94 | public static Parser Return(Object val) { 95 | class ReturnParser extends Parser { 96 | 97 | @Override 98 | public Object parse(State state) { 99 | return val; 100 | } 101 | } 102 | return new ReturnParser(); 103 | } 104 | 105 | /** 106 | * TODO : should add a exception hierarchy make the state can inform the operator what the exception is 107 | * EOF will success when the state get the stream's endpoint.under other circumstance , it always failed 108 | * @return the Parser 109 | */ 110 | public static Parser EOF() { 111 | class EOFParser extends Parser{ 112 | 113 | @Override 114 | public Object parse(State state) { 115 | char data = 0; 116 | try { 117 | data = state.next(); 118 | } catch (RuntimeException e) { 119 | return null; 120 | } 121 | throw new RuntimeException("expect eof but" + data); 122 | } 123 | } 124 | return new EOFParser(); 125 | } 126 | 127 | /** 128 | * the fail operator always failed and you can pass the message to indicate the reason of the fail 129 | * @param message the error message 130 | * @return the Parser 131 | */ 132 | public static Parser fail(String message) { 133 | class FailParser extends Parser{ 134 | 135 | @Override 136 | public Object parse(State state) { 137 | throw new RuntimeException(message); 138 | } 139 | } 140 | return new FailParser(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/CombinatorOperator.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | 6 | /** 7 | * Created by liuli on 15-12-3. 8 | */ 9 | public class CombinatorOperator { 10 | /** 11 | * the Try operator accept a Parser and execute it . if the Parser failed , Try operator will rollback it to the correct position 12 | * but the Try operator doesn't catch some exception , it will throw the exception from the given Parser 13 | * @param parser Parser to try 14 | * @return the after-try parser 15 | */ 16 | public static Parser Try(Parser parser) { 17 | class TryParser extends Parser { 18 | 19 | @Override 20 | public Object parse(State state) { 21 | Integer tran = state.begin(); 22 | Object data = null; 23 | try { 24 | data = parser.parse(state); 25 | } catch (RuntimeException e) { 26 | state.rollBack(tran); 27 | throw e; 28 | } 29 | state.commit(tran); 30 | return data; 31 | } 32 | } 33 | return new TryParser(); 34 | } 35 | 36 | /** 37 | * the between operator will execute the open operator then the close operator and then the psc operator,and return the result from close operator 38 | * @param open the open Parser 39 | * @param close the close Parser 40 | * @param psc the psc Parser 41 | * @return the between Parser 42 | */ 43 | public static Parser between(Parser open, Parser close, Parser psc) { 44 | class BetweenParser extends Parser { 45 | 46 | @Override 47 | public Object parse(State state) { 48 | open.parse(state); 49 | Object data = close.parse(state); 50 | psc.parse(state); 51 | return data; 52 | } 53 | } 54 | return new BetweenParser(); 55 | } 56 | 57 | /** 58 | * the many operator match zero to many times using the given parser 59 | * the many operator encapsulate the try operator 60 | * and it has catch the exception internal , so you have no necessary to handle some exception 61 | * @param parser the parser to match many times 62 | * @return the many Parser 63 | */ 64 | public static Parser many(Parser parser) { 65 | class ManyParser extends Parser { 66 | 67 | @Override 68 | public ArrayList parse(State state) { 69 | ArrayList re = new ArrayList<>(); 70 | Parser psc = Try(parser); 71 | Object obj = null; 72 | for (;;) { 73 | try { 74 | obj = psc.parse(state); 75 | } catch (RuntimeException e) { 76 | break; 77 | } 78 | re.add(obj); 79 | } 80 | return re; 81 | } 82 | }; 83 | return new ManyParser(); 84 | } 85 | 86 | /** 87 | * many1 operator likes many, but many1 will match at least one times. 88 | * if the first time not matched , it will throw the exception , and the first operator will not rollback 89 | * @param parser the Parser to match many1 times 90 | * @return the many1 Parser 91 | */ 92 | public static Parser many1(Parser parser) { 93 | class Many1Parser extends Parser{ 94 | 95 | @Override 96 | public ArrayList parse(State state) { 97 | Object r = parser.parse(state); 98 | ArrayList re = new ArrayList<>(); 99 | re.add(r); 100 | Parser psc = Try(parser); 101 | Object obj = null; 102 | for (;;) { 103 | try { 104 | obj = psc.parse(state); 105 | } catch (RuntimeException e) { 106 | break; 107 | } 108 | re.add(obj); 109 | } 110 | return re; 111 | } 112 | } 113 | return new Many1Parser(); 114 | } 115 | 116 | /** 117 | * the choice operator accept several parsers , and it will try them singly 118 | * if someone feasible,it will return the result from it. 119 | * and it will throw exception when all the parsers failed or a parser doesn't rollback after execution 120 | * it means you should try every operator unless the last one by yourself 121 | * @param parsers parsers to choice 122 | * @return the choice Parser 123 | */ 124 | public static Parser choice(Parser... parsers) { 125 | class ChoiceParser extends Parser{ 126 | 127 | @Override 128 | public Object parse(State state) { 129 | Object re = null; 130 | for (Parser parser : parsers) { 131 | int index = state.pos(); 132 | try { 133 | re = parser.parse(state); 134 | } catch (RuntimeException e) { 135 | if (state.pos() != index) { 136 | throw e; 137 | } 138 | continue; 139 | } 140 | return re; 141 | } 142 | throw new RuntimeException(); 143 | } 144 | } 145 | return new ChoiceParser(); 146 | } 147 | 148 | /** 149 | * sepBy operator match one to many times parser with separator 150 | * @param parser the parser 151 | * @param separator the separator 152 | * @return the parser 153 | */ 154 | public static Parser sepBy1(Parser parser, Parser separator) { 155 | class SepBy1Parser extends Parser { 156 | 157 | @Override 158 | public Object parse(State state) { 159 | Parser par = parser.bind(new HandlerAdapter() { 160 | @Override 161 | public Object bindHandle(Object value, State state) { 162 | ArrayList arrayList = new ArrayList(); 163 | arrayList.add(value); 164 | arrayList.addAll((Collection) many(separator.then(parser)).parse(state)); 165 | return arrayList; 166 | } 167 | }); 168 | return par.parse(state); 169 | } 170 | } 171 | return new SepBy1Parser(); 172 | } 173 | 174 | /** 175 | * sepBy operator match zero to many times parser with separator 176 | * and the parser will return all the parser's value and drop the separator's value 177 | * @param parser 178 | * @param separator 179 | * @return the parser 180 | */ 181 | public static Parser sepBy(Parser parser, Parser separator) { 182 | Parser par = choice(sepBy1(parser, separator), AtomOperator.Return(new ArrayList())); 183 | return par; 184 | } 185 | 186 | /** 187 | * skip1 separator match one to many times but it will not store the value 188 | * the first match must success otherwise it will throw the exception 189 | * and it also will not store the value 190 | * the skip operator catch the exception internal , but it not means that you don't need to wrap a try operator.because catch the exception but will not rollback. 191 | * @param parser the parser to skip one 192 | * @return the parser 193 | */ 194 | public static Parser skip1(Parser parser) { 195 | return parser.bind(new HandlerAdapter(){ 196 | @Override 197 | public Object bindHandle(Object value, State state) { 198 | 199 | for (; ; ) { 200 | try { 201 | parser.parse(state); 202 | } catch (RuntimeException e) { 203 | return null; 204 | } 205 | } 206 | } 207 | }); 208 | } 209 | 210 | /** 211 | * skip separator match zero to many times but it will not store the value 212 | * the skip operator catch the exception internal , but it not means that you don't need to wrap a try operator.because catch the exception but will not rollback. 213 | * @param parser the parser to skip 214 | * @return the Parser 215 | */ 216 | public static Parser skip(Parser parser) { 217 | class SkipParser extends Parser { 218 | 219 | @Override 220 | public Object parse(State state) { 221 | Parser par = choice(skip1(parser), AtomOperator.Return(new ArrayList<>())); 222 | Object re = par.parse(state); 223 | return re; 224 | } 225 | } 226 | return new SkipParser(); 227 | } 228 | 229 | /** 230 | * match many times .if failure occured ,then use the tailParser to Parse. 231 | * And manyTail use Over Monand ,so the tailParser will be abandoned. 232 | * @param parser used to match many times 233 | * @param tailParser used to match the tail 234 | * @return 235 | */ 236 | public static Parser manyTail(Parser parser, Parser tailParser) { 237 | return many(parser).over(tailParser); 238 | } 239 | 240 | /** 241 | * match many times at least one.if failure occured ,then use the tailParser to Parse. 242 | * And manyTail use Over Monand ,so the tailParser will be abandoned. 243 | * @param parser used to match many times at least one 244 | * @param tailParser used to match the tail 245 | * @return 246 | */ 247 | public static Parser many1Tail(Parser parser, Parser tailParser) { 248 | return many1(parser).over(tailParser); 249 | } 250 | 251 | /** 252 | * otherwise operator match the given parser if failed ,it will throw an exception with given description 253 | * @param parser the parser 254 | * @param description the description 255 | * @return the parser 256 | */ 257 | public static Parser otherWise(Parser parser, String description) { 258 | Parser par = choice(parser, AtomOperator.fail(description)); 259 | class OtherWiseParser extends Parser { 260 | 261 | @Override 262 | public Object parse(State state) { 263 | Object re = null; 264 | try { 265 | re = parser.parse(state); 266 | } catch (RuntimeException e) { 267 | throw new RuntimeException(description); 268 | } 269 | return re; 270 | } 271 | } 272 | return new OtherWiseParser(); 273 | } 274 | } -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/Global.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | /** 4 | * Created by liuli on 15-12-6. 5 | */ 6 | public interface Global { 7 | String LINESEPARATOR = System.getProperty("line.separator", "\n"); 8 | } 9 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/Handler.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | /** 4 | * Created by liuli on 15-12-4. 5 | */ 6 | public abstract class Handler { 7 | public abstract Object bindHandle(Object value, State state); 8 | 9 | public abstract Object thenHandle(State state); 10 | 11 | public abstract Object overHandle(State state); 12 | } 13 | 14 | class HandlerAdapter extends Handler { 15 | @Override 16 | public Object bindHandle(Object value, State state) { 17 | return null; 18 | } 19 | 20 | @Override 21 | public Object thenHandle(State state) { 22 | return null; 23 | } 24 | 25 | @Override 26 | public Object overHandle(State state) { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/Monand.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | /** 4 | * Created by liuli on 15-12-3. 5 | */ 6 | public interface Monand { 7 | Parser bind(Handler handler); 8 | 9 | Parser then(Parser parser); 10 | 11 | Parser over(Parser parser); 12 | } 13 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/Parser.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | /** 4 | * Created by liuli on 15-12-3. 5 | */ 6 | public abstract class Parser implements Monand { 7 | public abstract T parse(State state); 8 | 9 | @Override 10 | public Parser bind(Handler handler) { 11 | 12 | class BindParser extends Parser { 13 | 14 | @Override 15 | public Object parse(State state) { 16 | Object val = Parser.this.parse(state); 17 | Object re = handler.bindHandle(val, state); 18 | return re; 19 | } 20 | } 21 | 22 | return new BindParser(); 23 | } 24 | 25 | @Override 26 | public Parser then(Parser parser) { 27 | class ThenParser extends Parser { 28 | 29 | @Override 30 | public Object parse(State state) { 31 | Parser.this.parse(state); 32 | return parser.parse(state); 33 | } 34 | } 35 | return new ThenParser(); 36 | } 37 | 38 | @Override 39 | public Parser over(Parser parser) { 40 | class OverParser extends Parser{ 41 | 42 | @Override 43 | public Object parse(State state) { 44 | Object val = Parser.this.parse(state); 45 | parser.parse(state); 46 | return val; 47 | } 48 | } 49 | return new OverParser(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/State.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | /** 4 | * Created by liuli on 15-12-3. 5 | */ 6 | public interface State { 7 | int begin(); 8 | 9 | void commit(int tran); 10 | 11 | void rollBack(int tran); 12 | 13 | Character next() throws RuntimeException; 14 | 15 | int pos(); 16 | } -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/TextOperator.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by liuli on 15-12-3. 7 | */ 8 | public class TextOperator { 9 | 10 | /** 11 | * Chr operator just like equal , but it is special for character 12 | * @param chr the given character to match 13 | * @return the Parser 14 | */ 15 | public static Parser Chr(char chr) { 16 | return AtomOperator.equal(chr); 17 | } 18 | 19 | /** 20 | * Str operator match a string 21 | * @param string the given string 22 | * @return the parser 23 | */ 24 | public static Parser Str(String string) { 25 | class StrParser extends Parser { 26 | 27 | @Override 28 | public String parse(State state) { 29 | char[] StrArr = string.toCharArray(); 30 | for (char c : StrArr) { 31 | try { 32 | AtomOperator.equal(c).parse(state); 33 | } catch (RuntimeException e) { 34 | throw e; 35 | } 36 | } 37 | return string; 38 | } 39 | } 40 | return new StrParser(); 41 | } 42 | 43 | /** 44 | * charOf operator is a specialized oneOf for character to decide if a char in a string 45 | * @param string the given string 46 | * @return the Parser 47 | */ 48 | public static Parser charOf(String string) { 49 | return AtomOperator.oneOf(string.toCharArray()); 50 | } 51 | 52 | /** 53 | * space operator match a space 54 | * @return the Parser 55 | */ 56 | public static Parser space() { 57 | return Chr(' '); 58 | } 59 | 60 | /** 61 | * newLine operator match a line separator character(string) 62 | * @return the parser 63 | */ 64 | public static Parser newLine() { 65 | return Str(Global.LINESEPARATOR); 66 | } 67 | 68 | /** 69 | * whieteSpace operator match a white space character(string) which include tab space and line separator 70 | * @return 71 | */ 72 | public static Parser whiteSpace() { 73 | return CombinatorOperator.choice(CombinatorOperator.Try(space()), CombinatorOperator.Try(newLine()), Chr('\t')); 74 | } 75 | 76 | /** 77 | * Digit operator match a Digit character 78 | * @return the parser 79 | */ 80 | public static Parser Digit() { 81 | return charOf("0123456789"); 82 | } 83 | 84 | /** 85 | * uInt match an unsigned int value 86 | * @return the parser 87 | */ 88 | public static Parser uInt() { 89 | class UIntParser extends Parser { 90 | 91 | @Override 92 | public String parse(State state) { 93 | Parser many1 = CombinatorOperator.many1(Digit()); 94 | ArrayList arr = (ArrayList) many1.parse(state); 95 | char[] chrs = new char[arr.size()]; 96 | for (int i = 0; i < chrs.length; i++) { 97 | chrs[i] = arr.get(i); 98 | } 99 | return new String(chrs); 100 | } 101 | } 102 | return new UIntParser(); 103 | } 104 | 105 | /** 106 | * Int match an int value 107 | * @return the Parser 108 | */ 109 | public static Parser Int() { 110 | return CombinatorOperator.choice(CombinatorOperator.Try(Chr('-')).then(uInt()).bind(new MinusHandler()), uInt()); 111 | } 112 | 113 | /** 114 | * uFloat match uFloat value.and it also support the .xx format .it returns 0.xx value 115 | * @return the parser 116 | */ 117 | public static Parser uFloat() { 118 | class UFloatParser extends Parser { 119 | @Override 120 | public String parse(State state) { 121 | Parser chrPoint = Chr('.'); 122 | Parser Try = CombinatorOperator.Try(uInt()); 123 | Parser choice = CombinatorOperator.choice(Try.over(chrPoint), chrPoint.then(AtomOperator.Return("0"))); 124 | String left = (String) choice.parse(state); 125 | String right = uInt().parse(state); 126 | return left + "." + right; 127 | } 128 | } 129 | return new UFloatParser(); 130 | } 131 | 132 | /** 133 | * Float match a Float value. also support the .xx format 134 | * @return the parser 135 | */ 136 | public static Parser Float() { 137 | return CombinatorOperator.choice(CombinatorOperator.Try(Chr('-')).then(uFloat()).bind(new MinusHandler()), uFloat()); 138 | } 139 | } 140 | 141 | /** 142 | * MinusHandler to match the negative symbol 143 | */ 144 | class MinusHandler extends HandlerAdapter{ 145 | @Override 146 | public Object bindHandle(Object value, State state) { 147 | return "-" + value; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/lib/TextState.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.lib; 2 | 3 | /** 4 | * Created by liuli on 15-12-3. 5 | */ 6 | public class TextState implements State { 7 | 8 | private char[] state = null; 9 | 10 | private int index = 0; 11 | 12 | private int begin = 0; 13 | 14 | public TextState(String state) { 15 | this.state = state.toCharArray(); 16 | } 17 | 18 | @Override 19 | public int begin() { 20 | if (begin == 0) 21 | begin = pos(); 22 | return pos(); 23 | } 24 | 25 | @Override 26 | public void commit(int tran) { 27 | if(tran == begin) 28 | begin = -1; 29 | } 30 | 31 | @Override 32 | public void rollBack(int tran) { 33 | seekTo(tran); 34 | if (begin == tran) 35 | begin = -1; 36 | } 37 | 38 | public int pos() { 39 | return index; 40 | } 41 | 42 | private void seekTo(int pos) { 43 | if (pos < 0 && pos >= state.length) 44 | throw new RuntimeException("pos out of bounds"); 45 | index = pos; 46 | } 47 | 48 | @Override 49 | public Character next() { 50 | if (index == state.length) 51 | throw new RuntimeException("index out of bounds"); 52 | return state[index++]; 53 | } 54 | } -------------------------------------------------------------------------------- /src/com/ll/JParsec/test/AtomOperatorTest.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.test; 2 | 3 | import com.ll.JParsec.lib.AtomOperator; 4 | import com.ll.JParsec.lib.Parser; 5 | import com.ll.JParsec.lib.State; 6 | import com.ll.JParsec.lib.TextState; 7 | import junit.framework.Assert; 8 | import org.junit.Test; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Created by liuli on 15-12-4. 14 | */ 15 | public class AtomOperatorTest { 16 | 17 | @Test 18 | public void testEqual() throws Exception { 19 | State state = new TextState("01"); 20 | Parser eq = AtomOperator.equal('0'); 21 | assertEquals(eq.parse(state).charValue(), '0'); 22 | 23 | try { 24 | eq.parse(state); 25 | } catch (RuntimeException e) { 26 | return; 27 | } 28 | fail("exception not occur"); 29 | } 30 | 31 | @Test 32 | public void testNotEqual() throws Exception { 33 | State state = new TextState("01"); 34 | Parser neq = AtomOperator.notEqual('1'); 35 | assertEquals(neq.parse(state).charValue(), '0'); 36 | 37 | TestUtil.AssertThrowException(neq, state); 38 | } 39 | 40 | @Test 41 | public void testOneOf() throws Exception { 42 | State state = new TextState("0a"); 43 | Parser oneOf = AtomOperator.oneOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); 44 | assertEquals(oneOf.parse(state).charValue(), '0'); 45 | try { 46 | oneOf.parse(state); 47 | } catch (RuntimeException e) { 48 | return; 49 | } 50 | fail(); 51 | } 52 | 53 | @Test 54 | public void testNoneOf() throws Exception { 55 | State state = new TextState("a0"); 56 | Parser noneOf = AtomOperator.noneOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); 57 | assertEquals(noneOf.parse(state).charValue(), 'a'); 58 | 59 | try { 60 | noneOf.parse(state); 61 | } catch (RuntimeException e) { 62 | return; 63 | } 64 | fail(); 65 | } 66 | 67 | @Test 68 | public void testEOF() throws Exception { 69 | State state = new TextState("01"); 70 | Parser eof = AtomOperator.EOF(); 71 | state.next(); 72 | state.next(); 73 | eof.parse(state); 74 | 75 | state = new TextState("0"); 76 | TestUtil.AssertThrowException(eof, state); 77 | } 78 | } -------------------------------------------------------------------------------- /src/com/ll/JParsec/test/CombinatorOperatorTest.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.test; 2 | 3 | import com.ll.JParsec.lib.*; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | /** 12 | * Created by liuli on 15-12-4. 13 | */ 14 | public class CombinatorOperatorTest { 15 | 16 | @Test 17 | public void testTry() throws Exception { 18 | State state = new TextState("0"); 19 | Parser ne = AtomOperator.notEqual('0'); 20 | Parser Try = CombinatorOperator.Try(ne); 21 | TestUtil.AssertThrowException(Try, state); 22 | } 23 | 24 | @Test 25 | public void testBetween() throws Exception { 26 | State state = new TextState("abd"); 27 | Parser eq1 = AtomOperator.equal('a'); 28 | Parser eq2 = AtomOperator.equal('b'); 29 | Parser eq3 = AtomOperator.equal('d'); 30 | Parser bet = CombinatorOperator.between(eq1, eq2, eq3); 31 | assertEquals(bet.parse(state), 'b'); 32 | assertEquals(3, state.pos()); 33 | } 34 | 35 | @Test 36 | public void testMany() throws Exception { 37 | State state = new TextState("aaaaaaab"); 38 | Parser eq = AtomOperator.equal('a'); 39 | Parser many = CombinatorOperator.many(eq); 40 | ArrayList arr = (ArrayList) many.parse(state); 41 | for (Object c : arr) { 42 | assertEquals('a', ((Character) c).charValue()); 43 | } 44 | assertEquals(7, state.pos()); 45 | } 46 | 47 | @Test 48 | public void testMany1() throws Exception { 49 | State state = new TextState("aaaaaaab"); 50 | Parser eq = AtomOperator.equal('a'); 51 | Parser many1 = CombinatorOperator.many1(eq); 52 | ArrayList arr = (ArrayList) many1.parse(state); 53 | for (Object c : arr) { 54 | assertEquals('a', ((Character) c).charValue()); 55 | } 56 | assertEquals(7, state.pos()); 57 | 58 | state = new TextState("baaaaab"); 59 | TestUtil.AssertThrowException(many1,state); 60 | } 61 | 62 | @Test 63 | public void testChoice() throws Exception { 64 | 65 | State state = new TextState("ab"); 66 | Parser eq1 = AtomOperator.equal('b'); 67 | Parser eq2 = AtomOperator.equal('a'); 68 | Parser Try = CombinatorOperator.Try(eq1); 69 | Parser choice = CombinatorOperator.choice(Try, Try, Try, eq2); 70 | choice.parse(state); 71 | 72 | assertEquals(1, state.pos()); 73 | } 74 | 75 | @Test 76 | public void testSepBy1() throws Exception { 77 | State state = new TextState("a|a|a|a"); 78 | Parser s = AtomOperator.equal('|'); 79 | Parser eq = AtomOperator.equal('a'); 80 | Parser sepBy1 = CombinatorOperator.sepBy1(eq, s); 81 | List list = (List) sepBy1.parse(state); 82 | assertEquals(4, list.size()); 83 | assertEquals(7, state.pos()); 84 | 85 | 86 | state = new TextState("b|a|a|a"); 87 | TestUtil.AssertThrowException(sepBy1, state); 88 | } 89 | 90 | @Test 91 | public void testSepBy() throws Exception { 92 | State state = new TextState("a|a|a|a"); 93 | Parser s = AtomOperator.equal('|'); 94 | Parser eq = AtomOperator.equal('a'); 95 | Parser sepBy = CombinatorOperator.sepBy(eq, s); 96 | List list = (List) sepBy.parse(state); 97 | assertEquals(4, list.size()); 98 | assertEquals(7, state.pos()); 99 | } 100 | 101 | @Test 102 | public void testSkip1() throws Exception { 103 | 104 | State state = new TextState("aaab"); 105 | Parser eq = AtomOperator.equal('a'); 106 | Parser sk1 = CombinatorOperator.skip1(eq); 107 | ArrayList arr = (ArrayList) sk1.parse(state); 108 | assertEquals(null, arr); 109 | 110 | assertEquals(4, state.pos()); 111 | 112 | state = new TextState("baaab"); 113 | TestUtil.AssertThrowException(sk1,state); 114 | } 115 | 116 | @Test 117 | public void testSkip() throws Exception { 118 | State state = new TextState("aaab"); 119 | Parser eq = AtomOperator.equal('a'); 120 | Parser sk = CombinatorOperator.skip(eq); 121 | ArrayList arr = (ArrayList) sk.parse(state); 122 | assertEquals(null, arr); 123 | 124 | assertEquals(4, state.pos()); 125 | } 126 | 127 | @Test 128 | public void testManyTail() throws Exception { 129 | State state = new TextState("aab"); 130 | Parser eq = AtomOperator.equal('a'); 131 | Parser ne = AtomOperator.notEqual('a'); 132 | Parser mat = CombinatorOperator.manyTail(eq, ne); 133 | ArrayList arr = (ArrayList) mat.parse(state); 134 | for (Object c : arr) { 135 | assertEquals('a',((Character)c).charValue()); 136 | } 137 | } 138 | 139 | @Test 140 | public void testMany1Tail() throws Exception { 141 | State state = new TextState("aab"); 142 | Parser eq = AtomOperator.equal('a'); 143 | Parser ne = AtomOperator.notEqual('a'); 144 | Parser ma1t = CombinatorOperator.many1Tail(eq, ne); 145 | ArrayList arr = (ArrayList) ma1t.parse(state); 146 | for (Object c : arr) { 147 | assertEquals('a',((Character)c).charValue()); 148 | } 149 | 150 | state = new TextState("baab"); 151 | TestUtil.AssertThrowException(ma1t, state); 152 | } 153 | 154 | @Test 155 | public void testOtherWise() throws Exception { 156 | State state = new TextState("bd"); 157 | Parser eq = AtomOperator.equal('a'); 158 | Parser ow = CombinatorOperator.otherWise(eq, "the first operator is fail , so bad"); 159 | try { 160 | ow.parse(state); 161 | } catch (RuntimeException e) { 162 | assertEquals("the first operator is fail , so bad", e.getMessage()); 163 | } 164 | 165 | state = new TextState("ab"); 166 | Character c = (Character) ow.parse(state); 167 | assertEquals('a', c.charValue()); 168 | } 169 | } -------------------------------------------------------------------------------- /src/com/ll/JParsec/test/TestUtil.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.test; 2 | 3 | import com.ll.JParsec.lib.Parser; 4 | import com.ll.JParsec.lib.State; 5 | import static org.junit.Assert.*; 6 | /** 7 | * Created by liuli on 15-12-5. 8 | */ 9 | public class TestUtil { 10 | public static void AssertThrowException(Parser parser,State state) { 11 | try { 12 | parser.parse(state); 13 | } catch (RuntimeException e) { 14 | return; 15 | } 16 | fail("it should to throw some exception bu it didn't"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/com/ll/JParsec/test/TextOperatorTest.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.test; 2 | 3 | import com.ll.JParsec.lib.*; 4 | 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | /** 10 | * Created by liuli on 15-12-6. 11 | */ 12 | public class TextOperatorTest { 13 | 14 | @Test 15 | public void testChr() throws Exception { 16 | 17 | State state = new TextState("01"); 18 | Parser chr = TextOperator.Chr('0'); 19 | assertEquals(chr.parse(state).charValue(), '0'); 20 | TestUtil.AssertThrowException(chr, state); 21 | } 22 | 23 | @Test 24 | public void testStr() throws Exception { 25 | State state = new TextState("0123456789"); 26 | Parser str = TextOperator.Str("01234"); 27 | assertEquals("01234", str.parse(state)); 28 | 29 | state = new TextState("012d34"); 30 | TestUtil.AssertThrowException(str, state); 31 | } 32 | 33 | @Test 34 | public void testCharOf() throws Exception { 35 | State state = new TextState("0"); 36 | Parser chrOf = TextOperator.charOf("01234"); 37 | assertEquals('0', chrOf.parse(state)); 38 | 39 | state = new TextState("5"); 40 | TestUtil.AssertThrowException(chrOf, state); 41 | } 42 | 43 | @Test 44 | public void testSpace() throws Exception { 45 | State state = new TextState(" "); 46 | Parser spaceP = TextOperator.space(); 47 | assertEquals(new Character(' '), spaceP.parse(state)); 48 | 49 | state = new TextState("1"); 50 | TestUtil.AssertThrowException(spaceP, state); 51 | 52 | } 53 | 54 | @Test 55 | public void testNewLine() throws Exception { 56 | State state = new TextState(Global.LINESEPARATOR); 57 | Parser sep = TextOperator.whiteSpace(); 58 | assertEquals(Global.LINESEPARATOR, sep.parse(state).toString()); 59 | } 60 | 61 | @Test 62 | public void testWhiteSpace() throws Exception { 63 | State state = new TextState(" " + Global.LINESEPARATOR + "\t"); 64 | Parser whiteSpace = TextOperator.whiteSpace(); 65 | assertEquals(" ", whiteSpace.parse(state).toString()); 66 | assertEquals(Global.LINESEPARATOR, whiteSpace.parse(state).toString()); 67 | assertEquals("\t", whiteSpace.parse(state).toString()); 68 | } 69 | 70 | @Test 71 | public void testDigit() throws Exception { 72 | State state = new TextState("1a"); 73 | Parser digit = TextOperator.Digit(); 74 | assertEquals('1', digit.parse(state)); 75 | 76 | TestUtil.AssertThrowException(digit, state); 77 | } 78 | 79 | @Test 80 | public void testUInt() throws Exception { 81 | State state = new TextState("45212.ag"); 82 | Parser Uint = TextOperator.uInt(); 83 | assertEquals("45212", Uint.parse(state).toString()); 84 | 85 | TestUtil.AssertThrowException(Uint,state); 86 | } 87 | 88 | @Test 89 | public void testInt() throws Exception { 90 | State state = new TextState("45212.ag"); 91 | Parser Int = TextOperator.Int(); 92 | assertEquals("45212", Int.parse(state).toString()); 93 | 94 | state = new TextState("-4562h."); 95 | assertEquals("-4562",Int.parse(state)); 96 | } 97 | 98 | @Test 99 | public void testUFloat() throws Exception { 100 | State state = new TextState("0.23"); 101 | Parser uFloat = TextOperator.uFloat(); 102 | assertEquals("0.23", uFloat.parse(state)); 103 | 104 | state = new TextState(".23"); 105 | assertEquals("0.23", uFloat.parse(state)); 106 | } 107 | 108 | @Test 109 | public void testFloat() throws Exception { 110 | State state = new TextState("0.23"); 111 | Parser Float = TextOperator.Float(); 112 | assertEquals("0.23", Float.parse(state)); 113 | 114 | state = new TextState("-1.23"); 115 | assertEquals("-1.23", Float.parse(state)); 116 | 117 | state = new TextState(".23"); 118 | assertEquals("0.23", Float.parse(state)); 119 | 120 | state = new TextState("-.23"); 121 | assertEquals("-0.23", Float.parse(state)); 122 | } 123 | } -------------------------------------------------------------------------------- /src/com/ll/JParsec/test/TextStateTest.java: -------------------------------------------------------------------------------- 1 | package com.ll.JParsec.test; 2 | 3 | import com.ll.JParsec.lib.State; 4 | import com.ll.JParsec.lib.TextState; 5 | import org.junit.Test; 6 | import static org.junit.Assert.*; 7 | /** 8 | * Created by liuli on 15-12-4. 9 | */ 10 | public class TextStateTest { 11 | 12 | @Test 13 | public void testCommit() throws Exception { 14 | State state = new TextState("0123456789"); 15 | Integer chan = state.begin(); 16 | state.next(); 17 | state.next(); 18 | state.commit(chan); 19 | assertEquals(state.next().charValue(), '2'); 20 | } 21 | 22 | @Test 23 | public void testRollback() throws Exception { 24 | State state = new TextState("0123456789"); 25 | Integer chan = state.begin(); 26 | state.next(); 27 | state.next(); 28 | state.rollBack(chan); 29 | assertEquals(state.next().charValue(), '0'); 30 | } 31 | 32 | @Test 33 | public void testNext() throws Exception { 34 | State state = new TextState("0123456789"); 35 | state.next(); 36 | state.next(); 37 | Character data = state.next(); 38 | assertEquals(data.charValue(), '2'); 39 | } 40 | } --------------------------------------------------------------------------------