1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct ObjFlags(u8);
19
20impl ObjFlags {
21 pub const LITERAL: u8 = 0;
22 pub const EXECUTABLE: u8 = 1 << 3;
23
24 pub const ACCESS_NONE: u8 = 0;
25 pub const ACCESS_EXECUTE_ONLY: u8 = 1;
26 pub const ACCESS_READ_ONLY: u8 = 2;
27 pub const ACCESS_WRITE_ONLY: u8 = 3;
28 pub const ACCESS_UNLIMITED: u8 = 4;
29
30 const ACCESS_MASK: u8 = 0b0000_0111;
31 const EXEC_BIT: u8 = 1 << 3;
32 const GLOBAL_BIT: u8 = 1 << 4;
33 const COMPOSITE_BIT: u8 = 1 << 5;
34 const DEFERRED_BIT: u8 = 1 << 6;
35
36 pub fn new(access: u8, executable: bool, global: bool, composite: bool) -> Self {
38 let mut bits = access & Self::ACCESS_MASK;
39 if executable {
40 bits |= Self::EXEC_BIT;
41 }
42 if global {
43 bits |= Self::GLOBAL_BIT;
44 }
45 if composite {
46 bits |= Self::COMPOSITE_BIT;
47 }
48 Self(bits)
49 }
50
51 pub fn literal() -> Self {
53 Self::new(Self::ACCESS_UNLIMITED, false, false, false)
54 }
55
56 pub fn executable() -> Self {
58 Self::new(Self::ACCESS_UNLIMITED, true, false, false)
59 }
60
61 pub fn literal_composite() -> Self {
63 Self::new(Self::ACCESS_UNLIMITED, false, false, true)
64 }
65
66 pub fn executable_composite() -> Self {
68 Self::new(Self::ACCESS_UNLIMITED, true, false, true)
69 }
70
71 pub fn access(self) -> u8 {
72 self.0 & Self::ACCESS_MASK
73 }
74
75 pub fn is_executable(self) -> bool {
76 self.0 & Self::EXEC_BIT != 0
77 }
78
79 pub fn is_literal(self) -> bool {
80 !self.is_executable()
81 }
82
83 pub fn is_global(self) -> bool {
84 self.0 & Self::GLOBAL_BIT != 0
85 }
86
87 pub fn is_composite(self) -> bool {
88 self.0 & Self::COMPOSITE_BIT != 0
89 }
90
91 pub fn set_executable(&mut self) {
92 self.0 |= Self::EXEC_BIT;
93 }
94
95 pub fn set_literal(&mut self) {
96 self.0 &= !Self::EXEC_BIT;
97 }
98
99 pub fn set_access(&mut self, access: u8) {
100 self.0 = (self.0 & !Self::ACCESS_MASK) | (access & Self::ACCESS_MASK);
101 }
102
103 pub fn is_deferred(self) -> bool {
110 self.0 & Self::DEFERRED_BIT != 0
111 }
112
113 pub fn set_deferred(&mut self) {
115 self.0 |= Self::DEFERRED_BIT;
116 }
117
118 pub fn clear_deferred(&mut self) {
120 self.0 &= !Self::DEFERRED_BIT;
121 }
122
123 #[inline]
125 pub fn require_read(self) -> Result<(), crate::error::PsError> {
126 if self.access() >= Self::ACCESS_READ_ONLY {
127 Ok(())
128 } else {
129 Err(crate::error::PsError::InvalidAccess)
130 }
131 }
132
133 #[inline]
135 pub fn require_write(self) -> Result<(), crate::error::PsError> {
136 if self.access() >= Self::ACCESS_UNLIMITED {
137 Ok(())
138 } else {
139 Err(crate::error::PsError::InvalidAccess)
140 }
141 }
142
143 #[inline]
145 pub fn require_file_write(self) -> Result<(), crate::error::PsError> {
146 if self.access() >= Self::ACCESS_WRITE_ONLY {
147 Ok(())
148 } else {
149 Err(crate::error::PsError::InvalidAccess)
150 }
151 }
152}
153
154#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
156pub struct NameId(pub u32);
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
163pub struct EntityId(pub u32);
164
165impl EntityId {
166 const GLOBAL_BIT: u32 = 1 << 31;
167 const INDEX_MASK: u32 = !(1 << 31);
168
169 pub fn local(index: u32) -> Self {
171 debug_assert!(
172 index & Self::GLOBAL_BIT == 0,
173 "index overflows into tag bit"
174 );
175 EntityId(index)
176 }
177
178 pub fn global(index: u32) -> Self {
180 debug_assert!(
181 index & Self::GLOBAL_BIT == 0,
182 "index overflows into tag bit"
183 );
184 EntityId(index | Self::GLOBAL_BIT)
185 }
186
187 #[inline]
189 pub fn is_global(self) -> bool {
190 self.0 & Self::GLOBAL_BIT != 0
191 }
192
193 #[inline]
195 pub fn raw_index(self) -> usize {
196 (self.0 & Self::INDEX_MASK) as usize
197 }
198}
199
200#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
202pub struct OpCode(pub u16);
203
204#[derive(Clone, Copy, Debug, PartialEq, Eq)]
206pub struct SaveLevel(pub u32);
207
208#[derive(Clone, Copy, Debug, PartialEq)]
210pub enum PsValue {
211 Null,
213 Mark,
214 DictMark,
216 Bool(bool),
217 Int(i32),
218 Real(f64),
219
220 Name(NameId),
222
223 String {
225 entity: EntityId,
226 start: u32,
227 len: u32,
228 },
229 Array {
230 entity: EntityId,
231 start: u32,
232 len: u32,
233 },
234 PackedArray {
235 entity: EntityId,
236 start: u32,
237 len: u32,
238 },
239 Dict(EntityId),
240
241 Operator(OpCode),
243
244 File(EntityId),
246 Save(SaveLevel),
247 FontID(i32),
248 Gstate(u32),
250
251 Stopped,
253 Loop(EntityId),
254 HardReturn,
255 DictEnd(EntityId),
260
261 ExecArray {
264 entity: EntityId,
265 start: u32,
266 len: u32,
267 pos: u32,
268 },
269}
270
271#[derive(Clone, Copy, Debug, PartialEq)]
276pub struct PsObject {
277 pub value: PsValue,
278 pub flags: ObjFlags,
279}
280
281impl PsObject {
282 pub fn int(v: i32) -> Self {
285 Self {
286 value: PsValue::Int(v),
287 flags: ObjFlags::literal(),
288 }
289 }
290
291 pub fn real(v: f64) -> Self {
292 Self {
293 value: PsValue::Real(v),
294 flags: ObjFlags::literal(),
295 }
296 }
297
298 pub fn bool(v: bool) -> Self {
299 Self {
300 value: PsValue::Bool(v),
301 flags: ObjFlags::literal(),
302 }
303 }
304
305 pub fn null() -> Self {
306 Self {
307 value: PsValue::Null,
308 flags: ObjFlags::literal(),
309 }
310 }
311
312 pub fn mark() -> Self {
313 Self {
314 value: PsValue::Mark,
315 flags: ObjFlags::literal(),
316 }
317 }
318
319 pub fn dict_mark() -> Self {
321 Self {
322 value: PsValue::DictMark,
323 flags: ObjFlags::literal(),
324 }
325 }
326
327 pub fn name_lit(id: NameId) -> Self {
329 Self {
330 value: PsValue::Name(id),
331 flags: ObjFlags::literal(),
332 }
333 }
334
335 pub fn name_exec(id: NameId) -> Self {
337 Self {
338 value: PsValue::Name(id),
339 flags: ObjFlags::executable(),
340 }
341 }
342
343 pub fn operator(op: OpCode) -> Self {
344 Self {
345 value: PsValue::Operator(op),
346 flags: ObjFlags::executable(),
347 }
348 }
349
350 pub fn string(entity: EntityId, len: u32) -> Self {
352 Self {
353 value: PsValue::String {
354 entity,
355 start: 0,
356 len,
357 },
358 flags: ObjFlags::literal_composite(),
359 }
360 }
361
362 pub fn array(entity: EntityId, len: u32) -> Self {
364 Self {
365 value: PsValue::Array {
366 entity,
367 start: 0,
368 len,
369 },
370 flags: ObjFlags::literal_composite(),
371 }
372 }
373
374 pub fn procedure(entity: EntityId, len: u32) -> Self {
376 Self {
377 value: PsValue::Array {
378 entity,
379 start: 0,
380 len,
381 },
382 flags: ObjFlags::executable_composite(),
383 }
384 }
385
386 pub fn dict(entity: EntityId) -> Self {
388 Self {
389 value: PsValue::Dict(entity),
390 flags: ObjFlags::literal_composite(),
391 }
392 }
393
394 pub fn stopped_mark() -> Self {
396 Self {
397 value: PsValue::Stopped,
398 flags: ObjFlags::executable(),
399 }
400 }
401
402 pub fn loop_mark(entity: EntityId) -> Self {
404 Self {
405 value: PsValue::Loop(entity),
406 flags: ObjFlags::executable(),
407 }
408 }
409
410 pub fn hard_return() -> Self {
412 Self {
413 value: PsValue::HardReturn,
414 flags: ObjFlags::executable(),
415 }
416 }
417
418 pub fn dict_end(entity: EntityId) -> Self {
420 Self {
421 value: PsValue::DictEnd(entity),
422 flags: ObjFlags::executable(),
423 }
424 }
425
426 pub fn is_numeric(&self) -> bool {
429 matches!(self.value, PsValue::Int(_) | PsValue::Real(_))
430 }
431
432 pub fn is_int(&self) -> bool {
433 matches!(self.value, PsValue::Int(_))
434 }
435
436 pub fn is_real(&self) -> bool {
437 matches!(self.value, PsValue::Real(_))
438 }
439
440 pub fn is_bool(&self) -> bool {
441 matches!(self.value, PsValue::Bool(_))
442 }
443
444 pub fn is_array_type(&self) -> bool {
445 matches!(
446 self.value,
447 PsValue::Array { .. } | PsValue::PackedArray { .. }
448 )
449 }
450
451 pub fn is_composite(&self) -> bool {
452 self.flags.is_composite()
453 }
454
455 pub fn is_global_vm(&self) -> bool {
458 match self.value {
459 PsValue::Dict(e) => e.is_global(),
460 PsValue::Array { entity, .. } | PsValue::PackedArray { entity, .. } => {
461 entity.is_global()
462 }
463 PsValue::String { entity, .. } => entity.is_global(),
464 _ => self.flags.is_global(),
465 }
466 }
467
468 pub fn type_name(&self) -> &'static [u8] {
470 match self.value {
471 PsValue::Int(_) => b"integertype",
472 PsValue::Real(_) => b"realtype",
473 PsValue::Bool(_) => b"booleantype",
474 PsValue::Null => b"nulltype",
475 PsValue::Mark | PsValue::DictMark => b"marktype",
476 PsValue::Name(_) => b"nametype",
477 PsValue::String { .. } => b"stringtype",
478 PsValue::Array { .. } => b"arraytype",
479 PsValue::PackedArray { .. } => b"packedarraytype",
480 PsValue::Dict(_) => b"dicttype",
481 PsValue::Operator(_) => b"operatortype",
482 PsValue::File(_) => b"filetype",
483 PsValue::Save(_) => b"savetype",
484 PsValue::FontID(_) => b"fonttype",
485 PsValue::Gstate(_) => b"gstatetype",
486 _ => b"nulltype", }
488 }
489
490 pub fn as_f64(&self) -> Option<f64> {
494 match self.value {
495 PsValue::Int(v) => Some(v as f64),
496 PsValue::Real(v) => Some(v),
497 _ => None,
498 }
499 }
500
501 pub fn as_i32(&self) -> Option<i32> {
503 match self.value {
504 PsValue::Int(v) => Some(v),
505 _ => None,
506 }
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513
514 #[test]
515 fn test_obj_flags_basic() {
516 let f = ObjFlags::new(ObjFlags::ACCESS_UNLIMITED, true, false, true);
517 assert_eq!(f.access(), ObjFlags::ACCESS_UNLIMITED);
518 assert!(f.is_executable());
519 assert!(!f.is_global());
520 assert!(f.is_composite());
521 }
522
523 #[test]
524 fn test_obj_flags_set_literal() {
525 let mut f = ObjFlags::executable();
526 assert!(f.is_executable());
527 f.set_literal();
528 assert!(f.is_literal());
529 }
530
531 #[test]
532 fn test_ps_object_int() {
533 let obj = PsObject::int(42);
534 assert!(obj.is_int());
535 assert!(obj.is_numeric());
536 assert!(!obj.is_real());
537 assert_eq!(obj.as_i32(), Some(42));
538 assert_eq!(obj.as_f64(), Some(42.0));
539 assert_eq!(obj.type_name(), b"integertype");
540 }
541
542 #[test]
543 fn test_ps_object_real() {
544 let obj = PsObject::real(2.5);
545 assert!(obj.is_real());
546 assert!(obj.is_numeric());
547 assert_eq!(obj.as_f64(), Some(2.5));
548 assert_eq!(obj.as_i32(), None);
549 assert_eq!(obj.type_name(), b"realtype");
550 }
551
552 #[test]
553 fn test_ps_object_copy_semantics() {
554 let a = PsObject::int(10);
555 let b = a; assert_eq!(a.as_i32(), Some(10));
557 assert_eq!(b.as_i32(), Some(10));
558 }
559
560 #[test]
561 fn test_ps_object_procedure() {
562 let obj = PsObject::procedure(EntityId(0), 3);
563 assert!(obj.flags.is_executable());
564 assert!(obj.flags.is_composite());
565 assert!(obj.is_array_type());
566 }
567}