From f31767df66b9468cea81a3aceb34633fad213d67 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Fri, 14 Jun 2013 01:32:34 -0700 Subject: [PATCH] Implement terminfo param conditionals Implement the %?, %t, %e, and %; operators. Also implement the %<, %=, %> operators, without which conditionals aren't very useful. Fix the order of parameters for the arithmetic operators. Implement the missing %^ operator. --- src/libextra/terminfo/parm.rs | 147 +++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 18 deletions(-) diff --git a/src/libextra/terminfo/parm.rs b/src/libextra/terminfo/parm.rs index ace6ad3ebb0..59d34c7317b 100644 --- a/src/libextra/terminfo/parm.rs +++ b/src/libextra/terminfo/parm.rs @@ -24,8 +24,10 @@ enum States { CharConstant, CharClose, IntConstant, - IfCond, - IfBody + SeekIfElse(int), + SeekIfElsePercent(int), + SeekIfEnd(int), + SeekIfEndPercent(int) } /// Types of parameters a capability can use @@ -126,44 +128,68 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) } else { return Err(~"stack is empty") }, '+' => if stack.len() > 1 { match (stack.pop(), stack.pop()) { - (Number(x), Number(y)) => stack.push(Number(x + y)), - (_, _) => return Err(~"non-numbers on stack with +") + (Number(y), Number(x)) => stack.push(Number(x + y)), + _ => return Err(~"non-numbers on stack with +") } } else { return Err(~"stack is empty") }, '-' => if stack.len() > 1 { match (stack.pop(), stack.pop()) { - (Number(x), Number(y)) => stack.push(Number(x - y)), - (_, _) => return Err(~"non-numbers on stack with -") + (Number(y), Number(x)) => stack.push(Number(x - y)), + _ => return Err(~"non-numbers on stack with -") } } else { return Err(~"stack is empty") }, '*' => if stack.len() > 1 { match (stack.pop(), stack.pop()) { - (Number(x), Number(y)) => stack.push(Number(x * y)), - (_, _) => return Err(~"non-numbers on stack with *") + (Number(y), Number(x)) => stack.push(Number(x * y)), + _ => return Err(~"non-numbers on stack with *") } } else { return Err(~"stack is empty") }, '/' => if stack.len() > 1 { match (stack.pop(), stack.pop()) { - (Number(x), Number(y)) => stack.push(Number(x / y)), - (_, _) => return Err(~"non-numbers on stack with /") + (Number(y), Number(x)) => stack.push(Number(x / y)), + _ => return Err(~"non-numbers on stack with /") } } else { return Err(~"stack is empty") }, 'm' => if stack.len() > 1 { match (stack.pop(), stack.pop()) { - (Number(x), Number(y)) => stack.push(Number(x % y)), - (_, _) => return Err(~"non-numbers on stack with %") + (Number(y), Number(x)) => stack.push(Number(x % y)), + _ => return Err(~"non-numbers on stack with %") } } else { return Err(~"stack is empty") }, '&' => if stack.len() > 1 { match (stack.pop(), stack.pop()) { - (Number(x), Number(y)) => stack.push(Number(x & y)), - (_, _) => return Err(~"non-numbers on stack with &") + (Number(y), Number(x)) => stack.push(Number(x & y)), + _ => return Err(~"non-numbers on stack with &") } } else { return Err(~"stack is empty") }, '|' => if stack.len() > 1 { match (stack.pop(), stack.pop()) { - (Number(x), Number(y)) => stack.push(Number(x | y)), - (_, _) => return Err(~"non-numbers on stack with |") + (Number(y), Number(x)) => stack.push(Number(x | y)), + _ => return Err(~"non-numbers on stack with |") + } + } else { return Err(~"stack is empty") }, + '^' => if stack.len() > 1 { + match (stack.pop(), stack.pop()) { + (Number(y), Number(x)) => stack.push(Number(x ^ y)), + _ => return Err(~"non-numbers on stack with ^") + } + } else { return Err(~"stack is empty") }, + '=' => if stack.len() > 1 { + match (stack.pop(), stack.pop()) { + (Number(y), Number(x)) => stack.push(Number(if x == y { 1 } else { 0 })), + _ => return Err(~"non-numbers on stack with =") + } + } else { return Err(~"stack is empty") }, + '>' => if stack.len() > 1 { + match (stack.pop(), stack.pop()) { + (Number(y), Number(x)) => stack.push(Number(if x > y { 1 } else { 0 })), + _ => return Err(~"non-numbers on stack with >") + } + } else { return Err(~"stack is empty") }, + '<' => if stack.len() > 1 { + match (stack.pop(), stack.pop()) { + (Number(y), Number(x)) => stack.push(Number(if x < y { 1 } else { 0 })), + _ => return Err(~"non-numbers on stack with <") } } else { return Err(~"stack is empty") }, 'A' => if stack.len() > 1 { @@ -201,7 +227,19 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) }, (_, _) => return Err(~"first two params not numbers with %i") }, - '?' => state = return Err(fmt!("if expressions unimplemented (%?)", cap)), + + // conditionals + '?' => (), + 't' => if stack.len() > 0 { + match stack.pop() { + Number(0) => state = SeekIfElse(0), + Number(_) => (), + _ => return Err(~"non-number on stack with conditional") + } + } else { return Err(~"stack is empty") }, + 'e' => state = SeekIfEnd(0), + ';' => (), + _ => return Err(fmt!("unrecognized format option %c", cur)) } }, @@ -260,7 +298,46 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) old_state = Nothing; } } - _ => return Err(~"unimplemented state") + SeekIfElse(level) => { + if cur == '%' { + state = SeekIfElsePercent(level); + } + old_state = Nothing; + } + SeekIfElsePercent(level) => { + if cur == ';' { + if level == 0 { + state = Nothing; + } else { + state = SeekIfElse(level-1); + } + } else if cur == 'e' && level == 0 { + state = Nothing; + } else if cur == '?' { + state = SeekIfElse(level+1); + } else { + state = SeekIfElse(level); + } + } + SeekIfEnd(level) => { + if cur == '%' { + state = SeekIfEndPercent(level); + } + old_state = Nothing; + } + SeekIfEndPercent(level) => { + if cur == ';' { + if level == 0 { + state = Nothing; + } else { + state = SeekIfEnd(level-1); + } + } else if cur == '?' { + state = SeekIfEnd(level+1); + } else { + state = SeekIfEnd(level); + } + } } if state == old_state { state = Nothing; @@ -316,4 +393,38 @@ mod test { fn test_push_bad_param() { assert!(expand(bytes!("%pa"), [], &mut Variables::new()).is_err()); } + + #[test] + fn test_comparison_ops() { + let v = [('<', [1u8, 0u8, 0u8]), ('=', [0u8, 1u8, 0u8]), ('>', [0u8, 0u8, 1u8])]; + for v.iter().advance |&(op, bs)| { + let s = fmt!("%%{1}%%{2}%%%c%%d", op); + let res = expand(s.as_bytes(), [], &mut Variables::new()); + assert!(res.is_ok(), res.unwrap_err()); + assert_eq!(res.unwrap(), ~['0' as u8 + bs[0]]); + let s = fmt!("%%{1}%%{1}%%%c%%d", op); + let res = expand(s.as_bytes(), [], &mut Variables::new()); + assert!(res.is_ok(), res.unwrap_err()); + assert_eq!(res.unwrap(), ~['0' as u8 + bs[1]]); + let s = fmt!("%%{2}%%{1}%%%c%%d", op); + let res = expand(s.as_bytes(), [], &mut Variables::new()); + assert!(res.is_ok(), res.unwrap_err()); + assert_eq!(res.unwrap(), ~['0' as u8 + bs[2]]); + } + } + + #[test] + fn test_conditionals() { + let mut vars = Variables::new(); + let s = bytes!("\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"); + let res = expand(s, [Number(1)], &mut vars); + assert!(res.is_ok(), res.unwrap_err()); + assert_eq!(res.unwrap(), bytes!("\\E[31m").to_owned()); + let res = expand(s, [Number(8)], &mut vars); + assert!(res.is_ok(), res.unwrap_err()); + assert_eq!(res.unwrap(), bytes!("\\E[90m").to_owned()); + let res = expand(s, [Number(42)], &mut vars); + assert!(res.is_ok(), res.unwrap_err()); + assert_eq!(res.unwrap(), bytes!("\\E[38;5;42m").to_owned()); + } }