77//! - `register-code-conversion-map` — stores named conversion maps and returns stable ids
88//! - CCL-backed coding systems and `ccl-execute-on-string` share one bounded
99//! bytecode machine, including resumable register/instruction state.
10+ //! - Each 5-bit opcode decodes to [`command::CclCommand`]. The driver matches
11+ //! that enum exhaustively; commands not yet executed still signal
12+ //! `Error in CCL program`.
1013//! - `ccl-execute` — validates shape and designators while the remaining
1114//! register-only instruction set is implemented incrementally.
1215
16+ mod command;
17+
18+ use self :: command:: CclCommand ;
1319use super :: error:: { EvalResult , Flow , signal} ;
1420use super :: value:: * ;
1521use crate :: emacs_core:: SymId ;
@@ -191,6 +197,31 @@ fn ccl_relative_instruction(instruction: usize, offset: i64) -> Option<usize> {
191197 usize:: try_from ( target) . ok ( )
192198}
193199
200+ /// GNU `CCL_Branch` (`src/ccl.c`). `table_head` is the first jump-table word.
201+ /// `length` table entries are followed by one out-of-range entry. Each entry
202+ /// is a raw relative offset from `table_head`, not a packed command.
203+ fn ccl_branch_target (
204+ words : & [ i64 ] ,
205+ table_head : usize ,
206+ length : i64 ,
207+ selector : i64 ,
208+ error_at : usize ,
209+ ) -> Result < usize , Flow > {
210+ let slot = if ( 0 ..length) . contains ( & selector) {
211+ selector
212+ } else {
213+ length
214+ } ;
215+ let slot = usize:: try_from ( slot) . map_err ( |_| invalid_ccl_program_at ( error_at) ) ?;
216+ let entry = table_head
217+ . checked_add ( slot)
218+ . ok_or_else ( || invalid_ccl_program_at ( error_at) ) ?;
219+ let offset = * words
220+ . get ( entry)
221+ . ok_or_else ( || invalid_ccl_program_at ( error_at) ) ?;
222+ ccl_relative_instruction ( table_head, offset) . ok_or_else ( || invalid_ccl_program_at ( error_at) )
223+ }
224+
194225struct CclExecution {
195226 output : Vec < i64 > ,
196227 registers : [ i64 ; 8 ] ,
@@ -238,7 +269,8 @@ fn execute_compiled_ccl_with_state(
238269 . ok ( )
239270 . filter ( |register| * register < registers. len ( ) )
240271 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
241- let command = code & 0x1f ;
272+ let command = CclCommand :: from_repr ( ( code & 0x1f ) as u8 )
273+ . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
242274
243275 let mut read_character = |destination : & mut i64 | -> Option < bool > {
244276 if let Some ( value) = input. get ( source) {
@@ -254,39 +286,32 @@ fn execute_compiled_ccl_with_state(
254286 } ;
255287
256288 match command {
257- // CCL_SetRegister
258- 0x00 => registers[ register] = registers[ other_register] ,
259- // CCL_SetShortConst
260- 0x01 => registers[ register] = field1,
261- // CCL_SetConst
262- 0x02 => {
289+ CclCommand :: SetRegister => registers[ register] = registers[ other_register] ,
290+ CclCommand :: SetShortConst => registers[ register] = field1,
291+ CclCommand :: SetConst => {
263292 registers[ register] = * words
264293 . get ( instruction)
265294 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
266295 instruction += 1 ;
267296 }
268- // CCL_Jump
269- 0x04 => {
297+ CclCommand :: Jump => {
270298 instruction = ccl_relative_instruction ( instruction, field1)
271299 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
272300 }
273- // CCL_JumpCond
274- 0x05 if registers[ register] == 0 => {
301+ CclCommand :: JumpCond if registers[ register] == 0 => {
275302 instruction = ccl_relative_instruction ( instruction, field1)
276303 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
277304 }
278- 0x05 => { }
279- // CCL_WriteRegisterJump
280- 0x06 => {
305+ CclCommand :: JumpCond => { }
306+ CclCommand :: WriteRegisterJump => {
281307 output. push ( registers[ register] ) ;
282308 instruction = ccl_relative_instruction ( instruction, field1)
283309 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
284310 }
285- // CCL_WriteRegisterReadJump. The compiler stores a paired
286- // CCL_ReadJump word after this fused instruction; GNU skips it
287- // after a successful read, but resumes at that word when input is
288- // exhausted in a non-final block.
289- 0x07 => {
311+ // The compiler stores a paired ReadJump word after this fused
312+ // instruction. GNU skips it after a successful read, but resumes
313+ // at that word when input is exhausted in a non-final block.
314+ CclCommand :: WriteRegisterReadJump => {
290315 output. push ( registers[ register] ) ;
291316 instruction = instruction
292317 . checked_add ( 1 )
@@ -306,8 +331,7 @@ fn execute_compiled_ccl_with_state(
306331 }
307332 }
308333 }
309- // CCL_WriteConstJump
310- 0x08 => {
334+ CclCommand :: WriteConstJump => {
311335 output. push (
312336 * words
313337 . get ( instruction)
@@ -316,8 +340,7 @@ fn execute_compiled_ccl_with_state(
316340 instruction = ccl_relative_instruction ( instruction, field1)
317341 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
318342 }
319- // CCL_ReadJump
320- 0x0c => match read_character ( & mut registers[ register] ) {
343+ CclCommand :: ReadJump => match read_character ( & mut registers[ register] ) {
321344 Some ( true ) => instruction = eof_instruction,
322345 Some ( false ) => {
323346 instruction = ccl_relative_instruction ( instruction, field1)
@@ -331,9 +354,43 @@ fn execute_compiled_ccl_with_state(
331354 } ) ;
332355 }
333356 } ,
334- // CCL_ReadRegister. Consecutive encoded operands read into one or
335- // more registers; a zero field terminates the sequence.
336- 0x0e => {
357+ // `instruction` already points at the jump table. GNU indexes that
358+ // table by the register, or by `field1` when the register is
359+ // outside `0..field1`.
360+ CclCommand :: Branch => {
361+ instruction = ccl_branch_target (
362+ & words,
363+ instruction,
364+ field1,
365+ registers[ register] ,
366+ this_instruction,
367+ ) ?;
368+ }
369+ // GNU reads one character, then falls through into CCL_Branch.
370+ // EOF skips the table and runs the eof program. A suspended read
371+ // resumes on this same word.
372+ CclCommand :: ReadBranch => match read_character ( & mut registers[ register] ) {
373+ Some ( true ) => instruction = eof_instruction,
374+ Some ( false ) => {
375+ instruction = ccl_branch_target (
376+ & words,
377+ instruction,
378+ field1,
379+ registers[ register] ,
380+ this_instruction,
381+ ) ?;
382+ }
383+ None => {
384+ return Ok ( CclExecution {
385+ output,
386+ registers,
387+ instruction : this_instruction,
388+ } ) ;
389+ }
390+ } ,
391+ // Consecutive encoded operands read into one or more registers; a
392+ // zero field terminates the sequence.
393+ CclCommand :: ReadRegister => {
337394 let mut read_field = field1;
338395 let mut read_register = register;
339396 loop {
@@ -365,8 +422,7 @@ fn execute_compiled_ccl_with_state(
365422 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
366423 }
367424 }
368- // CCL_WriteRegister
369- 0x11 => {
425+ CclCommand :: WriteRegister => {
370426 let mut write_field = field1;
371427 let mut write_register = register;
372428 loop {
@@ -385,12 +441,11 @@ fn execute_compiled_ccl_with_state(
385441 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
386442 }
387443 }
388- // CCL_WriteConstString. A zero register field embeds one
389- // character directly in FIELD1. A nonzero field stores an ASCII
390- // string three octets per following word, most-significant octet
391- // first (the representation emitted by GNU `ccl-embed-string`).
392- 0x14 if register == 0 => output. push ( field1) ,
393- 0x14 => {
444+ // A zero register field embeds one character directly in FIELD1.
445+ // A nonzero field stores an ASCII string three octets per following
446+ // word, most-significant octet first (GNU `ccl-embed-string`).
447+ CclCommand :: WriteConstString if register == 0 => output. push ( field1) ,
448+ CclCommand :: WriteConstString => {
394449 let length = usize:: try_from ( field1)
395450 . ok ( )
396451 . ok_or_else ( || invalid_ccl_program_at ( this_instruction) ) ?;
@@ -408,16 +463,34 @@ fn execute_compiled_ccl_with_state(
408463 }
409464 instruction = end;
410465 }
411- // CCL_End. GNU leaves IC pointing at the End instruction so a
412- // completed STATUS cannot accidentally resume beyond the vector.
413- 0x16 => {
466+ // GNU leaves IC pointing at the End instruction so a completed
467+ // STATUS cannot accidentally resume beyond the vector.
468+ CclCommand :: End => {
414469 return Ok ( CclExecution {
415470 output,
416471 registers,
417472 instruction : this_instruction,
418473 } ) ;
419474 }
420- _ => return Err ( invalid_ccl_program_at ( this_instruction) ) ,
475+ CclCommand :: SetArray
476+ | CclCommand :: WriteConstReadJump
477+ | CclCommand :: WriteStringJump
478+ | CclCommand :: WriteArrayReadJump
479+ | CclCommand :: WriteExprConst
480+ | CclCommand :: WriteExprRegister
481+ | CclCommand :: Call
482+ | CclCommand :: WriteArray
483+ | CclCommand :: ExprSelfConst
484+ | CclCommand :: ExprSelfReg
485+ | CclCommand :: SetExprConst
486+ | CclCommand :: SetExprReg
487+ | CclCommand :: JumpCondExprConst
488+ | CclCommand :: JumpCondExprReg
489+ | CclCommand :: ReadJumpCondExprConst
490+ | CclCommand :: ReadJumpCondExprReg
491+ | CclCommand :: Extension => {
492+ return Err ( invalid_ccl_program_at ( this_instruction) ) ;
493+ }
421494 }
422495 }
423496
0 commit comments