1 package acl2s.lib.parse.obj;
2
3 import java.math.BigInteger;
4
5 import acl2s.lib.parse.ObjectNotSupported;
6 import acl2s.lib.parse.IParseContext;
7 import acl2s.lib.parse.ParseException;
8 import acl2s.lib.parse.Parser;
9 import acl2s.lib.parse.RealFns;
10 import acl2s.lib.parse.StringParser;
11 import acl2s.lib.parse.SymOrNumFns;
12
13 public abstract class IntOrRat extends Num {
14 /**
15 *
16 */
17 private static final long serialVersionUID = -8985321361858651937L;
18
19 public abstract double doubleApprox();
20
21 public abstract IntOrRat invert();
22
23 public abstract IntOrRat negate();
24
25 public abstract boolean lessThan(IntOrRat r);
26
27 public static IntOrRat parse(Parser p) throws ParseException {
28 IParseContext pc = p.getContext();
29 int radix = pc == null ? 10 : pc.getReadBase();
30 boolean inMacro = false;
31
32 char c = p.peek();
33 if (c == '#') {
34 p.skip();
35 c = p.pop();
36 switch (c) {
37 case '0':
38 case '1':
39 case '2':
40 case '3':
41 case '4':
42 case '5':
43 case '6':
44 case '7':
45 case '8':
46 case '9':
47 char d = p.pop();
48 if (Character.isDigit(d)) {
49 char e = p.pop();
50 if (e != 'r' && e != 'R') {
51 throw new ParseException("Expected 1-2 digit radix followed by R, e.g. #16R42.");
52 }
53 radix = 10 * (c - '0') + (d - '0');
54 } else {
55 if (d != 'r' && d != 'R') {
56 throw new ParseException("Expected 1-2 digit radix followed by R, e.g. #16R42.");
57 }
58 radix = (c - '0');
59 }
60 if (radix > 36 || radix < 2) {
61 throw new ParseException("Radix must be from 2 to 36, e.g. #16R42.");
62 }
63 inMacro = true;
64 break;
65 case 'b':
66 case 'B':
67 radix = 2;
68 inMacro = true;
69 break;
70 case 'o':
71 case 'O':
72 radix = 8;
73 inMacro = true;
74 break;
75 case 'x':
76 case 'X':
77 radix = 16;
78 inMacro = true;
79 break;
80 case 'r':
81 case 'R':
82 throw new ParseException("Expected 1-2 digit radix before the R, e.g. #16R42.");
83 default:
84 throw new ParseException("Unrecognized #-sequence for number.");
85 }
86 }
87 return parseNonMacroPart(p, radix, inMacro);
88 }
89
90 public static IntOrRat parseNonMacroPart(Parser p, int radix, boolean inMacro) throws ParseException {
91 boolean negative = false;
92 boolean isFloat = false;
93 Object saved = p.save();
94 char c = p.pop();
95 try {
96 if (c == '-') {
97 negative = true;
98 c = p.pop();
99 } else if (c == '+') {
100 c = p.pop();
101 }
102 BigInteger v1 = BigInteger.ZERO;
103 BigInteger v2 = BigInteger.ZERO;
104 int digit;
105 if (c != '.') {
106 digit = RealFns.getDigit(c);
107 if (digit < 0) {
108 throw new ParseException("At least one digit needed in number.");
109 }
110 if (digit >= radix) {
111 throw new ParseException("Invalid digit for radix " + radix + " number.");
112 }
113 v1 = BigInteger.valueOf(digit);
114 c = p.peek();
115 digit = RealFns.getDigit(c);
116 while (digit >= 0) {
117 if (digit >= radix) {
118 throw new ParseException("Invalid digit for radix " + radix + " number.");
119 }
120 v1 = v1.multiply(BigInteger.valueOf(radix)).add(BigInteger.valueOf(digit));
121 c = p.next();
122 digit = RealFns.getDigit(c);
123 }
124 if (negative) {
125 v1 = v1.negate();
126 }
127 if (SymOrNumFns.isTerminator(c)) {
128 return Int.create(v1);
129 }
130 if (c == '.' && SymOrNumFns.isTerminator(p.peek(1))) {
131 if (radix != 10) {
132 p.restore(saved);
133 return parseNonMacroPart(p, 10, inMacro);
134 }
135 p.skip();
136 return Int.create(v1);
137 }
138 if (RealFns.tryFloatExpt(p)) {
139 RealFns.floatThrow();
140 }
141 if (c == '.') {
142 isFloat = true;
143 } else if (c != '/') {
144 throw new ParseException("Invalid character in number.");
145 }
146 c = p.next();
147 } else { // c == '.' and no preceding digits
148 isFloat = true;
149 c = p.peek();
150 digit = RealFns.getDigit(c);
151 if (digit < 0) {
152 throw new ParseException("Invalid floating-point number.");
153 }
154 }
155 digit = RealFns.getDigit(c);
156 while (digit >= 0) {
157 if (digit >= radix) {
158 if (isFloat) {
159 break;
160 } else {
161 throw new ParseException("Invalid digit for radix " + radix + " number.");
162 }
163 }
164 v2 = v2.multiply(BigInteger.valueOf(radix)).add(BigInteger.valueOf(digit));
165 c = p.next();
166 digit = RealFns.getDigit(c);
167 }
168 if (isFloat) {
169 /*(void)*/RealFns.tryFloatExpt(p);
170 c = p.peek();
171 if (SymOrNumFns.isTerminator(c)) {
172 if (inMacro) {
173 throw new ParseException("Non-rational in radix macro.");
174 } else {
175 RealFns.floatThrow(); // terminal
176 }
177 }
178 } else if (SymOrNumFns.isTerminator(c)) {
179 return Rat.create(v1,v2);
180 }
181 throw new ParseException("Invalid character in number.");
182 } catch (ObjectNotSupported ons) {
183 throw ons;
184 } catch (ParseException pe) {
185 p.restore(saved);
186 // also, as side effect below, skips to terminator
187 if (RealFns.isPotentialNumber(p,radix) && !inMacro) {
188 throw new ObjectNotSupported("Potential numbers (CLTL 22.1.2) are reserved. Use || to make a symbol.");
189 } else {
190 throw pe;
191 }
192 }
193 }
194
195 public static void main(String[] args) throws ParseException {
196 for (String s : args) {
197 System.out.println(parse(new StringParser(null, s)));
198 }
199 }
200 }