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 # Reserve twice the amount of space. 453 addr = self._MakeFragment(size, size * 2) 454 # NOTE: Context is not relevant for fragments. 455 self.value = DataValue(None, addr) 456 457 def LoadAttr(self): 458 value = self.value 459 # Retrieved context should already be appropriate for the instance. 460 # NOTE: Adding 1 to skip any header. 461 self.value = self.load(value.ref + self.operand + 1) 462 463 def StoreAttr(self): 464 value = self.value 465 # Target should already be an instance. 466 # NOTE: Adding 1 to skip any header. 467 self.save(value.ref + self.operand + 1, self.source) 468 469 def LoadAttrIndex(self): 470 value = self.value 471 data = self.load(value.ref) 472 element = self.objlist[data.classcode + self.operand] 473 474 if element is not None: 475 attr_index, static_attr, offset = element 476 if attr_index == self.operand: 477 if static_attr: 478 self.value = self.load(offset) # offset is address of class/module attribute 479 else: 480 self.value = self.load(value.ref + offset) 481 return 482 483 self.exception = self._MakeObject(2, self.attr_error_instance) 484 return self.RaiseException() 485 486 # LoadAttrIndexContext not defined. 487 488 def LoadAttrIndexContextCond(self): 489 inst_value = self.value 490 data = self.load(inst_value.ref) 491 element = self.objlist[data.classcode + self.operand] 492 493 if element is not None: 494 attr_index, static_attr, offset = element 495 if attr_index == self.operand: 496 if static_attr: 497 loaded_value = self.load(offset) # offset is address of class/module attribute 498 if data.attrcode is None: # absent attrcode == class/module 499 self.value = loaded_value 500 else: 501 self.value = self._LoadAddressContextCond(loaded_value.context, loaded_value.ref, inst_value.ref) 502 else: 503 self.value = self.load(inst_value.ref + offset) 504 return 505 506 self.exception = self._MakeObject(2, self.attr_error_instance) 507 return self.RaiseException() 508 509 def StoreAttrIndex(self): 510 value = self.value 511 data = self.load(value.ref) 512 element = self.objlist[data.classcode + self.operand] 513 514 if element is not None: 515 attr_index, static_attr, offset = element 516 if attr_index == self.operand: 517 if static_attr: 518 self.exception = self._MakeObject(2, self.type_error_instance) 519 return self.RaiseException() 520 else: 521 self.save(value.ref + offset, self.source) 522 return 523 524 self.exception = self._MakeObject(2, self.attr_error_instance) 525 return self.RaiseException() 526 527 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 528 529 def MakeFrame(self): 530 self.invocation_sp_stack.append(len(self.frame_stack)) 531 self.frame_stack.extend([None] * self.operand) 532 533 def DropFrame(self): 534 self.local_sp_stack.pop() 535 frame = self.invocation_sp_stack.pop() 536 del self.frame_stack[frame:] # reset stack before call 537 538 def StoreFrame(self): 539 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 540 self.frame_stack[frame + self.operand] = self.value 541 542 def StoreFrameIndex(self): 543 value = self.value 544 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 545 data = self.load(value.ref) 546 element = self.paramlist[data.funccode + self.operand] 547 548 if element is not None: 549 # NOTE: Need to ensure correct positioning where a context has been generated. 550 param_index, offset = element 551 if param_index == self.operand: 552 self.frame_stack[frame + offset] = self.source 553 return 554 555 self.exception = self._MakeObject(2, self.type_error_instance) 556 return self.RaiseException() 557 558 def LoadCallable(self): 559 value = self.value 560 data = self.load(value.ref) 561 self.callable = data.codeaddr 562 563 def StoreCallable(self): 564 value = self.value 565 # NOTE: Should improve the representation and permit direct saving. 566 data = self.load(value.ref) 567 self.save(value.ref, data.with_callable(self.callable)) 568 569 def LoadContext(self): 570 value = self.value 571 # NOTE: Omission of the context of the context would make things like 572 # NOTE: self() inside methods impossible. 573 self.value = DataValue(value.context, value.context) 574 575 def CheckContext(self): 576 self.status = self.value.ref is not ReplaceableContext 577 578 def CheckClass(self): 579 value = self.value 580 if value.ref in (ReplaceableContext, PlaceholderContext): 581 self.status = 0 582 return 583 584 data = self.load(value.ref) 585 586 # Classes are not themselves usable as the self argument. 587 # NOTE: This may change at some point. 588 # However, where classes appear as the context, instance 589 # compatibility is required in the first argument. 590 591 self.status = data.attrcode is None # absent attrcode == class 592 593 def CheckFrame(self): 594 (nargs, ndefaults) = self.operand 595 596 # The frame is actually installed as the locals. 597 # Retrieve the context from the first local. 598 599 frame = self.local_sp_stack[-1] 600 nlocals = len(self.frame_stack[frame:]) 601 602 if not ((nargs - ndefaults) <= nlocals): 603 raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 604 self.exception = self._MakeObject(2, self.type_error_instance) 605 return self.RaiseException() 606 607 def CheckExtra(self): 608 nargs = self.operand 609 610 # The frame is actually installed as the locals. 611 # Retrieve the context from the first local. 612 613 frame = self.local_sp_stack[-1] 614 nlocals = len(self.frame_stack[frame:]) 615 616 # Provide the extra star parameter if necessary. 617 618 if nlocals == nargs: 619 self.frame_stack.extend([None]) # ExtendFrame(1) 620 621 def FillDefaults(self): 622 value = self.value 623 (nargs, ndefaults) = self.operand 624 625 # The frame is actually installed as the locals. 626 627 frame = self.local_sp_stack[-1] 628 nlocals = len(self.frame_stack[frame:]) 629 630 # Support population of defaults. 631 # This involves copying the "attributes" of a function into the frame. 632 633 default = nlocals - (nargs - ndefaults) 634 self.frame_stack.extend([None] * (nargs - nlocals)) 635 pos = nlocals 636 637 while pos < nargs: 638 self.frame_stack[frame + pos] = self.load(value.ref + default + 1) # skip header 639 default += 1 640 pos += 1 641 642 def CopyExtra(self): 643 start = self.operand 644 645 # The frame is the source of the extra arguments. 646 647 frame = self.local_sp_stack[-1] 648 nlocals = len(self.frame_stack[frame:]) 649 650 # Make a tuple to hold the arguments. 651 652 ref = self._MakeObject(nlocals - start + 1, self.tuple_instance) 653 654 extra = 0 655 pos = start 656 657 while pos < nlocals: 658 self.save(ref + extra + 1, self.frame_stack[frame + pos]) # skip header when storing 659 extra += 1 660 pos += 1 661 662 self.value = DataValue(ref, ref) 663 664 def CheckInstance(self): 665 value = self.value 666 target_value = self.source 667 668 # For the 'self' parameter in an invoked function, the proposed context 669 # ('self') is checked against the target's context. 670 671 self.status = self._CheckInstance(value.ref, target_value.ref) 672 673 def CheckType(self): 674 value = self.value 675 target_value = self.operand 676 self.status = self._CheckType(value.ref, target_value.ref) 677 678 def JumpInFrame(self): 679 codeaddr = self.callable 680 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 681 682 def JumpWithFrame(self): 683 codeaddr = self.callable 684 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 685 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 686 687 def JumpWithFrameDirect(self): 688 operand = self.operand 689 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 690 return self.jump(operand, self.pc + 1) # return to the instruction after this one 691 692 def ExtendFrame(self): 693 self.frame_stack.extend([None] * self.operand) 694 695 def AdjustFrame(self): 696 self.invocation_sp_stack[-1] += self.operand 697 698 def Return(self): 699 return self.pull_pc() 700 701 def LoadResult(self): 702 self.value = self.result 703 704 def StoreResult(self): 705 self.result = self.value 706 707 def Jump(self): 708 return self.operand 709 710 def JumpIfTrue(self): 711 if self.status: 712 return self.operand 713 714 def JumpIfFalse(self): 715 if not self.status: 716 return self.operand 717 718 def LoadException(self): 719 self.value = DataValue(self.exception, self.exception) 720 721 def StoreException(self): 722 self.exception = self.value.ref 723 724 def ClearException(self): 725 self.exception = None 726 727 def RaiseException(self): 728 # NOTE: Adding the program counter as the first attribute. 729 self.save(self.exception + 1, self.pc) 730 # Jumping to the current handler. 731 if self.abort_upon_exception: 732 raise Exception 733 return self.handler_stack[-1] 734 735 def PushHandler(self): 736 self.handler_stack.append(self.operand) 737 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 738 self.handler_pc_stack.append(len(self.pc_stack)) 739 740 def PopHandler(self): 741 # Reduce the local frame pointer stack to refer to the handler's frame. 742 del self.local_sp_stack[self.handler_local_sp_stack.pop():] 743 # Reduce the PC stack to discard all superfluous return addresses. 744 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 745 self.handler_stack.pop() 746 747 def CheckException(self): 748 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value.ref) 749 750 def TestIdentity(self): 751 self.status = self.value.ref == self.source.ref 752 753 def TestIdentityAddress(self): 754 self.status = self.value.ref == self.operand 755 756 # LoadBoolean is implemented in the generated code. 757 # StoreBoolean is implemented by testing against the True value. 758 759 def InvertBoolean(self): 760 self.status = not self.status 761 762 # Common implementation details. 763 764 def _CheckInstance(self, ref, cls): 765 data = self.load(ref) 766 target_data = self.load(cls) 767 768 # Insist on instance vs. class. 769 770 if data.attrcode is None: # absent attrcode == class/module 771 return 0 772 773 if target_data.attrcode is not None: # present attrcode == instance 774 return 0 775 776 # Find the table entry for the descendant. 777 778 element = self.objlist[target_data.classcode + data.attrcode] 779 780 if element is not None: 781 attr_index, static_attr, offset = element 782 return attr_index == data.attrcode 783 else: 784 return 0 785 786 def _CheckType(self, ref, cls): 787 data = self.load(ref) 788 target_data = self.load(cls) 789 790 # Insist on instance vs. class. 791 792 if data.attrcode is None: # absent attrcode == class/module 793 return 0 794 795 if target_data.attrcode is not None: # present attrcode == instance 796 return 0 797 798 # Return whether the types match. 799 800 return data.classcode == target_data.classcode 801 802 def _MakeObject(self, size, ref): 803 # Load the template. 804 data = self.load(ref) 805 addr = self.new(size) 806 # Save the header, overriding the size. 807 self.save(addr, data.with_size(size)) 808 return addr 809 810 def _MakeFragment(self, occupied, size): 811 addr = self.new(size) 812 # Save the header, overriding the size. 813 self.save(addr, FragmentObject(occupied, size)) 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