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(i64),
232 Real(f64),
233
234 Name(NameId),
236
237 String {
239 entity: EntityId,
240 start: u32,
241 len: u32,
242 },
243 Array {
244 entity: EntityId,
245 start: u32,
246 len: u32,
247 },
248 PackedArray {
249 entity: EntityId,
250 start: u32,
251 len: u32,
252 },
253 Dict(EntityId),
254
255 Operator(OpCode),
257
258 File(EntityId),
260 Save(SaveLevel),
261 FontID(i32),
262 Gstate(u32),
264
265 Stopped,
267 Loop(EntityId),
268 HardReturn,
269 DictEnd(EntityId),
274
275 ExecArray {
278 entity: EntityId,
279 start: u32,
280 len: u32,
281 pos: u32,
282 },
283}
284
285#[derive(Clone, Copy, Debug, PartialEq)]
290pub struct PsObject {
291 pub value: PsValue,
292 pub flags: ObjFlags,
293}
294
295impl PsObject {
296 pub fn int(v: impl Into<i64>) -> Self {
303 let v: i64 = v.into();
304 Self {
305 value: PsValue::Int(v),
306 flags: ObjFlags::literal(),
307 }
308 }
309
310 pub fn real(v: f64) -> Self {
311 Self {
312 value: PsValue::Real(v),
313 flags: ObjFlags::literal(),
314 }
315 }
316
317 pub fn bool(v: bool) -> Self {
318 Self {
319 value: PsValue::Bool(v),
320 flags: ObjFlags::literal(),
321 }
322 }
323
324 pub fn null() -> Self {
325 Self {
326 value: PsValue::Null,
327 flags: ObjFlags::literal(),
328 }
329 }
330
331 pub fn mark() -> Self {
332 Self {
333 value: PsValue::Mark,
334 flags: ObjFlags::literal(),
335 }
336 }
337
338 pub fn dict_mark() -> Self {
340 Self {
341 value: PsValue::DictMark,
342 flags: ObjFlags::literal(),
343 }
344 }
345
346 pub fn name_lit(id: NameId) -> Self {
348 Self {
349 value: PsValue::Name(id),
350 flags: ObjFlags::literal(),
351 }
352 }
353
354 pub fn name_exec(id: NameId) -> Self {
356 Self {
357 value: PsValue::Name(id),
358 flags: ObjFlags::executable(),
359 }
360 }
361
362 pub fn operator(op: OpCode) -> Self {
363 Self {
364 value: PsValue::Operator(op),
365 flags: ObjFlags::executable(),
366 }
367 }
368
369 pub fn string(entity: EntityId, len: u32) -> Self {
371 Self {
372 value: PsValue::String {
373 entity,
374 start: 0,
375 len,
376 },
377 flags: ObjFlags::literal_composite(),
378 }
379 }
380
381 pub fn array(entity: EntityId, len: u32) -> Self {
383 Self {
384 value: PsValue::Array {
385 entity,
386 start: 0,
387 len,
388 },
389 flags: ObjFlags::literal_composite(),
390 }
391 }
392
393 pub fn procedure(entity: EntityId, len: u32) -> Self {
395 Self {
396 value: PsValue::Array {
397 entity,
398 start: 0,
399 len,
400 },
401 flags: ObjFlags::executable_composite(),
402 }
403 }
404
405 pub fn dict(entity: EntityId) -> Self {
407 Self {
408 value: PsValue::Dict(entity),
409 flags: ObjFlags::literal_composite(),
410 }
411 }
412
413 pub fn stopped_mark() -> Self {
415 Self {
416 value: PsValue::Stopped,
417 flags: ObjFlags::executable(),
418 }
419 }
420
421 pub fn loop_mark(entity: EntityId) -> Self {
423 Self {
424 value: PsValue::Loop(entity),
425 flags: ObjFlags::executable(),
426 }
427 }
428
429 pub fn hard_return() -> Self {
431 Self {
432 value: PsValue::HardReturn,
433 flags: ObjFlags::executable(),
434 }
435 }
436
437 pub fn dict_end(entity: EntityId) -> Self {
439 Self {
440 value: PsValue::DictEnd(entity),
441 flags: ObjFlags::executable(),
442 }
443 }
444
445 pub fn is_numeric(&self) -> bool {
448 matches!(self.value, PsValue::Int(_) | PsValue::Real(_))
449 }
450
451 pub fn is_int(&self) -> bool {
452 matches!(self.value, PsValue::Int(_))
453 }
454
455 pub fn is_real(&self) -> bool {
456 matches!(self.value, PsValue::Real(_))
457 }
458
459 pub fn is_bool(&self) -> bool {
460 matches!(self.value, PsValue::Bool(_))
461 }
462
463 pub fn is_array_type(&self) -> bool {
464 matches!(
465 self.value,
466 PsValue::Array { .. } | PsValue::PackedArray { .. }
467 )
468 }
469
470 pub fn is_composite(&self) -> bool {
471 self.flags.is_composite()
472 }
473
474 pub fn is_global_vm(&self) -> bool {
477 match self.value {
478 PsValue::Dict(e) => e.is_global(),
479 PsValue::Array { entity, .. } | PsValue::PackedArray { entity, .. } => {
480 entity.is_global()
481 }
482 PsValue::String { entity, .. } => entity.is_global(),
483 _ => self.flags.is_global(),
484 }
485 }
486
487 pub fn type_name(&self) -> &'static [u8] {
489 match self.value {
490 PsValue::Int(_) => b"integertype",
491 PsValue::Real(_) => b"realtype",
492 PsValue::Bool(_) => b"booleantype",
493 PsValue::Null => b"nulltype",
494 PsValue::Mark | PsValue::DictMark => b"marktype",
495 PsValue::Name(_) => b"nametype",
496 PsValue::String { .. } => b"stringtype",
497 PsValue::Array { .. } => b"arraytype",
498 PsValue::PackedArray { .. } => b"packedarraytype",
499 PsValue::Dict(_) => b"dicttype",
500 PsValue::Operator(_) => b"operatortype",
501 PsValue::File(_) => b"filetype",
502 PsValue::Save(_) => b"savetype",
503 PsValue::FontID(_) => b"fonttype",
504 PsValue::Gstate(_) => b"gstatetype",
505 _ => b"nulltype", }
507 }
508
509 pub fn as_f64(&self) -> Option<f64> {
513 match self.value {
514 PsValue::Int(v) => Some(v as f64),
515 PsValue::Real(v) => Some(v),
516 _ => None,
517 }
518 }
519
520 pub fn as_i32(&self) -> Option<i32> {
528 match self.value {
529 PsValue::Int(v) => i32::try_from(v).ok(),
530 _ => None,
531 }
532 }
533
534 pub fn as_i64(&self) -> Option<i64> {
536 match self.value {
537 PsValue::Int(v) => Some(v),
538 _ => None,
539 }
540 }
541}
542
543#[cfg(test)]
544mod tests {
545 use super::*;
546
547 #[test]
548 fn test_obj_flags_basic() {
549 let f = ObjFlags::new(ObjFlags::ACCESS_UNLIMITED, true, false, true);
550 assert_eq!(f.access(), ObjFlags::ACCESS_UNLIMITED);
551 assert!(f.is_executable());
552 assert!(!f.is_global());
553 assert!(f.is_composite());
554 }
555
556 #[test]
557 fn test_obj_flags_set_literal() {
558 let mut f = ObjFlags::executable();
559 assert!(f.is_executable());
560 f.set_literal();
561 assert!(f.is_literal());
562 }
563
564 #[test]
565 fn test_ps_object_int() {
566 let obj = PsObject::int(42);
567 assert!(obj.is_int());
568 assert!(obj.is_numeric());
569 assert!(!obj.is_real());
570 assert_eq!(obj.as_i32(), Some(42));
571 assert_eq!(obj.as_f64(), Some(42.0));
572 assert_eq!(obj.type_name(), b"integertype");
573 }
574
575 #[test]
576 fn test_ps_object_real() {
577 let obj = PsObject::real(2.5);
578 assert!(obj.is_real());
579 assert!(obj.is_numeric());
580 assert_eq!(obj.as_f64(), Some(2.5));
581 assert_eq!(obj.as_i32(), None);
582 assert_eq!(obj.type_name(), b"realtype");
583 }
584
585 #[test]
586 fn test_ps_object_copy_semantics() {
587 let a = PsObject::int(10);
588 let b = a; assert_eq!(a.as_i32(), Some(10));
590 assert_eq!(b.as_i32(), Some(10));
591 }
592
593 #[test]
594 fn test_ps_object_procedure() {
595 let obj = PsObject::procedure(EntityId(0), 3);
596 assert!(obj.flags.is_executable());
597 assert!(obj.flags.is_composite());
598 assert!(obj.is_array_type());
599 }
600}