Skip to main content
summaryrefslogblamecommitdiffstats
blob: 40b4852a030712b1dcd8c9fbf1132709d45977c9 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                                
                                                                

                                                                        

                                                                           
                                            
                                                       
                                                    
                                                                          
  




                                                                                 
                                 





                                                             

                                          

   
                       


                       
                   
                   


                   










                                       
 






















                    
                     
                    
                    

                    
 



                     

                          
                        

                       
                      
                      
 
                     

                          

                                             
                                          
 



                                                                     

                                                                     
                  

                   
                    

                                   
                               

                                                

 
                                                               





                     
                   



                                         

                       
                                         

 
                                                            



                 
                   

                                    

                       
                                         

 
                                                                
                                                                  

 
                                                     
                                     
                                                           

 

                                                  
                  


                      
                                                          

                                                 
                           


                           

                                              











































                                                            
                      























                                 




                                                                  
                                                      






                                                                    









                                                            












                                         
                              

















                        

                 

                         
                 




                                  

                         







                                   


                                 




                                      










































































































































                                       
                                                   
                                                     
                                                                        
                                  






                                                                                     




                                        
                                                 
                                 
                          



                                 
                                   
                          


                                                            
                                                      
                 



                                                                  

                  
                                  
                                                          
                                                      
                 



                                                                 




                                         
                                       
                                         
                                                          
                                                      
                 






                                                          
                                                               


                                                             
                                                                     

                                      


                                 



                                           
                                    




















                                                     
                 
             

                                        

                                                    
                              
                          
                 
                                                 
                                                                                                  
                                       







                                                           


                                                                                      
                                                                          










                                                                                               
                  
















                                                                          
                                                              
                                                         
                      
                                











                                                          
                               

                                
                                                                        

                                                             
                                       


                                
                                           




                                              
                                                                              
                               

                                


                                            
                                                                        

                                                             



                                                                             
                                       

                                
                                           



                                              
                                                  

                          
                     





                                                                        
                                                                            


                                                               
                            

              
            

                      




                     
                                                                        
          
                                






                                                                                                       











                                                                                
                                           
                                                        


                                   
     
                  
     
                            




                                                                                                                  
                                                                                                    

              
                                           

         









                                                                          
      
              

 





                                                                                 
                                                                           













                                                                                                      




































                                                                                                       
                                                
            


                                   
                  

                      
                     
 










                                                                        
                                     

                
                                                                                        


                                                                
                                                     


                   
                               
                                                 





                                                     





                                                                   



                                               
                  
                                   
                                                           
                                                               


                  
                                                                     
                                                             

                 
     
                          
      
         


                   


                                   
                  
                  

                                      
                                     





                                                                              
     

 




















                                       






                                                  
                                                




                                                      
                      
                      


     
                                            
                              
                      
                      
                        
                                                       



                          
 

                                              
                              
                           



                                             

         

                       
                              
 

                                               

                                                        



                                                        



                                                          



                               



                                                

             
     

                                                                                


             
                                             
                              
                      
                      
                        
                                                       



                          
 
                                                         
                                    


                                              
                              
                           



                                             

         

                       
                              
 

                                               

                                                         



                                                        



                                                 



                               



                                                          

             
     

                                                                                


             
                                              
                              
                      
                      
                        
                                                       



                          
 

                       
                              
 







                                                        



                                                         



                               



                                                        

             
     

                                                                                


             

                                            

 
                                            
 
                                                     

                         
                            



                                                                     
                                             
                                         

                  

                                   
                  






                                                                               
     

                                











                                                                           
                  


                                               
                                                            



                                                                                                                 
      
                                                                           


                  




                                                  
                                           
                  

                                  


                                                                                   


                                                    
                                              


                                                                                      



                                                     
                                            
     
                   
                                                             

                                                          
                                                 

                                                    


                                                            

 
                  



















                                                                                                                                                           





                                 
                                                                







                                                       
                                                                                                  

                                           


                                                                                                 




                             


                                                                       



      
                                           

                       
                                                                  


                                                               
              
                                  

                                                

                            
                                
 
                                                               
                          
                                                                 
         


                                                         
                                              


                                                                        
                                        
                            

              
                                    


                                                           

                              

                          





                                                                  
                                                  
         
     
                                                                
      


























                                                                                

 
                                           
                  
            
                            


                            
 


                                  


                                                                                   


                                                           


                                                      
                                              



                                                     
                                            
     
                                           
                                                      
     
                                                            

                                                     
                                                                   


                                                                                  








                                                                     

                  

                   
                                                          






                                                            

                                          


                                           
     

                                                                                       

                                           
                        





                                                         
     
                       
      
     

 


                                                  

                         


                           
                 

                                 
                                         
                  
                                                        



                                                            
                           










                                                                    
                           










                                                                      

                                                     
              

                             
                              


                                  
                              






                                                          

                              




                  

 

                                                                          


                      
                                       
                          


                  
                                       
                         
              



                           

                  
                                       


                  





                                                                    
                                                                     

                                                            
                                                 
                                                   
                                                         
             
                               
                           



                  





                                                                     
                                                 
                                                   
                                                         
             
                               
                           



                  





                                                                     

                                                         
             
                               
                           

              
                  

             
                             
                                            
                                     
                         
 






                                      

                                        


                                                                      

                                     
                                                           

                                                              
                                                    

























                                                                                              
                      








                                                            
                                                 

                                           
                                                           




                                    
                                                

                                           
                                                           






                                                  
                                                          

                  


                                                             

                              


                                           
                                                               







                                                                                    



                                                                                     













                                                                                                       
     
      

 
                                                
                              

                                                      


                         
                                   



































                                                                                  
                                

                                                                    
             
                                                                            

                                                              
                                                                                      
                                 

                                          

                                                                                      

                                                                       
                 
                               
                                                
                                                       

                                                                                                   
                                   

                                          


                                                                                
                     
                 
                               
                                                    
                                                          

                  
                                  

                                          


                                                                                
                     
                 
                               
                                                   
                                                         
             
                                                    



         

                                                      



                                              

                                            
                                                                                                     
                                        
                                  
                                
                                                               





                                                                                     
                                                               

                                                            

                                     

                               
                  
                                                                                                                   










                                                                                   






                                                                  
                                                         
             
                                                                                                                             








                                                                                 





                                                                  
                                                   
                                                        
             
      



                                                                                           
                                 
                             

                                                                                  
                 
                               

                                                       
             
                                                                                                   
                                   
                             

                                                                            
                 
                               

                                                          

                  
                                  
                             

                                                                            
                 
                               

                                                         
             
                                                    



         

                                                   



                                                    

                                      
                               
                                                              

                                                                     
                                                                              
                                                           
                                 

                                                                                     



                                 

                                                                                     
                     
                                                       


                  
                                                           
                                 

                                                                                    



                                 

                                                                                    
                     
                                                       

                 
                           

                                                      



         

                                                        



                                                                                        

                                   
                               
                                                                                        
                          


                                                              
                             



                                                   

                 

                                                                                           



                                                                                      

                 
                                                                                                   
                             



                                                                                



                             



                                                                                

                 
                                               
                                               
                           

                                                      



         

                                                      



                                                    

                                        
                               


                                                                                        
                                                                       
             
                                                                                           
                                                                  
             
                  
                                                            
             

                                               
                                               
                           

                                                      



         

                                                 


                            

                                      
                              


                                                                     

                                                                                              
                                                           


                                                   
                                                           
             
                                               
                           

                                                     



         

                                                          


                            

                                 
                              


                                                                     

                                                                                              
                                                           


                                                   
                                                           
             
                                               
                           

                                                     



         

                                                          


                            

                                          
                              


                                                                     

                                                                                              
                                                           


                                                   
                                                           
             
                                               
                           

                                                     



         

                                                         

                               
                                    
                  
                                                          



                                             


     

                                                        

                              
                                    
                  
                                                          



                                             


     

                                                         


                         
                                    
                  
                                             

                                                                     
                                                          
                                                      
                       


     
                                             
                                          


                                    
                                                                                              



                             
                           
                                    








                                                                                               

 
                                                                                                       
              


                             
                           
                                    
                                                                                    









                                                                                               

 
                                            
              

                                      
                      
             

 
                                                       
              

                                                  
                      
             

 
                                              
              
                                    




                                  
                                                 
              
                                    




                                  
                                             
              
                                    




                                     












                                                            
 



                                  
                      


                           

                  
                 
                     


                       
                  
                   
                        

                   
             
 

                                                                                       
 
                                                    
 
                                                     


                                     

                                                















                                                                       
 
                                                                                                  


                         
                      


                                                                         
 


                                                        

                                               
 

                                                  
 

                                                  
 
                                                 
                                                            


                                                           
                                                                 

     
                

             





                                                                                              
                
                          

                       
                         
                          

                                              


                                
                                       



                             
                             
         
                      
                                                                     
     
                                                 

                              
 
               
                                                 
                                                                                
         

                                                       

         
 



                    

              


             
                                                                  


                                 
                                     




                                       
                                         
 







                                             
                           
 


                                         
 
                           
 


                                              
 
                                                 

                               
                                        
                               
                                               
     
 

                               
 



                                           
 
                           
 

                                   
                                       
 


                           


                                                

                               
                             
                
 
                                                                              
 
                 
 
                                
                                        





                                       
                                     





                                      










                                                                       
                  
 


                                
 
                                                              


                                                                              
     
                                

 

      


                                                 
                
 
                                                    
                  
                        


                                   
 
                    
 


                                                                         
         

                                                            



                       
 

                                                                           
     
     









                                        
                               

















                                                          
      




                                      
 

                                                             
 
                                                        


                                                                       












                                                                   

                                                               

                                                              
                   
                             

               
                    
                             
                                         
                                                
                                                                              
         

                                                         
         
                                                                                     



                                             
                  
                                                                                             
      

         
 

                 
                                
                                        





                                       
                                                                 

                                                                    
                                  



                                      

 

                                                       
 




                                                                    


                                                                       















                                                                              
                                                                               
                                                                                             
                                                          
 

                 
                                
                                        


                                       
          

                                   
                     
                                                                     
                            
                                                                            



                            

                                                     
                                                                     
                                     

                                                                                       



                                                          


                                      
                              
                    


                                       
                    
                                   







                                                       
                  

                                                    

                                               
                                                              

                  







                                                             
      







                                                                           







                                                      






                                                    
 





                                      

                                                         
 
                                                        
                                                              













                                                                       
 
                                     
                                                                              
                                                                               
                                                                                             




                                                                                                                           
                     
                                                                                      
      


                                                                                                          
                  
                                                                                      
      




                                     
 
                 
 





                                        
 





                                                                       


                                                                       

                                                                     


                                                        






















                                                                       


                                           










                                         

 




                                                                       
                                                
               
                                                                 






                                                                                 
 
                                   

 
                                    
/*******************************************************************************
 * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * You may elect to redistribute this code under either of these licenses.
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/

/*
 * Expression evaluation service.
 *
 * Extensions to regular C/C++ syntax:
 * 1. Special characters in identifiers: $"X"
 *    where X is object name that can contain any characters.
 * 2. Symbol IDs in expressions: ${X}
 *    where X is symbol ID as returned by symbols service.
 * 3. CPU registers: $X
 *    where X is a register name, e.g. $ax
 */

#include <tcf/config.h>

#if SERVICE_Expressions

#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/exceptions.h>
#include <tcf/framework/json.h>
#include <tcf/framework/cache.h>
#include <tcf/framework/context.h>
#include <tcf/services/symbols.h>
#include <tcf/services/stacktrace.h>
#include <tcf/services/expressions.h>
#include <tcf/services/memoryservice.h>
#include <tcf/services/registers.h>
#include <tcf/main/test.h>

#define SY_LEQ   256
#define SY_GEQ   257
#define SY_EQU   258
#define SY_NEQ   259
#define SY_AND   260
#define SY_OR    261
#define SY_SHL   262
#define SY_SHR   263
#define SY_VAL   264
#define SY_ID    265
#define SY_REF   266
#define SY_DEC   267
#define SY_INC   268
#define SY_A_SUB 269
#define SY_A_ADD 270
#define SY_A_SHL 271
#define SY_A_SHR 272
#define SY_A_OR  273
#define SY_A_XOR 274
#define SY_A_AND 275
#define SY_A_MUL 276
#define SY_A_DIV 277
#define SY_A_MOD 278
#define SY_SIZEOF 279
#define SY_NAME  280
#define SY_SCOPE 281
#define SY_PM_D  282
#define SY_PM_R  283

#define MODE_NORMAL 0
#define MODE_TYPE   1
#define MODE_SKIP   2

static char * text = NULL;
static int text_pos = 0;
static int text_len = 0;
static int text_ch = 0;
static int text_sy = 0;
static int sy_pos = 0;
static Value text_val;

/* Host endianness */
static int big_endian = 0;

static Context * expression_context = NULL;
static int expression_frame = STACK_NO_FRAME;
static ContextAddress expression_addr = 0;

#define MAX_ID_CALLBACKS 8
static ExpressionIdentifierCallBack * id_callbacks[MAX_ID_CALLBACKS];
static int id_callback_cnt = 0;

void set_value(Value * v, void * data, size_t size, int big_endian) {
    v->sym = NULL;
    v->reg = NULL;
    v->remote = 0;
    v->address = 0;
    v->function = 0;
    v->size = (ContextAddress)size;
    v->big_endian = big_endian;
    v->value = tmp_alloc(size);
    if (data == NULL) memset(v->value, 0, size);
    else memcpy(v->value, data, size);
}

static void set_int_value(Value * v, size_t size, uint64_t n) {
    union {
        uint8_t  u8;
        uint16_t u16;
        uint32_t u32;
        uint64_t u64;
    } buf;
    switch (size) {
    case 1: buf.u8 = (uint8_t)n; break;
    case 2: buf.u16 = (uint16_t)n; break;
    case 4: buf.u32 = (uint32_t)n; break;
    case 8: buf.u64 = n; break;
    default: assert(0);
    }
    set_value(v, &buf, size, big_endian);
}

static void set_fp_value(Value * v, size_t size, double n) {
    union {
        float  f;
        double d;
    } buf;
    switch (size) {
    case 4: buf.f = (float)n; break;
    case 8: buf.d = n; break;
    default: assert(0);
    }
    set_value(v, &buf, size, big_endian);
}

static void set_ctx_word_value(Value * v, ContextAddress data) {
    set_int_value(v, context_word_size(expression_context), data);
}

static void set_string_value(Value * v, char * str) {
    v->type_class = TYPE_CLASS_ARRAY;
    if (str != NULL) set_value(v, str, strlen(str) + 1, 0);
}

static void error(int no, const char * fmt, ...) {
    va_list ap;
    char buf[256];
    size_t l = 0;

    va_start(ap, fmt);
    l = snprintf(buf, sizeof(buf), "At col %d: ", sy_pos);
    vsnprintf(buf + l, sizeof(buf) - l, fmt, ap);
    va_end(ap);
    str_exception(no, buf);
}

static void next_ch(void) {
    if (text_pos >= text_len) return;
    text_ch = (unsigned char)text[text_pos++];
}

static int next_hex(void) {
    int ch = text_ch;
    next_ch();
    if (ch >= '0' && ch <= '9') return ch - '0';
    if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
    if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
    error(ERR_INV_EXPRESSION, "Invalid hexadecimal number");
    return 0;
}

static int next_oct(void) {
    int ch = text_ch;
    next_ch();
    if (ch >= '0' && ch <= '7') return ch - '0';
    error(ERR_INV_EXPRESSION, "Invalid octal number");
    return 0;
}

static int next_dec(void) {
    int ch = text_ch;
    next_ch();
    if (ch >= '0' && ch <= '9') return ch - '0';
    error(ERR_INV_EXPRESSION, "Invalid decimal number");
    return 0;
}

static int next_char_val(void) {
    int n = 0;
    if (text_ch == '\\') {
        next_ch();
        switch (text_ch) {
        case 'n' : n = '\n'; break;
        case 't' : n = '\t'; break;
        case 'v' : n = '\v'; break;
        case 'b' : n = '\b'; break;
        case 'r' : n = '\r'; break;
        case 'f' : n = '\f'; break;
        case 'a' : n = '\a'; break;
        case '\\': n = '\\'; break;
        case '\'': n = '\''; break;
        case '"' : n = '"'; break;
        case 'x' :
            next_ch();
            n = next_hex() << 8;
            n |= next_hex() << 4;
            n |= next_hex();
            return n;
        case '0' :
        case '1' :
        case '2' :
        case '3' :
            n = next_oct() << 6;
            n |= next_oct() << 3;
            n |= next_oct();
            return n;
        default  :
            n = text_ch;
            break;
        }
    }
    else {
        n = text_ch;
    }
    next_ch();
    return n;
}

static void set_string_text_val(int pos, int len, int in_quotes) {
    int cnt = 0;
    memset(&text_val, 0, sizeof(text_val));
    text_val.type_class = TYPE_CLASS_ARRAY;
    text_val.size = len + 1;
    text_val.value = tmp_alloc((size_t)text_val.size);
    text_val.constant = 1;
    text_pos = pos - 1;
    next_ch();
    if (in_quotes) {
        while (cnt < len) {
            ((char *)text_val.value)[cnt++] = (char)next_char_val();
        }
    }
    else {
        while (cnt < len) {
            ((char *)text_val.value)[cnt++] = (char)text_ch;
            next_ch();
        }
    }
    ((char *)text_val.value)[cnt] = 0;
}

static int is_name_character(int ch) {
    if (ch >= 'A' && ch <= 'Z') return 1;
    if (ch >= 'a' && ch <= 'z') return 1;
    if (ch >= '0' && ch <= '9') return 1;
    if (ch == '_') return 1;
    if (ch == '$') return 1;
    if (ch == '@') return 1;
    return 0;
}

static void next_sy(void) {
    for (;;) {
        int ch = text_ch;
        sy_pos = text_pos - 1;
        next_ch();
        switch (ch) {
        case 0:
            text_sy = 0;
            return;
        case ' ':
        case '\r':
        case '\n':
        case '\t':
            continue;
        case '(':
        case ')':
        case '{':
        case '}':
        case '~':
        case '[':
        case ']':
        case ';':
        case '?':
        case ',':
            text_sy = ch;
            return;
        case '.':
            if (text_ch == '*') {
                next_ch();
                text_sy = SY_PM_D;
                return;
            }
            text_sy = ch;
            return;
        case ':':
            if (text_ch == ':') {
                next_ch();
                text_sy = SY_SCOPE;
                return;
            }
            text_sy = ch;
            return;
        case '-':
            if (text_ch == '>') {
                next_ch();
                if (text_ch == '*') {
                    next_ch();
                    text_sy = SY_PM_R;
                    return;
                }
                text_sy = SY_REF;
                return;
            }
            if (text_ch == '-') {
                next_ch();
                text_sy = SY_DEC;
                return;
            }
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_A_SUB;
                return;
            }
            text_sy = ch;
            return;
        case '+':
            if (text_ch == '+') {
                next_ch();
                text_sy = SY_INC;
                return;
            }
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_A_ADD;
                return;
            }
            text_sy = ch;
            return;
        case '<':
            if (text_ch == '<') {
                next_ch();
                if (text_ch == '=') {
                    next_ch();
                    text_sy = SY_A_SHL;
                    return;
                }
                text_sy = SY_SHL;
                return;
            }
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_LEQ;
                return;
            }
            text_sy = ch;
            return;
        case '>':
            if (text_ch == '>') {
                next_ch();
                if (text_ch == '=') {
                    next_ch();
                    text_sy = SY_A_SHR;
                    return;
                }
                text_sy = SY_SHR;
                return;
            }
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_GEQ;
                return;
            }
            text_sy = ch;
            return;
        case '=':
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_EQU;
                return;
            }
            text_sy = ch;
            return;
        case '!':
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_NEQ;
                return;
            }
            text_sy = ch;
            return;
        case '&':
            if (text_ch == '&') {
                next_ch();
                text_sy = SY_AND;
                return;
            }
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_A_AND;
                return;
            }
            text_sy = ch;
            return;
        case '|':
            if (text_ch == '|') {
                next_ch();
                text_sy = SY_OR;
                return;
            }
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_A_OR;
                return;
            }
            text_sy = ch;
            return;
        case '*':
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_A_MUL;
                return;
            }
            text_sy = ch;
            return;
        case '/':
            if (text_ch == '|') {
                next_ch();
                text_sy = SY_A_DIV;
                return;
            }
            text_sy = ch;
            return;
        case '%':
            if (text_ch == '|') {
                next_ch();
                text_sy = SY_A_MOD;
                return;
            }
            text_sy = ch;
            return;
        case '^':
            if (text_ch == '=') {
                next_ch();
                text_sy = SY_A_XOR;
                return;
            }
            text_sy = ch;
            return;
        case '\'':
            memset(&text_val, 0, sizeof(text_val));
            text_val.type_class = TYPE_CLASS_INTEGER;
            set_int_value(&text_val, sizeof(uint16_t), next_char_val());
            text_val.constant = 1;
            if (text_ch != '\'') error(ERR_INV_EXPRESSION, "Missing 'single quote'");
            next_ch();
            text_sy = SY_VAL;
            return;
        case '"':
            {
                int len = 0;
                int pos = text_pos;
                while (text_ch != '"') {
                    next_char_val();
                    len++;
                }
                set_string_text_val(pos, len, 1);
                text_sy = SY_VAL;
                next_ch();
            }
            return;
        case '0':
            if (text_ch == 'x') {
                uint64_t value = 0;
                next_ch();
                while ((text_ch >= '0' && text_ch <= '9') ||
                       (text_ch >= 'A' && text_ch <= 'F') ||
                       (text_ch >= 'a' && text_ch <= 'f')) {
                    value = (value << 4) | next_hex();
                }
                memset(&text_val, 0, sizeof(text_val));
                text_val.type_class = TYPE_CLASS_CARDINAL;
                set_int_value(&text_val, sizeof(uint64_t), value);
                text_val.constant = 1;
            }
            else {
                int64_t value = 0;
                while (text_ch >= '0' && text_ch <= '7') {
                    value = (value << 3) | next_oct();
                }
                memset(&text_val, 0, sizeof(text_val));
                text_val.type_class = TYPE_CLASS_INTEGER;
                set_int_value(&text_val, sizeof(int64_t), value);
                text_val.constant = 1;
            }
            text_sy = SY_VAL;
            return;
        default:
            if (ch >= '0' && ch <= '9') {
                int pos = text_pos - 2;
                int64_t value = ch - '0';
                while (text_ch >= '0' && text_ch <= '9') {
                    value = (value * 10) + next_dec();
                }
                memset(&text_val, 0, sizeof(text_val));
                if (text_ch == '.') {
                    char * end = NULL;
                    double x = strtod(text + pos, &end);
                    text_pos = end - text;
                    next_ch();
                    text_val.type_class = TYPE_CLASS_REAL;
                    set_fp_value(&text_val, sizeof(double), x);
                }
                else {
                    text_val.type_class = TYPE_CLASS_INTEGER;
                    set_int_value(&text_val, sizeof(int64_t), value);
                }
                text_val.constant = 1;
                text_sy = SY_VAL;
                return;
            }
            if (ch == '$') {
                if (text_ch == '"') {
                    int len = 0;
                    int pos = text_pos + 1;
                    next_char_val();
                    while (text_ch != '"') {
                        next_char_val();
                        len++;
                    }
                    set_string_text_val(pos, len, 1);
                    text_sy = SY_NAME;
                    next_ch();
                    return;
                }
                if (text_ch == '{') {
                    int len = 0;
                    int pos = text_pos + 1;
                    next_ch();
                    while (text_ch != '}') {
                        next_ch();
                        len++;
                    }
                    set_string_text_val(pos, len, 0);
                    text_sy = SY_ID;
                    next_ch();
                    return;
                }
            }
            if (is_name_character(ch)) {
                int len = 1;
                int pos = text_pos - 1;
                while (is_name_character(text_ch)) {
                    next_ch();
                    len++;
                }
                set_string_text_val(pos, len, 0);
                if (strcmp((const char *)text_val.value, "sizeof") == 0) text_sy = (int)SY_SIZEOF;
                else text_sy = SY_NAME;
                return;
            }
            error(ERR_INV_EXPRESSION, "Illegal character");
            break;
        }
    }
}

static void reg2value(Context * ctx, int frame, RegisterDefinition * def, Value * v) {
    memset(v, 0, sizeof(Value));
    set_value(v, NULL, def->size, def->big_endian);
    v->type_class = def->fp_value ? TYPE_CLASS_REAL : TYPE_CLASS_CARDINAL;
    v->reg = def;
    if (frame == STACK_TOP_FRAME) {
        if (context_read_reg(ctx, def, 0, def->size, v->value) < 0) exception(errno);
    }
    else {
        StackFrame * info = NULL;
        if (get_frame_info(ctx, frame, &info) < 0) exception(errno);
        if (read_reg_bytes(info, def, 0, def->size, (uint8_t *)v->value) < 0) exception(errno);
    }
}

#if ENABLE_Symbols
static void set_value_endianness(Value * v, Symbol * sym, Symbol * type) {
    SYM_FLAGS flags = 0;
    if (sym != NULL && get_symbol_flags(sym, &flags) < 0) {
        error(errno, "Cannot retrieve symbol flags");
    }
    if (flags & SYM_FLAG_BIG_ENDIAN) v->big_endian = 1;
    else if (flags & SYM_FLAG_LITTLE_ENDIAN) v->big_endian = 0;
    else {
        if (type != NULL && get_symbol_flags(type, &flags) < 0) {
            error(errno, "Cannot retrieve symbol flags");
        }
        if (flags & SYM_FLAG_BIG_ENDIAN) v->big_endian = 1;
        else if (flags & SYM_FLAG_LITTLE_ENDIAN) v->big_endian = 0;
        else v->big_endian = expression_context->big_endian;
    }
}

/* Note: sym2value() does NOT set v->size if v->sym != NULL */
static int sym2value(int mode, Symbol * sym, Value * v) {
    int sym_class = 0;
    memset(v, 0, sizeof(Value));
    if (get_symbol_class(sym, &sym_class) < 0) {
        error(errno, "Cannot retrieve symbol class");
    }
    if (get_symbol_type(sym, &v->type) < 0) {
        error(errno, "Cannot retrieve symbol type");
    }
    if (get_symbol_type_class(sym, &v->type_class) < 0) {
        error(errno, "Cannot retrieve symbol type class");
    }
    switch (sym_class) {
    case SYM_CLASS_VALUE:
        {
            int endianness = 0;
            size_t size = 0;
            void * value = NULL;
            if (get_symbol_value(sym, &value, &size, &endianness) < 0) {
                error(errno, "Cannot retrieve symbol value");
            }
            v->big_endian = endianness;
            v->constant = 1;
            v->size = size;
            if (value != NULL) {
                v->value = tmp_alloc(size);
                memcpy(v->value, value, size);
            }
        }
        break;
    case SYM_CLASS_REFERENCE:
        if (mode == MODE_NORMAL && get_symbol_address(sym, &v->address) < 0) {
            int endianness = 0;
            size_t size = 0;
            void * value = NULL;
            int frame = 0;
            Context * ctx = NULL;
            RegisterDefinition * reg = NULL;
            if (get_symbol_value(sym, &value, &size, &endianness) < 0) {
                error(errno, "Cannot retrieve symbol value");
            }
            if (get_symbol_register(sym, &ctx, &frame, &reg) == 0 &&
                    ctx == expression_context && frame == expression_frame) {
                v->reg = reg;
            }
            v->big_endian = endianness;
            v->size = size;
            if (value != NULL) {
                v->value = tmp_alloc(size);
                memcpy(v->value, value, size);
            }
        }
        else {
            set_value_endianness(v, sym, v->type);
            v->remote = 1;
        }
        v->sym = sym;
        break;
    case SYM_CLASS_FUNCTION:
        {
            ContextAddress word = 0;
            v->type_class = TYPE_CLASS_CARDINAL;
            if (v->type != NULL) get_array_symbol(v->type, 0, &v->type);
            if (mode == MODE_NORMAL && get_symbol_address(sym, &word) < 0) {
                error(errno, "Cannot retrieve symbol address");
            }
            set_ctx_word_value(v, word);
            v->function = 1;
        }
        break;
    default:
        v->type = sym;
        break;
    }
    return sym_class;
}
#endif

static int identifier(int mode, Value * scope, char * name, Value * v) {
    int i;
    memset(v, 0, sizeof(Value));
    if (scope == NULL) {
        for (i = 0; i < id_callback_cnt; i++) {
            if (id_callbacks[i](expression_context, expression_frame, name, v)) return SYM_CLASS_VALUE;
        }
        if (expression_context == NULL) {
            exception(ERR_INV_CONTEXT);
        }
        if (name[0] == '$') {
            RegisterDefinition * def = get_reg_definitions(expression_context);
            if (def != NULL) {
                while (def->name != NULL) {
                    if (strcmp(name + 1, def->name) == 0) {
                        reg2value(expression_context, expression_frame, def, v);
                        return SYM_CLASS_REFERENCE;
                    }
                    def++;
                }
            }
        }
        if (strcmp(name, "$thread") == 0) {
            set_string_value(v, expression_context->id);
            v->constant = 1;
            return SYM_CLASS_VALUE;
        }
    }
#if ENABLE_Symbols
    {
        Symbol * sym = NULL;
        int n = scope != NULL ?
            find_symbol_in_scope(expression_context, expression_frame, expression_addr, scope->type, name, &sym) :
            find_symbol_by_name(expression_context, expression_frame, expression_addr, name, &sym);

        if (n < 0) {
            if (get_error_code(errno) != ERR_SYM_NOT_FOUND) error(errno, "Cannot read symbol data");
        }
        else {
            return sym2value(mode, sym, v);
        }
    }
#elif ENABLE_RCBP_TEST
    {
        void * ptr = NULL;
        int cls = 0;
        if (find_test_symbol(expression_context, name, &ptr, &cls) >= 0) {
            v->type_class = TYPE_CLASS_CARDINAL;
            set_ctx_word_value(v, (ContextAddress)ptr);
            return cls;
        }
    }
#endif
    return -1;
}

static int qualified_name(int mode, Value * scope, Value * v) {
    Value x;
    int sym_class = 0;
    for (;;) {
        if (text_sy != SY_NAME) error(ERR_INV_EXPRESSION, "Identifier expected");
        if (mode != MODE_SKIP) {
            sym_class = identifier(mode, scope, (char *)text_val.value, v);
            if (sym_class < 0) error(ERR_INV_EXPRESSION, "Undefined identifier '%s'", text_val.value);
        }
        else {
            memset(v, 0, sizeof(Value));
        }
        next_sy();
        if (text_sy != SY_SCOPE) break;
        next_sy();
        scope = &x;
        x = *v;
    }
    return sym_class;
}

static int64_t to_int(int mode, Value * v);
#define TYPE_EXPR_LENGTH 64

static int type_expression(int mode, int * buf) {
    int i = 0;
    int pos = 0;
    int expr_buf[TYPE_EXPR_LENGTH];
    int expr_len = 0;
    while (text_sy == '*') {
        next_sy();
        if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long");
        buf[pos++] = 1;
    }
    if (text_sy == '(') {
        next_sy();
        expr_len = type_expression(mode, expr_buf);
        if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected");
        next_sy();
    }
    while (text_sy == '[') {
        next_sy();
        if (text_sy != SY_VAL) error(ERR_INV_EXPRESSION, "Number expected");
        if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long");
        buf[pos] = (int)to_int(mode, &text_val);
        if (mode == MODE_NORMAL && buf[pos] < 1) error(ERR_INV_EXPRESSION, "Positive number expected");
        pos++;
        next_sy();
        if (text_sy != ']') error(ERR_INV_EXPRESSION, "']' expected");
        next_sy();
    }
    for (i = 0; i < expr_len; i++) {
        if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long");
        buf[pos++] = expr_buf[i];
    }
    return pos;
}

static int type_name(int mode, Symbol ** type) {
    Value v;
    int expr_buf[TYPE_EXPR_LENGTH];
    int expr_len = 0;
    char name[256];
    int sym_class;
    int is_struct = 0;
    int is_class = 0;
    int name_cnt = 0;

    if (text_sy == SY_NAME) {
        if (strcmp((const char *)(text_val.value), "struct") == 0) {
            is_struct = 1;
            next_sy();
        }
        else if (strcmp((const char *)(text_val.value), "class") == 0) {
            is_class = 1;
            next_sy();
        }
    }

    if (text_sy != SY_NAME) return 0;
    name[0] = 0;
    do {
        if (strlen((const char *)(text_val.value)) + strlen(name) >= sizeof(name) - 1) {
            error(ERR_BUFFER_OVERFLOW, "Type name is too long");
        }
        if (name[0]) strcat(name, " ");
        strcat(name, (const char *)(text_val.value));
        name_cnt++;
        next_sy();
    }
    while (text_sy == SY_NAME);
    sym_class = identifier(mode, NULL, name, &v);
    if (sym_class < 0) return 0;
    if (text_sy == SY_SCOPE) {
        Value scope = v;
        next_sy();
        sym_class = qualified_name(mode, &scope, &v);
    }
    if (sym_class != SYM_CLASS_TYPE) {
        if (is_struct || is_class) {
            error(ERR_INV_EXPRESSION, "Type '%s' not found", name);
        }
        return 0;
    }
    expr_len = type_expression(mode, expr_buf);
    if (mode != MODE_SKIP) {
        int i;
        for (i = 0; i < expr_len; i++) {
#if ENABLE_Symbols
            if (expr_buf[i] == 1) {
                if (get_array_symbol(v.type, 0, &v.type)) {
                    error(errno, "Cannot create pointer type");
                }
            }
            else {
                if (get_array_symbol(v.type, expr_buf[i], &v.type)) {
                    error(errno, "Cannot create array type");
                }
            }
#else
            v.type = NULL;
#endif
        }
    }
    *type = v.type;
    return 1;
}

static void load_value(Value * v) {
    v->sym = NULL;
    v->reg = NULL;
    if (v->remote) {
        size_t size = (size_t)v->size;
        void * buf = tmp_alloc(size);
        assert(!v->constant);
        if (context_read_mem(expression_context, v->address, buf, size) < 0) {
            error(errno, "Can't read variable value");
        }
        v->value = buf;
        v->remote = 0;
    }
}

static int is_number(Value * v) {
    switch (v->type_class) {
    case TYPE_CLASS_INTEGER:
    case TYPE_CLASS_CARDINAL:
    case TYPE_CLASS_REAL:
    case TYPE_CLASS_ENUMERATION:
        return 1;
    }
    return 0;
}

static int is_whole_number(Value * v) {
    switch (v->type_class) {
    case TYPE_CLASS_INTEGER:
    case TYPE_CLASS_CARDINAL:
    case TYPE_CLASS_ENUMERATION:
        return 1;
    }
    return 0;
}

static void to_host_endianness(Value * v) {
    assert(v->type_class != TYPE_CLASS_COMPOSITE);
    assert(v->type_class != TYPE_CLASS_ARRAY);
    assert(!v->remote);
    if (v->big_endian != big_endian) {
        size_t i = 0;
        size_t n = (size_t)v->size;
        uint8_t * buf = (uint8_t *)tmp_alloc(n);
        for (i = 0; i < n; i++) {
            buf[i] = ((uint8_t *)v->value)[n - i - 1];
        }
        v->value = buf;
        v->big_endian = big_endian;
        v->sym = NULL;
        v->reg = NULL;
    }
}

static int64_t to_int(int mode, Value * v) {
    if (mode != MODE_NORMAL) {
        v->sym = NULL;
        v->reg = NULL;
        if (v->remote) {
            v->value = tmp_alloc_zero((size_t)v->size);
            v->remote = 0;
        }
        return 0;
    }

    if (v->type_class == TYPE_CLASS_POINTER) {
        load_value(v);
        to_host_endianness(v);
        switch (v->size)  {
        case 1: return *(uint8_t *)v->value;
        case 2: return *(uint16_t *)v->value;
        case 4: return *(uint32_t *)v->value;
        case 8: return *(uint64_t *)v->value;
        }
    }
    if (is_number(v)) {
        load_value(v);
        to_host_endianness(v);

        if (v->type_class == TYPE_CLASS_REAL) {
            switch (v->size)  {
            case 4: return (int64_t)*(float *)v->value;
            case 8: return (int64_t)*(double *)v->value;
            }
        }
        else if (v->type_class == TYPE_CLASS_CARDINAL) {
            switch (v->size)  {
            case 1: return (int64_t)*(uint8_t *)v->value;
            case 2: return (int64_t)*(uint16_t *)v->value;
            case 4: return (int64_t)*(uint32_t *)v->value;
            case 8: return (int64_t)*(uint64_t *)v->value;
            }
        }
        else {
            switch (v->size)  {
            case 1: return *(int8_t *)v->value;
            case 2: return *(int16_t *)v->value;
            case 4: return *(int32_t *)v->value;
            case 8: return *(int64_t *)v->value;
            }
        }
    }

    error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type");
    return 0;
}

static uint64_t to_uns(int mode, Value * v) {
    if (mode != MODE_NORMAL) {
        v->sym = NULL;
        v->reg = NULL;
        if (v->remote) {
            v->value = tmp_alloc_zero((size_t)v->size);
            v->remote = 0;
        }
        return 0;
    }

    if (v->type_class == TYPE_CLASS_ARRAY && v->remote) {
        return (uint64_t)v->address;
    }
    if (v->type_class == TYPE_CLASS_POINTER) {
        load_value(v);
        to_host_endianness(v);
        switch (v->size)  {
        case 1: return *(uint8_t *)v->value;
        case 2: return *(uint16_t *)v->value;
        case 4: return *(uint32_t *)v->value;
        case 8: return *(uint64_t *)v->value;
        }
    }
    if (is_number(v)) {
        load_value(v);
        to_host_endianness(v);

        if (v->type_class == TYPE_CLASS_REAL) {
            switch (v->size)  {
            case 4: return (uint64_t)*(float *)v->value;
            case 8: return (uint64_t)*(double *)v->value;
            }
        }
        else if (v->type_class == TYPE_CLASS_CARDINAL) {
            switch (v->size)  {
            case 1: return *(uint8_t *)v->value;
            case 2: return *(uint16_t *)v->value;
            case 4: return *(uint32_t *)v->value;
            case 8: return *(uint64_t *)v->value;
            }
        }
        else {
            switch (v->size)  {
            case 1: return (uint64_t)*(int8_t *)v->value;
            case 2: return (uint64_t)*(int16_t *)v->value;
            case 4: return (uint64_t)*(int32_t *)v->value;
            case 8: return (uint64_t)*(int64_t *)v->value;
            }
        }
    }

    error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type");
    return 0;
}

static double to_double(int mode, Value * v) {
    if (mode != MODE_NORMAL) {
        v->sym = NULL;
        v->reg = NULL;
        if (v->remote) {
            v->value = tmp_alloc_zero((size_t)v->size);
            v->remote = 0;
        }
        return 0;
    }

    if (is_number(v)) {
        load_value(v);
        to_host_endianness(v);

        if (v->type_class == TYPE_CLASS_REAL) {
            switch (v->size)  {
            case 4: return *(float *)v->value;
            case 8: return *(double *)v->value;
            }
        }
        else if (v->type_class == TYPE_CLASS_CARDINAL) {
            switch (v->size)  {
            case 1: return (double)*(uint8_t *)v->value;
            case 2: return (double)*(uint16_t *)v->value;
            case 4: return (double)*(uint32_t *)v->value;
            case 8: return (double)*(uint64_t *)v->value;
            }
        }
        else {
            switch (v->size)  {
            case 1: return (double)*(int8_t *)v->value;
            case 2: return (double)*(int16_t *)v->value;
            case 4: return (double)*(int32_t *)v->value;
            case 8: return (double)*(int64_t *)v->value;
            }
        }
    }

    error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type");
    return 0;
}

static int to_boolean(int mode, Value * v) {
    return to_int(mode, v) != 0;
}

static void expression(int mode, Value * v);

static void primary_expression(int mode, Value * v) {
    if (text_sy == '(') {
        next_sy();
        expression(mode, v);
        if (text_sy != ')') error(ERR_INV_EXPRESSION, "Missing ')'");
        next_sy();
    }
    else if (text_sy == SY_VAL) {
        if (mode != MODE_SKIP) *v = text_val;
        else memset(v, 0, sizeof(Value));
        next_sy();
    }
    else if (text_sy == SY_SCOPE) {
        Value x;
        next_sy();
        memset(&x, 0, sizeof(x));
        if (qualified_name(mode, &x, v) == SYM_CLASS_TYPE)
            error(ERR_INV_EXPRESSION, "Illegal usage of a type in expression");
    }
    else if (text_sy == SY_NAME) {
        if (qualified_name(mode, NULL, v) == SYM_CLASS_TYPE)
            error(ERR_INV_EXPRESSION, "Illegal usage of a type in expression");
    }
    else if (text_sy == SY_ID) {
        if (mode != MODE_SKIP) {
            int ok = 0;
            const char * id = (char *)text_val.value;
            {
                Context * ctx = NULL;
                int frame = STACK_NO_FRAME;
                RegisterDefinition * def = NULL;
                if (id2register(id, &ctx, &frame, &def) >= 0) {
                    if (frame == STACK_TOP_FRAME) frame = expression_frame;
                    reg2value(ctx, frame, def, v);
                    ok = 1;
                }
            }
#if ENABLE_Symbols
            if (!ok) {
                Symbol * sym = NULL;
                if (id2symbol(id, &sym) >= 0) {
                    int sym_class = sym2value(mode, sym, v);
                    if (sym_class == SYM_CLASS_TYPE) error(ERR_INV_EXPRESSION, "Illegal usage of type '%s'", id);
                    ok = 1;
                }
            }
#endif
            if (!ok) error(ERR_INV_EXPRESSION, "Symbol not found: %s", id);
        }
        next_sy();
    }
    else {
        error(ERR_INV_EXPRESSION, "Syntax error");
    }
}

static void op_deref(int mode, Value * v) {
#if ENABLE_Symbols
    Symbol * type = NULL;
    if (mode == MODE_SKIP) return;
    if (v->type_class != TYPE_CLASS_ARRAY && v->type_class != TYPE_CLASS_POINTER) {
        error(ERR_INV_EXPRESSION, "Array or pointer type expected");
    }
    if (get_symbol_base_type(v->type, &type) < 0) {
        error(errno, "Cannot retrieve symbol type");
    }
    if (v->type_class == TYPE_CLASS_POINTER) {
        if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) {
            error(errno, "Cannot retrieve symbol size");
        }
        v->address = (ContextAddress)to_uns(mode, v);
        v->remote = 1;
        v->constant = 0;
        v->value = NULL;
        set_value_endianness(v, NULL, type);
    }
    v->type = type;
    if (get_symbol_type_class(v->type, &v->type_class) < 0) {
        error(errno, "Cannot retrieve symbol type class");
    }
    if (get_symbol_size(v->type, &v->size) < 0) {
        error(errno, "Cannot retrieve symbol size");
    }
#else
    error(ERR_UNSUPPORTED, "Symbols service not available");
#endif
}

#if ENABLE_Symbols
static void evaluate_symbol_address(Symbol * sym, ContextAddress obj_addr, ContextAddress index, ContextAddress * addr) {
    LocationExpressionState * state = NULL;
    LocationInfo * loc_info = NULL;
    StackFrame * stk_info = NULL;
    uint64_t args[2];

    args[0] = obj_addr;
    args[1] = index;
    if (get_location_info(sym, &loc_info) < 0) {
        error(errno, "Cannot get symbol location expression");
    }
    if (get_frame_info(expression_context, expression_frame, &stk_info) < 0) {
        error(errno, "Cannot get stack frame info");
    }
    state = evaluate_location_expression(expression_context, stk_info, loc_info->cmds, loc_info->cmds_cnt, args, 2);
    if (state->stk_pos != 1) error(ERR_INV_EXPRESSION, "Cannot evaluate symbol address");
    *addr = (ContextAddress)state->stk[0];
}

static void find_field(Symbol * class_sym, ContextAddress obj_addr, const char * name, const char * id, Symbol ** field_sym, ContextAddress * field_addr) {
    Symbol ** children = NULL;
    Symbol ** inheritance = NULL;
    int count = 0;
    int h = 0;
    int i;

    if (get_symbol_children(class_sym, &children, &count) < 0) {
        error(errno, "Cannot retrieve field list");
    }
    for (i = 0; i < count; i++) {
        char * s = NULL;
        if (get_symbol_name(children[i], &s) < 0) {
            error(errno, "Cannot retrieve field name");
        }
        if (s == NULL) {
            if (inheritance == NULL) inheritance = (Symbol **)tmp_alloc(sizeof(Symbol *) * count);
            inheritance[h++] = children[i];
        }
        else if (name != NULL ? strcmp(s, name) == 0 : strcmp(symbol2id(children[i]), id) == 0) {
            evaluate_symbol_address(children[i], obj_addr, 0, field_addr);
            *field_sym = children[i];
            return;
        }
    }
    for (i = 0; i < h; i++) {
        ContextAddress x = 0;
        evaluate_symbol_address(inheritance[i], obj_addr, 0, &x);
        find_field(inheritance[i], x, name, id, field_sym, field_addr);
        if (*field_sym != NULL) return;
    }
}
#endif

static void op_field(int mode, Value * v) {
    char * id = NULL;
    char * name = NULL;
    if (!v->remote) error(ERR_INV_EXPRESSION, "L-value expected");
    if (text_sy == SY_ID) id = (char *)text_val.value;
    else if (text_sy == SY_NAME) name = (char *)text_val.value;
    else error(ERR_INV_EXPRESSION, "Field name expected");
    next_sy();
    if (mode == MODE_SKIP) return;
    if (v->type_class == TYPE_CLASS_COMPOSITE) {
#if ENABLE_Symbols
        Symbol * sym = NULL;
        int sym_class = 0;
        ContextAddress addr = 0;

        find_field(v->type, v->address, name, id, &sym, &addr);
        if (sym == NULL) {
            error(ERR_SYM_NOT_FOUND, "Invalid field name or ID");
        }
        if (get_symbol_class(sym, &sym_class) < 0) {
            error(errno, "Cannot retrieve symbol class");
        }
        if (sym_class == SYM_CLASS_FUNCTION) {
            v->type_class = TYPE_CLASS_CARDINAL;
            get_symbol_type(sym, &v->type);
            if (v->type != NULL) get_array_symbol(v->type, 0, &v->type);
            set_ctx_word_value(v, addr);
            v->function = 1;
        }
        else {
            ContextAddress size = 0;
            if (get_symbol_size(sym, &size) < 0) {
                error(errno, "Cannot retrieve field size");
            }
            v->address = addr;
            v->size = size;
            v->sym = NULL;
            v->reg = NULL;
            if (get_symbol_type(sym, &v->type) < 0) {
                error(errno, "Cannot retrieve symbol type");
            }
            if (get_symbol_type_class(sym, &v->type_class) < 0) {
                error(errno, "Cannot retrieve symbol type class");
            }
            set_value_endianness(v, sym, v->type);
        }
#else
        error(ERR_UNSUPPORTED, "Symbols service not available");
#endif
    }
    else if (v->reg != NULL) {
        if (id != NULL) {
            Context * ctx = NULL;
            int frame = STACK_NO_FRAME;
            RegisterDefinition * def = NULL;
            if (id2register(id, &ctx, &frame, &def) < 0) exception(errno);
            if (frame == STACK_TOP_FRAME) frame = expression_frame;
            reg2value(ctx, frame, def, v);
        }
        else {
            RegisterDefinition * def = get_reg_definitions(expression_context);
            if (def != NULL) {
                while (def->name != NULL) {
                    if (def->parent == v->reg && strcmp(name, def->name) == 0) {
                        reg2value(expression_context, expression_frame, def, v);
                        return;
                    }
                    def++;
                }
            }
            error(ERR_INV_EXPRESSION, "Unknown register; %s", name);
        }
    }
    else {
        error(ERR_INV_EXPRESSION, "Composite type expected");
    }
}

static void op_index(int mode, Value * v) {
#if ENABLE_Symbols
    Value i;
    int64_t lower_bound = 0;
    ContextAddress offs = 0;
    ContextAddress size = 0;
    Symbol * type = NULL;

    expression(mode, &i);
    if (mode == MODE_SKIP) return;

    if (v->type_class != TYPE_CLASS_ARRAY && v->type_class != TYPE_CLASS_POINTER) {
        error(ERR_INV_EXPRESSION, "Array or pointer expected");
    }
    if (v->type == NULL) {
        error(ERR_INV_EXPRESSION, "Value type is unknown");
    }
    if (get_symbol_base_type(v->type, &type) < 0) {
        error(errno, "Cannot get array element type");
    }
    if (v->type_class == TYPE_CLASS_POINTER) {
        v->address = (ContextAddress)to_uns(mode, v);
        v->remote = 1;
        v->constant = 0;
        v->value = NULL;
        set_value_endianness(v, NULL, type);
    }
    if (get_symbol_size(type, &size) < 0) {
        error(errno, "Cannot get array element size");
    }
    if (get_symbol_lower_bound(v->type, &lower_bound) < 0) {
        error(errno, "Cannot get array lower bound");
    }
    offs = (ContextAddress)(to_int(mode, &i) - lower_bound) * size;
    if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) {
        error(errno, "Cannot retrieve symbol size");
    }
    if (v->type_class == TYPE_CLASS_ARRAY && offs + size > v->size) {
        error(ERR_INV_EXPRESSION, "Invalid index");
    }
    if (v->remote) {
        v->address += offs;
    }
    else {
        v->value = (char *)v->value + offs;
    }
    v->sym = NULL;
    v->reg = NULL;
    v->size = size;
    v->type = type;
    if (get_symbol_type_class(type, &v->type_class) < 0) {
        error(errno, "Cannot retrieve symbol type class");
    }
#else
    error(ERR_UNSUPPORTED, "Symbols service not available");
#endif
}

static void op_addr(int mode, Value * v) {
    if (mode == MODE_SKIP) return;
    if (v->function) {
        v->type_class = TYPE_CLASS_POINTER;
        v->function = 0;
    }
    else {
        if (!v->remote) error(ERR_INV_EXPRESSION, "Invalid '&': value has no address");
        set_ctx_word_value(v, v->address);
        v->type_class = TYPE_CLASS_POINTER;
        v->constant = 0;
#if ENABLE_Symbols
        if (v->type != NULL) {
            if (get_array_symbol(v->type, 0, &v->type)) {
                error(errno, "Cannot get pointer type");
            }
        }
#else
        v->type = NULL;
#endif
    }
}

static void unary_expression(int mode, Value * v);

static void op_sizeof(int mode, Value * v) {
    Symbol * type = NULL;
    int pos = 0;
    int p = text_sy == '(';

    if (p) next_sy();
    pos = sy_pos;
    if (type_name(mode, &type)) {
        if (mode != MODE_SKIP) {
            ContextAddress type_size = 0;
#if ENABLE_Symbols
            if (get_symbol_size(type, &type_size) < 0) {
                error(errno, "Cannot retrieve symbol size");
            }
#endif
            set_ctx_word_value(v, type_size);
            v->type = NULL;
            v->type_class = TYPE_CLASS_CARDINAL;
            v->constant = 1;
        }
    }
    else {
        text_pos = pos;
        next_ch();
        next_sy();
        unary_expression(mode == MODE_NORMAL ? MODE_TYPE : mode, v);
        if (mode != MODE_SKIP) {
            set_ctx_word_value(v, v->size);
            v->type = NULL;
            v->type_class = TYPE_CLASS_CARDINAL;
            v->constant = 1;
        }
    }
    if (p) {
        if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected");
        next_sy();
    }
}


static void postfix_expression(int mode, Value * v) {
    primary_expression(mode, v);
    for (;;) {
        if (text_sy == '.') {
            next_sy();
            op_field(mode, v);
        }
        else if (text_sy == '[') {
            next_sy();
            op_index(mode, v);
            if (text_sy != ']') {
                error(ERR_INV_EXPRESSION, "']' expected");
            }
            next_sy();
        }
        else if (text_sy == SY_REF) {
            next_sy();
            op_deref(mode, v);
            op_field(mode, v);
        }
        else {
            break;
        }
    }
}

/* Note: lazy_unary_expression() does not set v->size if v->sym != NULL */
static void lazy_unary_expression(int mode, Value * v) {
    switch (text_sy) {
    case '*':
        next_sy();
        lazy_unary_expression(mode, v);
        op_deref(mode, v);
        break;
    case '&':
        next_sy();
        lazy_unary_expression(mode, v);
        op_addr(mode, v);
        break;
    case SY_SIZEOF:
        next_sy();
        op_sizeof(mode, v);
        break;
    case '+':
        next_sy();
        lazy_unary_expression(mode, v);
        break;
    case '-':
        next_sy();
        unary_expression(mode, v);
        if (mode != MODE_SKIP) {
            if (!is_number(v)) {
                error(ERR_INV_EXPRESSION, "Numeric types expected");
            }
            else if (v->type_class == TYPE_CLASS_REAL) {
                set_fp_value(v, sizeof(double), -to_double(mode, v));
            }
            else if (v->type_class != TYPE_CLASS_CARDINAL) {
                int64_t value = -to_int(mode, v);
                v->type_class = TYPE_CLASS_INTEGER;
                set_int_value(v, sizeof(int64_t), value);
            }
            assert(!v->remote);
            v->type = NULL;
        }
        break;
    case '!':
        next_sy();
        unary_expression(mode, v);
        if (mode != MODE_SKIP) {
            if (!is_whole_number(v)) {
                error(ERR_INV_EXPRESSION, "Integral types expected");
            }
            else {
                int32_t value = !to_int(mode, v);
                v->type_class = TYPE_CLASS_INTEGER;
                set_int_value(v, sizeof(int32_t), value);
            }
            assert(!v->remote);
            v->type = NULL;
        }
        break;
    case '~':
        next_sy();
        unary_expression(mode, v);
        if (mode != MODE_SKIP) {
            if (!is_whole_number(v)) {
                error(ERR_INV_EXPRESSION, "Integral types expected");
            }
            else {
                int64_t value = ~to_int(mode, v);
                set_int_value(v, sizeof(int64_t), value);
            }
            assert(!v->remote);
            v->type = NULL;
        }
        break;
#if ENABLE_Symbols
    case '(':
    {
        Symbol * type = NULL;
        int type_class = TYPE_CLASS_UNKNOWN;
        ContextAddress type_size = 0;
        int pos = sy_pos;

        assert(text[pos] == '(');
        next_sy();
        if (!type_name(mode, &type)) {
            text_pos = pos;
            next_ch();
            next_sy();
            assert(text_sy == '(');
            postfix_expression(mode, v);
            break;
        }
        if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected");
        next_sy();
        unary_expression(mode, v);
        if (mode == MODE_SKIP) break;
        if (get_symbol_type_class(type, &type_class) < 0) {
            error(errno, "Cannot retrieve symbol type class");
        }
        if (get_symbol_size(type, &type_size) < 0) {
            error(errno, "Cannot retrieve symbol size");
        }
        if (v->remote && v->size == type_size) {
            /* A type cast can be an l-value expression as long as the size does not change */
            int ok = 0;
            switch (type_class) {
            case TYPE_CLASS_CARDINAL:
            case TYPE_CLASS_POINTER:
            case TYPE_CLASS_INTEGER:
            case TYPE_CLASS_ENUMERATION:
                switch (v->type_class) {
                case TYPE_CLASS_CARDINAL:
                case TYPE_CLASS_POINTER:
                case TYPE_CLASS_INTEGER:
                case TYPE_CLASS_ENUMERATION:
                    ok = 1;
                    break;
                }
                break;
            case TYPE_CLASS_REAL:
                ok = v->type_class == TYPE_CLASS_REAL;
                break;
            }
            if (ok) {
                v->type = type;
                v->type_class = type_class;
                break;
            }
        }
        switch (type_class) {
        case TYPE_CLASS_UNKNOWN:
            error(ERR_INV_EXPRESSION, "Unknown type class");
            break;
        case TYPE_CLASS_CARDINAL:
        case TYPE_CLASS_POINTER:
            {
                uint64_t value = to_uns(mode, v);
                v->type = type;
                v->type_class = type_class;
                set_int_value(v, (size_t)type_size, value);
            }
            break;
        case TYPE_CLASS_INTEGER:
        case TYPE_CLASS_ENUMERATION:
            {
                int64_t value = to_int(mode, v);
                v->type = type;
                v->type_class = type_class;
                set_int_value(v, (size_t)type_size, value);
            }
            break;
        case TYPE_CLASS_REAL:
            {
                double value = to_double(mode, v);
                v->type = type;
                v->type_class = type_class;
                set_fp_value(v, (size_t)type_size, value);
            }
            break;
        case TYPE_CLASS_ARRAY:
            if (v->type_class == TYPE_CLASS_POINTER) {
                v->address = (ContextAddress)to_uns(mode, v);
                v->sym = NULL;
                v->reg = NULL;
                v->type = type;
                v->type_class = type_class;
                v->size = type_size;
                v->big_endian = expression_context->big_endian;
                v->remote = 1;
                v->constant = 0;
                v->value = NULL;
            }
            else {
                error(ERR_INV_EXPRESSION, "Invalid type cast: illegal source type");
            }
            break;
        default:
            error(ERR_INV_EXPRESSION, "Invalid type cast: illegal destination type");
            break;
        }
        break;
    }
#endif
    default:
        postfix_expression(mode, v);
        break;
    }
}

static void unary_expression(int mode, Value * v) {
    lazy_unary_expression(mode, v);
#if ENABLE_Symbols
    if (mode != MODE_SKIP && v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) {
        error(errno, "Cannot retrieve symbol size");
    }
#endif
}

static void pm_expression(int mode, Value * v) {
    unary_expression(mode, v);
#if ENABLE_Symbols
    while (text_sy == SY_PM_D || text_sy == SY_PM_R) {
        Value x;
        int sy = text_sy;
        next_sy();
        unary_expression(mode, &x);
        if (x.type == NULL || x.type_class != TYPE_CLASS_MEMBER_PTR) {
            error(ERR_INV_EXPRESSION, "Invalid type: pointer to member expected");
        }
        if (mode != MODE_SKIP) {
            ContextAddress obj = 0;
            ContextAddress ptr = 0;
            ContextAddress addr = 0;
            if (sy == SY_PM_D) {
                if (!v->remote) error(ERR_INV_EXPRESSION, "L-value expected");
                obj = v->address;
            }
            else {
                obj = (ContextAddress)to_uns(mode, v);
            }
            ptr = (ContextAddress)to_uns(mode, &x);
            evaluate_symbol_address(x.type, obj, ptr, &addr);
            set_ctx_word_value(v, addr);
            v->constant = 0;
            if (get_symbol_base_type(x.type, &v->type) < 0) {
                error(ERR_INV_EXPRESSION, "Cannot get pointed type");
            }
            if (get_symbol_type_class(x.type, &v->type_class) < 0) {
                error(ERR_INV_EXPRESSION, "Cannot get pointed type class");
            }
        }
    }
#endif
}

static void multiplicative_expression(int mode, Value * v) {
    pm_expression(mode, v);
    while (text_sy == '*' || text_sy == '/' || text_sy == '%') {
        Value x;
        int sy = text_sy;
        next_sy();
        pm_expression(mode, &x);
        if (mode != MODE_SKIP) {
            if (!is_number(v) || !is_number(&x)) {
                error(ERR_INV_EXPRESSION, "Numeric types expected");
            }
            if (mode == MODE_NORMAL && sy != '*' && to_int(mode, &x) == 0) {
                error(ERR_INV_EXPRESSION, "Dividing by zero");
            }
            if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
                double value = 0;
                if (mode == MODE_NORMAL) {
                    switch (sy) {
                    case '*': value = to_double(mode, v) * to_double(mode, &x); break;
                    case '/': value = to_double(mode, v) / to_double(mode, &x); break;
                    default: error(ERR_INV_EXPRESSION, "Invalid type");
                    }
                }
                v->type = NULL;
                v->type_class = TYPE_CLASS_REAL;
                set_fp_value(v, sizeof(double), value);
            }
            else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
                uint64_t value = 0;
                if (mode == MODE_NORMAL) {
                    switch (sy) {
                    case '*': value = to_uns(mode, v) * to_uns(mode, &x); break;
                    case '/': value = to_uns(mode, v) / to_uns(mode, &x); break;
                    case '%': value = to_uns(mode, v) % to_uns(mode, &x); break;
                    }
                }
                v->type = NULL;
                v->type_class = TYPE_CLASS_CARDINAL;
                set_int_value(v, sizeof(uint64_t), value);
            }
            else {
                int64_t value = 0;
                if (mode == MODE_NORMAL) {
                    switch (sy) {
                    case '*': value = to_int(mode, v) * to_int(mode, &x); break;
                    case '/': value = to_int(mode, v) / to_int(mode, &x); break;
                    case '%': value = to_int(mode, v) % to_int(mode, &x); break;
                    }
                }
                v->type = NULL;
                v->type_class = TYPE_CLASS_INTEGER;
                set_int_value(v, sizeof(int64_t), value);
            }
            v->constant = v->constant && x.constant;
        }
    }
}

static void additive_expression(int mode, Value * v) {
    multiplicative_expression(mode, v);
    while (text_sy == '+' || text_sy == '-') {
        Value x;
        int sy = text_sy;
        next_sy();
        multiplicative_expression(mode, &x);
        if (mode != MODE_SKIP) {
            if (sy == '+' && v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) {
                if (mode == MODE_TYPE) {
                    v->remote = 0;
                    v->size = 0;
                    v->value = tmp_alloc_zero((size_t)v->size);
                }
                else {
                    char * value;
                    load_value(v);
                    load_value(&x);
                    v->size = strlen((char *)v->value) + strlen((char *)x.value) + 1;
                    value = (char *)tmp_alloc((size_t)v->size);
                    strcpy(value, (const char *)(v->value));
                    strcat(value, (const char *)(x.value));
                    v->value = value;
                }
                v->type = NULL;
            }
#if ENABLE_Symbols
            else if ((v->type_class == TYPE_CLASS_POINTER || v->type_class == TYPE_CLASS_ARRAY) && is_number(&x)) {
                uint64_t value = 0;
                Symbol * base = NULL;
                ContextAddress size = 0;
                if (v->type == NULL || get_symbol_base_type(v->type, &base) < 0 ||
                    base == 0 || get_symbol_size(base, &size) < 0 || size == 0) {
                    error(ERR_INV_EXPRESSION, "Unknown pointer base type size");
                }
                switch (sy) {
                case '+': value = to_uns(mode, v) + to_uns(mode, &x) * size; break;
                case '-': value = to_uns(mode, v) - to_uns(mode, &x) * size; break;
                }
                if (v->type_class == TYPE_CLASS_ARRAY) {
                    if (get_array_symbol(base, 0, &v->type) < 0 ||
                        get_symbol_size(v->type, &v->size) < 0) {
                        error(errno, "Cannot cast to pointer");
                    }
                    v->type_class = TYPE_CLASS_POINTER;
                }
                set_int_value(v, (size_t)v->size, value);
            }
            else if (is_number(v) && (x.type_class == TYPE_CLASS_POINTER || x.type_class == TYPE_CLASS_ARRAY) && sy == '+') {
                uint64_t value = 0;
                Symbol * base = NULL;
                ContextAddress size = 0;
                if (x.type == NULL || get_symbol_base_type(x.type, &base) < 0 ||
                    base == 0 || get_symbol_size(base, &size) < 0 || size == 0) {
                    error(ERR_INV_EXPRESSION, "Unknown pointer base type size");
                }
                value = to_uns(mode, &x) + to_uns(mode, v) * size;
                v->type = x.type;
                if (x.type_class == TYPE_CLASS_ARRAY) {
                    if (get_array_symbol(base, 0, &v->type) < 0 ||
                        get_symbol_size(v->type, &v->size) < 0) {
                        error(errno, "Cannot cast to pointer");
                    }
                }
                v->type_class = TYPE_CLASS_POINTER;
                set_int_value(v, (size_t)x.size, value);
            }
#endif
            else if (!is_number(v) || !is_number(&x)) {
                error(ERR_INV_EXPRESSION, "Numeric types expected");
            }
            else if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
                double value = 0;
                switch (sy) {
                case '+': value = to_double(mode, v) + to_double(mode, &x); break;
                case '-': value = to_double(mode, v) - to_double(mode, &x); break;
                }
                v->type = NULL;
                v->type_class = TYPE_CLASS_REAL;
                set_fp_value(v, sizeof(double), value);
            }
            else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
                uint64_t value = 0;
                switch (sy) {
                case '+': value = to_uns(mode, v) + to_uns(mode, &x); break;
                case '-': value = to_uns(mode, v) - to_uns(mode, &x); break;
                }
                v->type = NULL;
                v->type_class = TYPE_CLASS_CARDINAL;
                set_int_value(v, sizeof(uint64_t), value);
            }
            else {
                int64_t value = 0;
                switch (sy) {
                case '+': value = to_int(mode, v) + to_int(mode, &x); break;
                case '-': value = to_int(mode, v) - to_int(mode, &x); break;
                }
                v->type = NULL;
                v->type_class = TYPE_CLASS_INTEGER;
                set_int_value(v, sizeof(int64_t), value);
            }
            v->constant = v->constant && x.constant;
        }
    }
}

static void shift_expression(int mode, Value * v) {
    additive_expression(mode, v);
    while (text_sy == SY_SHL || text_sy == SY_SHR) {
        Value x;
        int sy = text_sy;
        next_sy();
        additive_expression(mode, &x);
        if (mode != MODE_SKIP) {
            uint64_t value = 0;
            if (!is_whole_number(v) || !is_whole_number(&x)) {
                error(ERR_INV_EXPRESSION, "Integral types expected");
            }
            if (x.type_class != TYPE_CLASS_CARDINAL && to_int(mode, &x) < 0) {
                if (v->type_class == TYPE_CLASS_CARDINAL) {
                    switch (sy) {
                    case SY_SHL: value = to_uns(mode, v) >> -to_int(mode, &x); break;
                    case SY_SHR: value = to_uns(mode, v) << -to_int(mode, &x); break;
                    }
                }
                else {
                    switch (sy) {
                    case SY_SHL: value = to_int(mode, v) >> -to_int(mode, &x); break;
                    case SY_SHR: value = to_int(mode, v) << -to_int(mode, &x); break;
                    }
                    v->type_class = TYPE_CLASS_INTEGER;
                }
            }
            else {
                if (v->type_class == TYPE_CLASS_CARDINAL) {
                    switch (sy) {
                    case SY_SHL: value = to_uns(mode, v) << to_uns(mode, &x); break;
                    case SY_SHR: value = to_uns(mode, v) >> to_uns(mode, &x); break;
                    }
                }
                else {
                    switch (sy) {
                    case SY_SHL: value = to_int(mode, v) << to_uns(mode, &x); break;
                    case SY_SHR: value = to_int(mode, v) >> to_uns(mode, &x); break;
                    }
                    v->type_class = TYPE_CLASS_INTEGER;
                }
            }
            v->type = NULL;
            v->constant = v->constant && x.constant;
            set_int_value(v, sizeof(uint64_t), value);
        }
    }
}

static void relational_expression(int mode, Value * v) {
    shift_expression(mode, v);
    while (text_sy == '<' || text_sy == '>' || text_sy == SY_LEQ || text_sy == SY_GEQ) {
        Value x;
        int sy = text_sy;
        next_sy();
        shift_expression(mode, &x);
        if (mode != MODE_SKIP) {
            uint32_t value = 0;
            if (v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) {
                int n = 0;
                load_value(v);
                load_value(&x);
                n = strcmp((char *)v->value, (char *)x.value);
                switch (sy) {
                case '<': value = n < 0; break;
                case '>': value = n > 0; break;
                case SY_LEQ: value = n <= 0; break;
                case SY_GEQ: value = n >= 0; break;
                }
            }
            else if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
                switch (sy) {
                case '<': value = to_double(mode, v) < to_double(mode, &x); break;
                case '>': value = to_double(mode, v) > to_double(mode, &x); break;
                case SY_LEQ: value = to_double(mode, v) <= to_double(mode, &x); break;
                case SY_GEQ: value = to_double(mode, v) >= to_double(mode, &x); break;
                }
            }
            else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
                switch (sy) {
                case '<': value = to_uns(mode, v) < to_uns(mode, &x); break;
                case '>': value = to_uns(mode, v) > to_uns(mode, &x); break;
                case SY_LEQ: value = to_uns(mode, v) <= to_uns(mode, &x); break;
                case SY_GEQ: value = to_uns(mode, v) >= to_uns(mode, &x); break;
                }
            }
            else {
                switch (sy) {
                case '<': value = to_int(mode, v) < to_int(mode, &x); break;
                case '>': value = to_int(mode, v) > to_int(mode, &x); break;
                case SY_LEQ: value = to_int(mode, v) <= to_int(mode, &x); break;
                case SY_GEQ: value = to_int(mode, v) >= to_int(mode, &x); break;
                }
            }
            if (mode != MODE_NORMAL) value = 0;
            v->type_class = TYPE_CLASS_INTEGER;
            v->type = NULL;
            v->constant = v->constant && x.constant;
            set_int_value(v, sizeof(uint32_t), value);
        }
    }
}

static void equality_expression(int mode, Value * v) {
    relational_expression(mode, v);
    while (text_sy == SY_EQU || text_sy == SY_NEQ) {
        Value x;
        int sy = text_sy;
        next_sy();
        relational_expression(mode, &x);
        if (mode != MODE_SKIP) {
            uint32_t value = 0;
            if (v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) {
                load_value(v);
                load_value(&x);
                value = strcmp((char *)v->value, (char *)x.value) == 0;
            }
            else if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
                value = to_double(mode, v) == to_double(mode, &x);
            }
            else {
                value = to_int(mode, v) == to_int(mode, &x);
            }
            if (sy == SY_NEQ) value = !value;
            if (mode != MODE_NORMAL) value = 0;
            v->type_class = TYPE_CLASS_INTEGER;
            v->type = NULL;
            v->constant = v->constant && x.constant;
            set_int_value(v, sizeof(uint32_t), value);
        }
    }
}

static void and_expression(int mode, Value * v) {
    equality_expression(mode, v);
    while (text_sy == '&') {
        Value x;
        next_sy();
        equality_expression(mode, &x);
        if (mode != MODE_SKIP) {
            int64_t value = 0;
            if (!is_whole_number(v) || !is_whole_number(&x)) {
                error(ERR_INV_EXPRESSION, "Integral types expected");
            }
            if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
                v->type_class = TYPE_CLASS_CARDINAL;
                value = to_uns(mode, v) & to_uns(mode, &x);
            }
            else {
                v->type_class = TYPE_CLASS_INTEGER;
                value = to_int(mode, v) & to_int(mode, &x);
            }
            if (mode != MODE_NORMAL) value = 0;
            v->type = NULL;
            v->constant = v->constant && x.constant;
            set_int_value(v, sizeof(int64_t), value);
        }
    }
}

static void exclusive_or_expression(int mode, Value * v) {
    and_expression(mode, v);
    while (text_sy == '^') {
        Value x;
        next_sy();
        and_expression(mode, &x);
        if (mode != MODE_SKIP) {
            int64_t value = 0;
            if (!is_whole_number(v) || !is_whole_number(&x)) {
                error(ERR_INV_EXPRESSION, "Integral types expected");
            }
            if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
                v->type_class = TYPE_CLASS_CARDINAL;
                value = to_uns(mode, v) ^ to_uns(mode, &x);
            }
            else {
                v->type_class = TYPE_CLASS_INTEGER;
                value = to_int(mode, v) ^ to_int(mode, &x);
            }
            if (mode != MODE_NORMAL) value = 0;
            v->type = NULL;
            v->constant = v->constant && x.constant;
            set_int_value(v, sizeof(int64_t), value);
        }
    }
}

static void inclusive_or_expression(int mode, Value * v) {
    exclusive_or_expression(mode, v);
    while (text_sy == '|') {
        Value x;
        next_sy();
        exclusive_or_expression(mode, &x);
        if (mode != MODE_SKIP) {
            int64_t value = 0;
            if (!is_whole_number(v) || !is_whole_number(&x)) {
                error(ERR_INV_EXPRESSION, "Integral types expected");
            }
            if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
                v->type_class = TYPE_CLASS_CARDINAL;
                value = to_uns(mode, v) | to_uns(mode, &x);
            }
            else {
                v->type_class = TYPE_CLASS_INTEGER;
                value = to_int(mode, v) | to_int(mode, &x);
            }
            if (mode != MODE_NORMAL) value = 0;
            v->type = NULL;
            v->constant = v->constant && x.constant;
            set_int_value(v, sizeof(int64_t), value);
        }
    }
}

static void logical_and_expression(int mode, Value * v) {
    inclusive_or_expression(mode, v);
    while (text_sy == SY_AND) {
        Value x;
        int b = to_boolean(mode, v);
        next_sy();
        inclusive_or_expression(b ? mode : MODE_SKIP, &x);
        if (b) {
            if (!v->constant) x.constant = 0;
            *v = x;
        }
    }
}

static void logical_or_expression(int mode, Value * v) {
    logical_and_expression(mode, v);
    while (text_sy == SY_OR) {
        Value x;
        int b = to_boolean(mode, v);
        next_sy();
        logical_and_expression(!b ? mode : MODE_SKIP, &x);
        if (!b) {
            if (!v->constant) x.constant = 0;
            *v = x;
        }
    }
}

static void conditional_expression(int mode, Value * v) {
    logical_or_expression(mode, v);
    if (text_sy == '?') {
        Value x;
        Value y;
        int b = to_boolean(mode, v);
        next_sy();
        expression(b ? mode : MODE_SKIP, &x);
        if (text_sy != ':') error(ERR_INV_EXPRESSION, "Missing ':'");
        next_sy();
        conditional_expression(!b ? mode : MODE_SKIP, &y);
        if (!v->constant) x.constant = y.constant = 0;
        *v = b ? x : y;
    }
}

static void expression(int mode, Value * v) {
    /* TODO: assignments in expressions */
    conditional_expression(mode, v);
}

static int evaluate_type(Context * ctx, int frame, ContextAddress addr, char * s, Value * v) {
    Trap trap;

    expression_context = ctx;
    expression_frame = frame;
    expression_addr = addr;
    if (!set_trap(&trap)) return -1;
    text = s;
    text_pos = 0;
    text_len = strlen(s) + 1;
    next_ch();
    next_sy();
    expression(MODE_TYPE, v);
    if (text_sy != 0) error(ERR_INV_EXPRESSION, "Illegal characters at the end of expression");
    clear_trap(&trap);
    return 0;
}

int evaluate_expression(Context * ctx, int frame, ContextAddress addr, char * s, int load, Value * v) {
    Trap trap;

    expression_context = ctx;
    expression_frame = frame;
    expression_addr = addr;
    if (!set_trap(&trap)) return -1;
    if (s == NULL || *s == 0) str_exception(ERR_INV_EXPRESSION, "Empty expression");
    text = s;
    text_pos = 0;
    text_len = strlen(s) + 1;
    next_ch();
    next_sy();
    expression(MODE_NORMAL, v);
    if (text_sy != 0) error(ERR_INV_EXPRESSION, "Illegal characters at the end of expression");
    if (load) load_value(v);
    clear_trap(&trap);
    return 0;
}

int value_to_boolean(Value * v, int * res) {
    Trap trap;
    if (!set_trap(&trap)) return -1;
    *res = to_boolean(MODE_NORMAL, v);
    clear_trap(&trap);
    return 0;
}

int value_to_address(Value * v, ContextAddress * res) {
    Trap trap;
    if (!set_trap(&trap)) return -1;
    *res = (ContextAddress)to_uns(MODE_NORMAL, v);
    clear_trap(&trap);
    return 0;
}

int value_to_signed(Value * v, int64_t *res) {
    Trap trap;
    if (!set_trap(&trap)) return -1;
    *res = to_int(MODE_NORMAL, v);
    clear_trap(&trap);
    return 0;
}

int value_to_unsigned(Value * v, uint64_t *res) {
    Trap trap;
    if (!set_trap(&trap)) return -1;
    *res = to_uns(MODE_NORMAL, v);
    clear_trap(&trap);
    return 0;
}

int value_to_double(Value * v, double *res) {
    Trap trap;
    if (!set_trap(&trap)) return -1;
    *res = to_double(MODE_NORMAL, v);
    clear_trap(&trap);
    return 0;
}

/********************** Commands **************************/

typedef struct CommandArgs {
    char token[256];
    char id[256];
} CommandArgs;

typedef struct CommandCreateArgs {
    char token[256];
    char id[256];
    char language[256];
    char * script;
} CommandCreateArgs;

typedef struct CommandAssignArgs {
    char token[256];
    char id[256];
    char * value_buf;
    size_t value_size;
} CommandAssignArgs;

typedef struct Expression {
    LINK link_all;
    LINK link_id;
    char id[256];
    char var_id[256];
    char parent[256];
    char language[256];
    Channel * channel;
    char * script;
    int can_assign;
    ContextAddress size;
    int type_class;
    char type[256];
} Expression;

#define link_all2exp(A)  ((Expression *)((char *)(A) - offsetof(Expression, link_all)))
#define link_id2exp(A)   ((Expression *)((char *)(A) - offsetof(Expression, link_id)))

#define ID2EXP_HASH_SIZE (32 * MEM_USAGE_FACTOR - 1)

static LINK expressions = TCF_LIST_INIT(expressions);
static LINK id2exp[ID2EXP_HASH_SIZE];

#define MAX_SYM_NAME 1024

static const char * EXPRESSIONS = "Expressions";
static unsigned expr_id_cnt = 0;

#define expression_hash(id) ((unsigned)atoi(id + 4) % ID2EXP_HASH_SIZE)

static Expression * find_expression(char * id) {
    if (id[0] == 'E' && id[1] == 'X' && id[2] == 'P' && id[3] == 'R') {
        unsigned hash = expression_hash(id);
        LINK * l = id2exp[hash].next;
        while (l != &id2exp[hash]) {
            Expression * e = link_id2exp(l);
            l = l->next;
            if (strcmp(e->id, id) == 0) return e;
        }
    }
    return NULL;
}

static int symbol_to_expression(char * expr_id, char * parent, char * sym_id, Expression ** res) {
#if ENABLE_Symbols
    Symbol * sym = NULL;
    Symbol * type = NULL;
    int sym_class = 0;
    size_t script_len = strlen(sym_id) + 8;
    char * script = (char *)tmp_alloc(script_len);
    Expression * expr = (Expression *)tmp_alloc_zero(sizeof(Expression));

    strlcpy(expr->id, expr_id, sizeof(expr->id));
    strlcpy(expr->var_id, sym_id, sizeof(expr->var_id));
    strlcpy(expr->parent, parent, sizeof(expr->parent));

    if (id2symbol(sym_id, &sym) < 0) return -1;

    snprintf(script, script_len, "${%s}", sym_id);
    expr->script = script;

    get_symbol_type_class(sym, &expr->type_class);
    get_symbol_size(sym, &expr->size);

    if (get_symbol_class(sym, &sym_class) == 0) {
        expr->can_assign = sym_class == SYM_CLASS_REFERENCE;
    }

    if (get_symbol_type(sym, &type) == 0 && type != NULL) {
        strlcpy(expr->type, symbol2id(type), sizeof(expr->type));
    }

    *res = expr;
    return 0;
#else
    errno = ERR_UNSUPPORTED;
    return -1;
#endif
}

static int expression_context_id(char * id, Context ** ctx, int * frame, Expression ** expr) {
    int err = 0;
    Expression * e = NULL;

    if (id[0] == 'S') {
        char parent[256];
        char * s = id + 1;
        size_t i = 0;
        while (*s && i < sizeof(parent) - 1) {
            char ch = *s++;
            if (ch == '.') {
                if (*s == '.') {
                    parent[i++] = *s++;
                    continue;
                }
                break;
            }
            parent[i++] = ch;
        }
        parent[i] = 0;
        if (symbol_to_expression(id, parent, s, &e) < 0) err = errno;
    }
    else if ((e = find_expression(id)) == NULL) {
        err = ERR_INV_CONTEXT;
    }

    if (!err) {
        if ((*ctx = id2ctx(e->parent)) != NULL) {
            *frame = context_has_state(*ctx) ? STACK_TOP_FRAME : STACK_NO_FRAME;
        }
        else if (id2frame(e->parent, ctx, frame) < 0) {
            err = errno;
        }
    }

    if (err) {
        errno = err;
        return -1;
    }

    *expr = e;
    return 0;
}

static void write_context(OutputStream * out, Expression * expr) {
    write_stream(out, '{');
    json_write_string(out, "ID");
    write_stream(out, ':');
    json_write_string(out, expr->id);

    write_stream(out, ',');

    json_write_string(out, "ParentID");
    write_stream(out, ':');
    json_write_string(out, expr->parent);

    if (expr->var_id[0]) {
        write_stream(out, ',');

        json_write_string(out, "SymbolID");
        write_stream(out, ':');
        json_write_string(out, expr->var_id);
    }

    write_stream(out, ',');

    json_write_string(out, "Expression");
    write_stream(out, ':');
    json_write_string(out, expr->script);

    write_stream(out, ',');

    json_write_string(out, "CanAssign");
    write_stream(out, ':');
    json_write_boolean(out, expr->can_assign);

    if (expr->type_class != TYPE_CLASS_UNKNOWN) {
        write_stream(out, ',');

        json_write_string(out, "Class");
        write_stream(out, ':');
        json_write_long(out, expr->type_class);
    }

    if (expr->type[0]) {
        write_stream(out, ',');

        json_write_string(out, "Type");
        write_stream(out, ':');
        json_write_string(out, expr->type);
    }

    write_stream(out, ',');

    json_write_string(out, "Size");
    write_stream(out, ':');
    json_write_uint64(out, expr->size);

    write_stream(out, '}');
}

static void get_context_cache_client(void * x) {
    CommandArgs * args = (CommandArgs *)x;
    Channel * c = cache_channel();
    Context * ctx = NULL;
    int frame = STACK_NO_FRAME;
    Expression * expr = NULL;
    int err = 0;

    if (expression_context_id(args->id, &ctx, &frame, &expr) < 0) err = errno;

    cache_exit();

    write_stringz(&c->out, "R");
    write_stringz(&c->out, args->token);
    write_errno(&c->out, err);

    if (err) {
        write_stringz(&c->out, "null");
    }
    else {
        write_context(&c->out, expr);
        write_stream(&c->out, 0);
    }

    write_stream(&c->out, MARKER_EOM);
}

static void command_get_context(char * token, Channel * c) {
    CommandArgs args;

    json_read_string(&c->inp, args.id, sizeof(args.id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    strlcpy(args.token, token, sizeof(args.token));
    cache_enter(get_context_cache_client, c, &args, sizeof(args));
}

#if ENABLE_Symbols

static int sym_cnt = 0;
static int sym_max = 0;
static Symbol ** sym_buf = NULL;

static void get_children_callback(void * x, Symbol * symbol) {
    if (sym_cnt >= sym_max) {
        sym_max += 8;
        sym_buf = (Symbol **)loc_realloc(sym_buf, sizeof(Symbol *) * sym_max);
    }
    sym_buf[sym_cnt++] = symbol;
}

#endif

static void get_children_cache_client(void * x) {
    CommandArgs * args = (CommandArgs *)x;
    Channel * c = cache_channel();
    int err = 0;

    /* TODO: Expressions.getChildren - structures */
#if ENABLE_Symbols
    char parent_id[256];
    {
        Context * ctx;
        int frame = STACK_NO_FRAME;

        sym_cnt = 0;

        if ((ctx = id2ctx(args->id)) != NULL && context_has_state(ctx)) {
            frame = get_top_frame(ctx);
            strlcpy(parent_id, frame2id(ctx, frame), sizeof(parent_id));
        }
        else if (id2frame(args->id, &ctx, &frame) == 0) {
            strlcpy(parent_id, args->id, sizeof(parent_id));
        }
        else {
            ctx = NULL;
        }

        if (ctx != NULL && err == 0 && enumerate_symbols(
                ctx, frame, get_children_callback, &args) < 0) err = errno;
    }
#else
    err = ERR_UNSUPPORTED;
#endif

    cache_exit();

    write_stringz(&c->out, "R");
    write_stringz(&c->out, args->token);

    write_errno(&c->out, err);

    write_stream(&c->out, '[');
#if ENABLE_Symbols
    {
        int i;
        for (i = 0; i < sym_cnt; i++) {
            const char * s = parent_id;
            if (i > 0) write_stream(&c->out, ',');
            write_stream(&c->out, '"');
            write_stream(&c->out, 'S');
            while (*s) {
                if (*s == '.') write_stream(&c->out, '.');
                json_write_char(&c->out, *s++);
            }
            write_stream(&c->out, '.');
            s = symbol2id(sym_buf[i]);
            while (*s) json_write_char(&c->out, *s++);
            write_stream(&c->out, '"');
        }
    }
#endif
    write_stream(&c->out, ']');
    write_stream(&c->out, 0);

    write_stream(&c->out, MARKER_EOM);
}

static void command_get_children(char * token, Channel * c) {
    CommandArgs args;

    json_read_string(&c->inp, args.id, sizeof(args.id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    strlcpy(args.token, token, sizeof(args.token));
    cache_enter(get_children_cache_client, c, &args, sizeof(args));
}

static void command_create_cache_client(void * x) {
    CommandCreateArgs * args = (CommandCreateArgs *)x;
    Expression * e;
    Expression buf;
    Channel * c = cache_channel();
    int frame = STACK_NO_FRAME;
    int err = 0;

    memset(e = &buf, 0, sizeof(buf));
    do snprintf(e->id, sizeof(e->id), "EXPR%d", expr_id_cnt++);
    while (find_expression(e->id) != NULL);
    strlcpy(e->parent, args->id, sizeof(e->parent));
    strlcpy(e->language, args->language, sizeof(e->language));
    e->channel = c;
    e->script = args->script;

    if (!err) {
        Value value;
        Context * ctx = NULL;
        memset(&value, 0, sizeof(value));
        if ((ctx = id2ctx(e->parent)) != NULL) {
            frame = context_has_state(ctx) ? STACK_TOP_FRAME : STACK_NO_FRAME;
        }
        else if (id2frame(e->parent, &ctx, &frame) < 0) {
            err = errno;
        }
        if (!err && evaluate_type(ctx, frame, 0, e->script, &value) < 0) err = errno;
        if (!err) {
            e->can_assign = value.remote;
            e->type_class = value.type_class;
            e->size = value.size;
#if ENABLE_Symbols
            if (value.type != NULL) strlcpy(e->type, symbol2id(value.type), sizeof(e->type));
#endif
        }
    }

    cache_exit();

    write_stringz(&c->out, "R");
    write_stringz(&c->out, args->token);
    write_errno(&c->out, err);

    if (err) {
        write_stringz(&c->out, "null");
    }
    else {
        *(e = (Expression *)loc_alloc(sizeof(Expression))) = buf;
        list_add_last(&e->link_all, &expressions);
        list_add_last(&e->link_id, id2exp + expression_hash(e->id));
        write_context(&c->out, e);
        write_stream(&c->out, 0);
    }

    write_stream(&c->out, MARKER_EOM);
}

static void command_create(char * token, Channel * c) {
    CommandCreateArgs args;

    json_read_string(&c->inp, args.id, sizeof(args.id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    json_read_string(&c->inp, args.language, sizeof(args.language));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    args.script = json_read_alloc_string(&c->inp);
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    strlcpy(args.token, token, sizeof(args.token));
    cache_enter(command_create_cache_client, c, &args, sizeof(args));
}

static void command_evaluate_cache_client(void * x) {
    CommandCreateArgs * args = (CommandCreateArgs *)x;
    Channel * c = cache_channel();
    Context * ctx = NULL;
    int frame = STACK_NO_FRAME;
    Expression * expr = NULL;
    int value_ok = 0;
    Value value;
    int err = 0;

    memset(&value, 0, sizeof(value));
    if (expression_context_id(args->id, &ctx, &frame, &expr) < 0) err = errno;
    if (!err && frame != STACK_NO_FRAME && !ctx->stopped) err = ERR_IS_RUNNING;
    if (!err && evaluate_expression(ctx, frame, 0, expr->script, 0, &value) < 0) err = errno;
    if (value.size >= 0x100000) err = ERR_BUFFER_OVERFLOW;

    cache_exit();

    write_stringz(&c->out, "R");
    write_stringz(&c->out, args->token);
    if (err) {
        write_stringz(&c->out, "null");
    }
    else {
        JsonWriteBinaryState state;

        value_ok = 1;
        json_write_binary_start(&state, &c->out, (size_t)value.size);
        if (!value.remote) {
            json_write_binary_data(&state, value.value, (size_t)value.size);
        }
        else {
            char buf[256];
            size_t offs = 0;
            while (offs < (size_t)value.size) {
                int size = (size_t)value.size - offs;
                if (size > (int)sizeof(buf)) size = (int)sizeof(buf);
                memset(buf, 0, size);
                if (!err && context_read_mem(ctx, value.address + offs, buf, size) < 0)
                    err = set_errno(errno, "Cannot read target memory");
                json_write_binary_data(&state, buf, size);
                offs += size;
            }
        }
        json_write_binary_end(&state);
        write_stream(&c->out, 0);
    }
    write_errno(&c->out, err);
    if (!value_ok) {
        write_stringz(&c->out, "null");
    }
    else {
        int cnt = 0;
        write_stream(&c->out, '{');

        if (value.type_class != TYPE_CLASS_UNKNOWN) {
            json_write_string(&c->out, "Class");
            write_stream(&c->out, ':');
            json_write_long(&c->out, value.type_class);
            cnt++;
        }

#if ENABLE_Symbols
        if (value.type != NULL) {
            if (cnt > 0) write_stream(&c->out, ',');
            json_write_string(&c->out, "Type");
            write_stream(&c->out, ':');
            json_write_string(&c->out, symbol2id(value.type));
            cnt++;
        }

        if (value.sym != NULL) {
            if (cnt > 0) write_stream(&c->out, ',');
            json_write_string(&c->out, "Symbol");
            write_stream(&c->out, ':');
            json_write_string(&c->out, symbol2id(value.sym));
            cnt++;
        }
#endif
        if (value.reg != NULL) {
            if (cnt > 0) write_stream(&c->out, ',');
            json_write_string(&c->out, "Register");
            write_stream(&c->out, ':');
            json_write_string(&c->out, register2id(ctx, frame, value.reg));
            cnt++;
        }

        if (value.remote) {
            if (cnt > 0) write_stream(&c->out, ',');
            json_write_string(&c->out, "Address");
            write_stream(&c->out, ':');
            json_write_uint64(&c->out, value.address);
            cnt++;
        }

        if (value.big_endian) {
            if (cnt > 0) write_stream(&c->out, ',');
            json_write_string(&c->out, "BigEndian");
            write_stream(&c->out, ':');
            json_write_boolean(&c->out, 1);
            cnt++;
        }

        write_stream(&c->out, '}');
        write_stream(&c->out, 0);
    }
    write_stream(&c->out, MARKER_EOM);
}

static void command_evaluate(char * token, Channel * c) {
    CommandArgs args;

    json_read_string(&c->inp, args.id, sizeof(args.id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    strlcpy(args.token, token, sizeof(args.token));
    cache_enter(command_evaluate_cache_client, c, &args, sizeof(args));
}

static void command_assign_cache_client(void * x) {
    CommandAssignArgs * args = (CommandAssignArgs *)x;
    Channel * c = cache_channel();
    Context * ctx = NULL;
    int frame = STACK_NO_FRAME;
    Expression * expr = NULL;
    Value value;
    int err = 0;

    memset(&value, 0, sizeof(value));
    if (expression_context_id(args->id, &ctx, &frame, &expr) < 0) err = errno;
    if (!err && frame != STACK_NO_FRAME && !ctx->stopped) err = ERR_IS_RUNNING;
    if (!err && evaluate_expression(ctx, frame, 0, expr->script, 0, &value) < 0) err = errno;
    if (!err) {
        if (value.reg != NULL) {
            StackFrame * info = NULL;
            if (get_frame_info(ctx, frame, &info) < 0) err = errno;
            if (!err && write_reg_bytes(info, value.reg, 0, args->value_size, (uint8_t *)args->value_buf) < 0) err = errno;
#if SERVICE_Registers
            if (!err) send_event_register_changed(register2id(ctx, frame, value.reg));
#endif
        }
        else if (value.remote) {
            if (context_write_mem(ctx, value.address, args->value_buf, args->value_size) < 0) err = errno;
#if SERVICE_Memory
            if (!err) send_event_memory_changed(ctx, value.address, args->value_size);
#endif
        }
        else {
            err = ERR_INV_EXPRESSION;
        }
    }

    cache_exit();

    write_stringz(&c->out, "R");
    write_stringz(&c->out, args->token);
    write_errno(&c->out, err);
    write_stream(&c->out, MARKER_EOM);
    loc_free(args->value_buf);
}

static void command_assign(char * token, Channel * c) {
    CommandAssignArgs args;

    json_read_string(&c->inp, args.id, sizeof(args.id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    args.value_buf = json_read_alloc_binary(&c->inp, &args.value_size);
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    strlcpy(args.token, token, sizeof(args.token));
    cache_enter(command_assign_cache_client, c, &args, sizeof(args));
}

static void command_dispose(char * token, Channel * c) {
    char id[256];
    int err = 0;
    Expression * e;

    json_read_string(&c->inp, id, sizeof(id));
    if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
    if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);

    e = find_expression(id);
    if (e != NULL) {
        list_remove(&e->link_all);
        list_remove(&e->link_id);
        loc_free(e->script);
        loc_free(e);
    }
    else {
        err = ERR_INV_CONTEXT;
    }

    write_stringz(&c->out, "R");
    write_stringz(&c->out, token);
    write_errno(&c->out, err);
    write_stream(&c->out, MARKER_EOM);
}

static void on_channel_close(Channel * c) {
    LINK * l = expressions.next;
    while (l != &expressions) {
        Expression * e = link_all2exp(l);
        l = l->next;
        if (e->channel == c) {
            list_remove(&e->link_all);
            list_remove(&e->link_id);
            loc_free(e->script);
            loc_free(e);
        }
    }
}

void add_identifier_callback(ExpressionIdentifierCallBack * callback) {
    assert(id_callback_cnt < MAX_ID_CALLBACKS);
    id_callbacks[id_callback_cnt++] = callback;
}

void ini_expressions_service(Protocol * proto) {
    unsigned i;
    for (i = 0; i < ID2EXP_HASH_SIZE; i++) list_init(id2exp + i);
    add_channel_close_listener(on_channel_close);
    add_command_handler(proto, EXPRESSIONS, "getContext", command_get_context);
    add_command_handler(proto, EXPRESSIONS, "getChildren", command_get_children);
    add_command_handler(proto, EXPRESSIONS, "create", command_create);
    add_command_handler(proto, EXPRESSIONS, "evaluate", command_evaluate);
    add_command_handler(proto, EXPRESSIONS, "assign", command_assign);
    add_command_handler(proto, EXPRESSIONS, "dispose", command_dispose);

    big_endian = big_endian_host();
}

#endif  /* if SERVICE_Expressions */

Back to the top