1 #!/usr/bin/env python 2 3 """ 4 A really simple virtual processor employing a simple set of instructions which 5 ignore low-level operations and merely concentrate on variable access, structure 6 access, structure allocation and function invocations. 7 8 Copyright (C) 2007, 2008, 2009, 2010 Paul Boddie <paul@boddie.org.uk> 9 10 This program is free software; you can redistribute it and/or modify it under 11 the terms of the GNU General Public License as published by the Free Software 12 Foundation; either version 3 of the License, or (at your option) any later 13 version. 14 15 This program is distributed in the hope that it will be useful, but WITHOUT 16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 18 details. 19 20 You should have received a copy of the GNU General Public License along with 21 this program. If not, see <http://www.gnu.org/licenses/>. 22 23 -------- 24 25 The execution model of the virtual processor involves the following things: 26 27 * Memory contains constants, global variable 28 references and program code 29 30 * PC (program counter) stack contains the return address associated 31 with each function invocation 32 33 * Frame stack contains invocation frames in use and in 34 preparation plus temporary storage 35 36 * Local frame pointer stack refers to the frames in the frame stack 37 38 * Invocation frame pointer stack 39 40 * Exception handler stack 41 42 * Exception handler locals stack refers to the state of the local frame 43 pointer stack 44 45 * Exception handler PC stack refers to the state of the PC stack 46 47 * Registers: current value, 48 boolean status value, 49 source value, 50 current result, 51 current exception, 52 current callable 53 """ 54 55 from micropython.program import DataValue, ReplaceableContext, PlaceholderContext, FragmentObject 56 from rsvplib import Library 57 58 class IllegalInstruction(Exception): 59 pass 60 61 class IllegalAddress(Exception): 62 def __init__(self, address): 63 self.address = address 64 def __repr__(self): 65 return "IllegalAddress(%r)" % self.address 66 def __str__(self): 67 return repr(self) 68 69 class EmptyPCStack(Exception): 70 pass 71 72 class EmptyFrameStack(Exception): 73 pass 74 75 class BreakpointReached(Exception): 76 pass 77 78 class RSVPMachine: 79 80 "A really simple virtual processor." 81 82 def __init__(self, memory, objlist, paramlist, pc=None, debug=0, abort_upon_exception=0): 83 84 """ 85 Initialise the processor with a 'memory' (a list of values containing 86 instructions and data), the object and parameter lists 'objlist' and 87 'paramlist', and the optional program counter 'pc'. 88 """ 89 90 self.memory = memory 91 self._objlist = objlist 92 self._paramlist = paramlist 93 self.objlist = objlist.as_raw() 94 self.paramlist = paramlist.as_raw() 95 self.library = None 96 97 self.pc = pc or 0 98 self.debug = debug 99 self.abort_upon_exception = abort_upon_exception 100 101 # Profiling. 102 103 self.counter = 0 104 self.cost = 0 105 106 # Stacks. 107 108 self.pc_stack = [] 109 self.frame_stack = [] 110 self.local_sp_stack = [0] 111 self.invocation_sp_stack = [] 112 self.handler_stack = [len(self.memory) - 1] # final handler is the end of the code 113 self.handler_local_sp_stack = [] 114 self.handler_pc_stack = [] 115 116 # Registers. 117 118 self.instruction = None 119 self.operand = None 120 self.value = None 121 self.status = None 122 self.source = None 123 self.callable = None 124 self.result = None 125 self.exception = None 126 127 # Constants. 128 129 cls = self._get_class("__builtins__", "AttributeError") 130 self.attr_error = cls.location 131 self.attr_error_instance = cls.instance_template_location 132 cls = self._get_class("__builtins__", "TypeError") 133 self.type_error = cls.location 134 self.type_error_instance = cls.instance_template_location 135 cls = self._get_class("__builtins__", "tuple") 136 self.tuple_class = cls.location 137 self.tuple_instance = cls.instance_template_location 138 139 # Debugging attributes. 140 141 self.breakpoints = set() 142 143 def _get_class(self, module, name): 144 return self._objlist.access(module, name).get_value() 145 146 # Debugging methods. 147 148 def dump(self): 149 print "PC", self.pc, "->", self.load(self.pc) 150 print "PC stack", self.pc_stack 151 print "Frame stack", self.frame_stack 152 print "Local stack pointers", self.local_sp_stack 153 print "Invocation stack pointers", self.invocation_sp_stack 154 print "Handler stack", self.handler_stack 155 print "Handler frame stack", self.handler_local_sp_stack 156 print "Handler PC stack", self.handler_pc_stack 157 print 158 print "Instruction", self.instruction 159 print "Operand", self.operand 160 print "Value", self.value 161 print "Status", self.status 162 print "Source", self.source 163 print "Callable", self.callable 164 print "Result", self.result 165 print "Exception", self.exception 166 167 def show(self): 168 self.show_memory(self.memory, 0) 169 170 def show_pc(self, run_in=10): 171 start = max(0, self.pc - run_in) 172 end = self.pc + run_in 173 memory = self.memory[start:end] 174 self.show_memory(memory, start) 175 176 def show_memory(self, memory, start): 177 for i, x in enumerate(memory): 178 location = start + i 179 if location == self.pc: 180 print "->", 181 else: 182 print " ", 183 print "%5d %r" % (location, x) 184 185 def step(self, dump=0): 186 self.execute() 187 self.show_pc() 188 if dump: 189 self.dump() 190 191 def set_break(self, location): 192 self.breakpoints.add(location) 193 194 # Internal operations. 195 196 def load(self, address): 197 198 "Return the value at the given 'address'." 199 200 try: 201 return self.memory[address] 202 except IndexError: 203 raise IllegalAddress(address) 204 except TypeError: 205 raise IllegalAddress(address) 206 207 def save(self, address, value): 208 209 "Save to the given 'address' the specified 'value'." 210 211 try: 212 self.memory[address] = value 213 except IndexError: 214 raise IllegalAddress(address) 215 except TypeError: 216 raise IllegalAddress(address) 217 218 def new(self, size): 219 220 """ 221 Allocate space of the given 'size', returning the address of the space. 222 """ 223 224 addr = len(self.memory) 225 for i in range(0, size): 226 self.memory.append(None) 227 return addr 228 229 def push_pc(self, data): 230 231 "Push 'data' onto the PC stack." 232 233 self.pc_stack.append(data) 234 235 def pull_pc(self): 236 237 "Pull a value from the PC stack and return it." 238 239 try: 240 return self.pc_stack.pop() 241 except IndexError: 242 raise EmptyPCStack 243 244 def run(self): 245 246 "Execute code in the memory, starting from the current PC address." 247 248 breakpoint = 0 249 250 try: 251 while 1: 252 self.execute() 253 except EmptyPCStack: 254 pass 255 except BreakpointReached: 256 breakpoint = 1 257 258 print "Execution terminated", 259 if self.exception is not None: 260 ref = self.exception 261 addr = self.load(ref + 1) 262 print "with exception:", self.load(ref) 263 print "At address %d: %r" % (addr, self.load(addr)) 264 elif breakpoint: 265 print "with breakpoint." 266 print "At address", self.pc 267 else: 268 print "successfully." 269 print "After", self.counter, "instructions at cost", self.cost, "units." 270 271 def test(self, module): 272 273 """ 274 Test the code in the memory by running the code and investigating the 275 contents of variables. Use 'module' to identify result variables. 276 """ 277 278 self.run() 279 success = 1 280 281 if self.exception is None: 282 for name in module.keys(): 283 if name.startswith("result"): 284 label, expected = name.split("_") 285 attr = module[name] 286 287 # NOTE: Assumptions about headers and content made. 288 289 attr_location = module.location + 1 + attr.position 290 value = self.load(attr_location) 291 292 if value is not None: 293 content = self.load(value.ref + 1) 294 print label, expected, content 295 success = success and (int(expected) == content) 296 else: 297 print label, expected, "missing" 298 success = 0 299 300 return success 301 else: 302 return 0 303 304 def execute(self): 305 306 "Execute code in the memory at the current PC address." 307 308 if self.pc in self.breakpoints: 309 self.breakpoints.remove(self.pc) 310 raise BreakpointReached 311 312 self.instruction = self.load(self.pc) 313 314 # Process any inputs of the instruction. 315 316 self.process_inputs() 317 318 # Perform the instruction itself. 319 320 next_pc = self.perform(self.instruction) 321 322 # Update the program counter. 323 324 if next_pc is None: 325 self.pc += 1 326 else: 327 self.pc = next_pc 328 329 def get_method(self, instruction): 330 331 "Return the handler method for the given 'instruction'." 332 333 instruction_name = instruction.__class__.__name__ 334 if self.debug: 335 print "%8d %s" % (self.pc, instruction_name) 336 method = getattr(self, instruction_name, None) 337 if method is None: 338 raise IllegalInstruction, (self.pc, instruction_name) 339 return method 340 341 def perform(self, instruction, is_input=0): 342 343 "Perform the 'instruction', returning the next PC value or None." 344 345 if not is_input: 346 self.counter += 1 347 self.cost += instruction.cost 348 self.operand = instruction.get_operand() 349 method = self.get_method(instruction) 350 return method() 351 352 def process_inputs(self): 353 354 """ 355 Process any inputs of the current instruction. This permits any directly 356 connected sub-instructions to produce the effects that separate 357 instructions would otherwise have. 358 """ 359 360 value = self.value 361 if self.instruction.source is not None: 362 self.perform(self.instruction.source, 1) 363 self.source = self.value 364 self.value = value 365 if self.instruction.input is not None: 366 self.perform(self.instruction.input, 1) 367 368 def jump(self, addr, next): 369 370 """ 371 Jump to the subroutine at (or identified by) 'addr'. If 'addr' 372 identifies a library function then invoke the library function and set 373 PC to 'next' afterwards; otherwise, set PC to 'addr'. 374 """ 375 376 # Trap library functions introduced through the use of strings instead 377 # of proper locations. 378 379 if isinstance(addr, str): 380 handler = self.library and self.library.native_functions[addr](self.library) 381 if handler is None: 382 return next 383 else: 384 return handler 385 else: 386 self.push_pc(self.pc + 1) 387 return addr 388 389 # Instructions. 390 391 def LoadConst(self): 392 self.value = DataValue(self.operand, self.operand) 393 394 def LoadClass(self): 395 self.value = DataValue(PlaceholderContext, self.operand) 396 397 def LoadFunction(self): 398 self.value = DataValue(ReplaceableContext, self.operand) 399 400 def LoadName(self): 401 frame = self.local_sp_stack[-1] 402 self.value = self.frame_stack[frame + self.operand] 403 404 def StoreName(self): 405 frame = self.local_sp_stack[-1] 406 self.frame_stack[frame + self.operand] = self.source # uses the source value 407 408 LoadTemp = LoadName 409 410 def StoreTemp(self): 411 frame = self.local_sp_stack[-1] 412 self.frame_stack[frame + self.operand] = self.value 413 414 def LoadAddress(self): 415 # Preserve context (potentially null). 416 self.value = self.load(self.operand) 417 418 def LoadAddressContext(self): 419 value = self.load(self.operand) 420 inst_value = self.value 421 self.value = DataValue(inst_value.ref, value.ref) 422 423 def LoadAddressContextCond(self): 424 value = self.load(self.operand) 425 inst_value = self.value 426 self.value = self._LoadAddressContextCond(value.context, value.ref, inst_value.ref) 427 428 def StoreAddress(self): 429 # Preserve context. 430 self.save(self.operand, self.source) 431 432 def StoreAddressContext(self): 433 # Overwrite context if null. 434 context_value = self.value 435 source_value = self.source 436 if source_value.context is ReplaceableContext: 437 context = context_value.ref 438 else: 439 context = source_value.context 440 self.save(self.operand, DataValue(context, source_value.ref)) 441 442 def MakeInstance(self): 443 size = self.operand 444 value = self.value 445 # NOTE: Referencing the instance template. 446 addr = self._MakeObject(size, value.ref - 1) 447 # Introduce object as context for the new object. 448 self.value = DataValue(addr, addr) 449 450 def MakeFragment(self): 451 size = self.operand 452 addr = self._MakeFragment(size) 453 # NOTE: Context is not relevant for fragments. 454 self.value = DataValue(None, addr) 455 456 def LoadAttr(self): 457 value = self.value 458 # Retrieved context should already be appropriate for the instance. 459 # NOTE: Adding 1 to skip any header. 460 self.value = self.load(value.ref + self.operand + 1) 461 462 def StoreAttr(self): 463 value = self.value 464 # Target should already be an instance. 465 # NOTE: Adding 1 to skip any header. 466 self.save(value.ref + self.operand + 1, self.source) 467 468 def LoadAttrIndex(self): 469 value = self.value 470 data = self.load(value.ref) 471 element = self.objlist[data.classcode + self.operand] 472 473 if element is not None: 474 attr_index, static_attr, offset = element 475 if attr_index == self.operand: 476 if static_attr: 477 self.value = self.load(offset) # offset is address of class/module attribute 478 else: 479 self.value = self.load(value.ref + offset) 480 return 481 482 self.exception = self._MakeObject(2, self.attr_error_instance) 483 return self.RaiseException() 484 485 # LoadAttrIndexContext not defined. 486 487 def LoadAttrIndexContextCond(self): 488 inst_value = self.value 489 data = self.load(inst_value.ref) 490 element = self.objlist[data.classcode + self.operand] 491 492 if element is not None: 493 attr_index, static_attr, offset = element 494 if attr_index == self.operand: 495 if static_attr: 496 loaded_value = self.load(offset) # offset is address of class/module attribute 497 if data.attrcode is None: # absent attrcode == class/module 498 self.value = loaded_value 499 else: 500 self.value = self._LoadAddressContextCond(loaded_value.context, loaded_value.ref, inst_value.ref) 501 else: 502 self.value = self.load(inst_value.ref + offset) 503 return 504 505 self.exception = self._MakeObject(2, self.attr_error_instance) 506 return self.RaiseException() 507 508 def StoreAttrIndex(self): 509 value = self.value 510 data = self.load(value.ref) 511 element = self.objlist[data.classcode + self.operand] 512 513 if element is not None: 514 attr_index, static_attr, offset = element 515 if attr_index == self.operand: 516 if static_attr: 517 self.exception = self._MakeObject(2, self.type_error_instance) 518 return self.RaiseException() 519 else: 520 self.save(value.ref + offset, self.source) 521 return 522 523 self.exception = self._MakeObject(2, self.attr_error_instance) 524 return self.RaiseException() 525 526 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 527 528 def MakeFrame(self): 529 self.invocation_sp_stack.append(len(self.frame_stack)) 530 self.frame_stack.extend([None] * self.operand) 531 532 def DropFrame(self): 533 self.local_sp_stack.pop() 534 frame = self.invocation_sp_stack.pop() 535 del self.frame_stack[frame:] # reset stack before call 536 537 def StoreFrame(self): 538 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 539 self.frame_stack[frame + self.operand] = self.value 540 541 def StoreFrameIndex(self): 542 value = self.value 543 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 544 data = self.load(value.ref) 545 element = self.paramlist[data.funccode + self.operand] 546 547 if element is not None: 548 # NOTE: Need to ensure correct positioning where a context has been generated. 549 param_index, offset = element 550 if param_index == self.operand: 551 self.frame_stack[frame + offset] = self.source 552 return 553 554 self.exception = self._MakeObject(2, self.type_error_instance) 555 return self.RaiseException() 556 557 def LoadCallable(self): 558 value = self.value 559 data = self.load(value.ref) 560 self.callable = data.codeaddr 561 562 def StoreCallable(self): 563 value = self.value 564 # NOTE: Should improve the representation and permit direct saving. 565 data = self.load(value.ref) 566 self.save(value.ref, data.with_callable(self.callable)) 567 568 def LoadContext(self): 569 value = self.value 570 # NOTE: Omission of the context of the context would make things like 571 # NOTE: self() inside methods impossible. 572 self.value = DataValue(value.context, value.context) 573 574 def CheckContext(self): 575 self.status = self.value.ref is not ReplaceableContext 576 577 def CheckClass(self): 578 value = self.value 579 if value.ref in (ReplaceableContext, PlaceholderContext): 580 self.status = 0 581 return 582 583 data = self.load(value.ref) 584 585 # Classes are not themselves usable as the self argument. 586 # NOTE: This may change at some point. 587 # However, where classes appear as the context, instance 588 # compatibility is required in the first argument. 589 590 self.status = data.attrcode is None # absent attrcode == class 591 592 def CheckFrame(self): 593 (nargs, ndefaults) = self.operand 594 595 # The frame is actually installed as the locals. 596 # Retrieve the context from the first local. 597 598 frame = self.local_sp_stack[-1] 599 nlocals = len(self.frame_stack[frame:]) 600 601 if not ((nargs - ndefaults) <= nlocals): 602 raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 603 self.exception = self._MakeObject(2, self.type_error_instance) 604 return self.RaiseException() 605 606 def CheckExtra(self): 607 nargs = self.operand 608 609 # The frame is actually installed as the locals. 610 # Retrieve the context from the first local. 611 612 frame = self.local_sp_stack[-1] 613 nlocals = len(self.frame_stack[frame:]) 614 615 # Provide the extra star parameter if necessary. 616 617 if nlocals == nargs: 618 self.frame_stack.extend([None]) # ExtendFrame(1) 619 620 def FillDefaults(self): 621 value = self.value 622 (nargs, ndefaults) = self.operand 623 624 # The frame is actually installed as the locals. 625 626 frame = self.local_sp_stack[-1] 627 nlocals = len(self.frame_stack[frame:]) 628 629 # Support population of defaults. 630 # This involves copying the "attributes" of a function into the frame. 631 632 default = nlocals - (nargs - ndefaults) 633 self.frame_stack.extend([None] * (nargs - nlocals)) 634 pos = nlocals 635 636 while pos < nargs: 637 self.frame_stack[frame + pos] = self.load(value.ref + default + 1) # skip header 638 default += 1 639 pos += 1 640 641 def CopyExtra(self): 642 start = self.operand 643 644 # The frame is the source of the extra arguments. 645 646 frame = self.local_sp_stack[-1] 647 nlocals = len(self.frame_stack[frame:]) 648 649 # Make a tuple to hold the arguments. 650 651 ref = self._MakeObject(nlocals - start + 1, self.tuple_instance) 652 653 extra = 0 654 pos = start 655 656 while pos < nlocals: 657 self.save(ref + extra + 1, self.frame_stack[frame + pos]) # skip header when storing 658 extra += 1 659 pos += 1 660 661 self.value = DataValue(ref, ref) 662 663 def CheckInstance(self): 664 value = self.value 665 target_value = self.source 666 667 # For the 'self' parameter in an invoked function, the proposed context 668 # ('self') is checked against the target's context. 669 670 self.status = self._CheckInstance(value.ref, target_value.ref) 671 672 def CheckType(self): 673 value = self.value 674 target_value = self.operand 675 self.status = self._CheckType(value.ref, target_value.ref) 676 677 def JumpInFrame(self): 678 codeaddr = self.callable 679 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 680 681 def JumpWithFrame(self): 682 codeaddr = self.callable 683 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 684 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 685 686 def JumpWithFrameDirect(self): 687 operand = self.operand 688 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 689 return self.jump(operand, self.pc + 1) # return to the instruction after this one 690 691 def ExtendFrame(self): 692 self.frame_stack.extend([None] * self.operand) 693 694 def AdjustFrame(self): 695 self.invocation_sp_stack[-1] += self.operand 696 697 def Return(self): 698 return self.pull_pc() 699 700 def LoadResult(self): 701 self.value = self.result 702 703 def StoreResult(self): 704 self.result = self.value 705 706 def Jump(self): 707 return self.operand 708 709 def JumpIfTrue(self): 710 if self.status: 711 return self.operand 712 713 def JumpIfFalse(self): 714 if not self.status: 715 return self.operand 716 717 def LoadException(self): 718 self.value = DataValue(self.exception, self.exception) 719 720 def StoreException(self): 721 self.exception = self.value.ref 722 723 def ClearException(self): 724 self.exception = None 725 726 def RaiseException(self): 727 # NOTE: Adding the program counter as the first attribute. 728 self.save(self.exception + 1, self.pc) 729 # Jumping to the current handler. 730 if self.abort_upon_exception: 731 raise Exception 732 return self.handler_stack[-1] 733 734 def PushHandler(self): 735 self.handler_stack.append(self.operand) 736 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 737 self.handler_pc_stack.append(len(self.pc_stack)) 738 739 def PopHandler(self): 740 # Reduce the local frame pointer stack to refer to the handler's frame. 741 del self.local_sp_stack[self.handler_local_sp_stack.pop():] 742 # Reduce the PC stack to discard all superfluous return addresses. 743 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 744 self.handler_stack.pop() 745 746 def CheckException(self): 747 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value.ref) 748 749 def TestIdentity(self): 750 self.status = self.value.ref == self.source.ref 751 752 def TestIdentityAddress(self): 753 self.status = self.value.ref == self.operand 754 755 # LoadBoolean is implemented in the generated code. 756 # StoreBoolean is implemented by testing against the True value. 757 758 def InvertBoolean(self): 759 self.status = not self.status 760 761 # Common implementation details. 762 763 def _CheckInstance(self, ref, cls): 764 data = self.load(ref) 765 target_data = self.load(cls) 766 767 # Insist on instance vs. class. 768 769 if data.attrcode is None: # absent attrcode == class/module 770 return 0 771 772 if target_data.attrcode is not None: # present attrcode == instance 773 return 0 774 775 # Find the table entry for the descendant. 776 777 element = self.objlist[target_data.classcode + data.attrcode] 778 779 if element is not None: 780 attr_index, static_attr, offset = element 781 return attr_index == data.attrcode 782 else: 783 return 0 784 785 def _CheckType(self, ref, cls): 786 data = self.load(ref) 787 target_data = self.load(cls) 788 789 # Insist on instance vs. class. 790 791 if data.attrcode is None: # absent attrcode == class/module 792 return 0 793 794 if target_data.attrcode is not None: # present attrcode == instance 795 return 0 796 797 # Return whether the types match. 798 799 return data.classcode == target_data.classcode 800 801 def _MakeObject(self, size, ref): 802 # Load the template. 803 data = self.load(ref) 804 addr = self.new(size) 805 # Save the header, overriding the size. 806 self.save(addr, data.with_size(size)) 807 return addr 808 809 def _MakeFragment(self, size): 810 # Reserve twice the amount of space. 811 addr = self.new(size * 2) 812 # Save the header, overriding the size. 813 self.save(addr, FragmentObject(size, size * 2)) 814 return addr 815 816 def _LoadAddressContextCond(self, context, ref, inst_ref): 817 # Check the instance context against the target's context. 818 # This provides the context overriding for methods. 819 if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_ref, context): 820 # Replace the context with the instance. 821 return DataValue(inst_ref, ref) 822 else: 823 return DataValue(context, ref) 824 825 # Convenience functions. 826 827 def machine(program, with_builtins=0, debug=0, abort_upon_exception=0): 828 print "Making the image..." 829 code = program.get_image(with_builtins) 830 print "Getting raw structures..." 831 ot = program.get_object_table() 832 pt = program.get_parameter_table() 833 objlist = ot.as_list() 834 paramlist = pt.as_list() 835 print "Getting raw image..." 836 rc = program.get_raw_image() 837 print "Initialising the machine..." 838 importer = program.get_importer() 839 true_constant = importer.get_constant(True).location 840 false_constant = importer.get_constant(False).location 841 rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception) 842 library = Library(rm, true_constant, false_constant) 843 rm.library = library 844 rm.pc = program.code_location 845 print "Returning program occupying %d locations." % len(rm.memory) 846 return rm 847 848 # vim: tabstop=4 expandtab shiftwidth=4