001 /*
002 * Java Genetic Algorithm Library (jenetics-7.1.1).
003 * Copyright (c) 2007-2022 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020 package io.jenetics.prog.op;
021
022 import static java.lang.Character.isDigit;
023 import static java.lang.Character.isJavaIdentifierPart;
024 import static java.lang.Character.isJavaIdentifierStart;
025 import static java.lang.String.format;
026 import static io.jenetics.prog.op.MathTokenType.COMMA;
027 import static io.jenetics.prog.op.MathTokenType.DIV;
028 import static io.jenetics.prog.op.MathTokenType.IDENTIFIER;
029 import static io.jenetics.prog.op.MathTokenType.LPAREN;
030 import static io.jenetics.prog.op.MathTokenType.MINUS;
031 import static io.jenetics.prog.op.MathTokenType.MOD;
032 import static io.jenetics.prog.op.MathTokenType.NUMBER;
033 import static io.jenetics.prog.op.MathTokenType.PLUS;
034 import static io.jenetics.prog.op.MathTokenType.POW;
035 import static io.jenetics.prog.op.MathTokenType.RPAREN;
036 import static io.jenetics.prog.op.MathTokenType.TIMES;
037
038 import io.jenetics.ext.internal.parser.CharSequenceTokenizer;
039 import io.jenetics.ext.internal.parser.ParsingException;
040 import io.jenetics.ext.internal.parser.Token;
041
042 /**
043 * Tokenizer for simple arithmetic expressions.
044 *
045 * <pre>{@code
046 * LPAREN: '(';
047 * RPAREN: ')';
048 * COMMA: ',';
049 * PLUS: '+';
050 * MINUS: '-';
051 * TIMES: '*';
052 * DIV: '/';
053 * POW: '^';
054 * NUMBER: ('0'..'9')+ ('.' ('0'..'9')+)? ((e|E) (+|-)? ('0'..'9'))?
055 * ID: ('a'..'z'|'A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')+;
056 * WS: [ \r\n\t] + -> skip;
057 * }</pre>
058 *
059 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
060 * @since 7.1
061 * @version 7.1
062 */
063 final class MathStringTokenizer extends CharSequenceTokenizer {
064
065 public MathStringTokenizer(final CharSequence input) {
066 super(input);
067 }
068
069 @Override
070 public Token<String> next() {
071 while (isNonEof(c)) {
072 final char value = c;
073
074 switch (value) {
075 case ' ', '\r', '\n', '\t':
076 WS();
077 continue;
078 case '(':
079 consume();
080 return LPAREN.token(value);
081 case ')':
082 consume();
083 return RPAREN.token(value);
084 case ',':
085 consume();
086 return COMMA.token(value);
087 case '+':
088 consume();
089 return PLUS.token(value);
090 case '-':
091 consume();
092 return MINUS.token(value);
093 case '*':
094 if (LA(2) == '*') {
095 consume();
096 consume();
097 return POW.token("**");
098 } else {
099 consume();
100 return TIMES.token(value);
101 }
102 case '/':
103 consume();
104 return DIV.token(value);
105 case '%':
106 consume();
107 return MOD.token(value);
108 case '^':
109 consume();
110 return POW.token(value);
111 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
112 return NUMBER();
113 default:
114 if (isJavaIdentifierStart(c)) {
115 return ID();
116 } else {
117 throw new ParsingException(format(
118 "Got invalid character '%s' at position '%d'.",
119 c, pos
120 ));
121 }
122 }
123 }
124
125 return null;
126 }
127
128 // NUMBER (E SIGN? UNSIGNED_INTEGER)?
129 private Token<String> NUMBER() {
130 final var value = new StringBuilder();
131
132 REAL_NUMBER(value);
133 if ('e' == c || 'E' == c) {
134 value.append(c);
135 consume();
136
137 if ('+' == c || '-' == c) {
138 value.append(c);
139 consume();
140 }
141 if (isDigit(c)) {
142 UNSIGNED_NUMBER(value);
143 }
144 }
145
146 return NUMBER.token(value.toString());
147 }
148
149 // ('0' .. '9') + ('.' ('0' .. '9') +)?
150 private void REAL_NUMBER(final StringBuilder value) {
151 UNSIGNED_NUMBER(value);
152 if ('.' == c) {
153 value.append(c);
154 consume();
155 UNSIGNED_NUMBER(value);
156 }
157 }
158
159 // ('0' .. '9')+
160 private void UNSIGNED_NUMBER(final StringBuilder value) {
161 while (isDigit(c)) {
162 value.append(c);
163 consume();
164 }
165 }
166
167 private Token<String> ID() {
168 final var value = new StringBuilder();
169
170 do {
171 value.append(c);
172 consume();
173 } while (isJavaIdentifierPart(c));
174
175 return IDENTIFIER.token(value.toString());
176 }
177
178 }
|