# Copyright (C) 2008-2010, Parrot Foundation. # # pirric.pir # A rudimentary old style Basic interpreter for parrot # This is a proof of concept version, don't blame for redundant code # and other ugliness # # pirric is PIR Retro basIC # # Only one instruction per line. # # Instructions implemented: # - Flow control: GOTO, GOSUB, RETURN, RUN, END, STOP, CONT, EXIT # - Conditional: IF/ELSE # - Loop: FOR/NEXT # - Programming: LIST, LOAD, SAVE # - Debugging: TRON, TROFF # - Input/Output: PRINT # - Error control: ERROR, ON ERROR GOTO, ON ERROR EXIT # - Miscellaneous: REM, CLEAR # - Variables: varname = expression # - Access to parrot modules: LOAD "module name" , B # # Shorthands: # - ? -> PRINT # # Expressions: # - Operators: + - * / < > = unary+ unary- MOD ^ # - Predefined numeric functions: COMPLEX, SQR, EXP, LN, SIN, COS, TAN, ASIN, ACOS, ATAN, SINH, COSH, TANH # - Predefined string functions: CHR$, ASC, LEN, LEFT$, RIGHT$, MID$ # - Parenthesis # - Indexing with [ ] # - Special functions: NEW, ISA, COMPREG, GETPARROTINTERP # - Calls to methods in foreign objects # - Calls to functions in foreign namespaces # # Command line options: # -d Parrot debugger mode. Jumps to the debugger after each # TRON line inform and after the 'Ready' prompt. # -t Trace on. Same as the TRON instruction # -p all remaining arguments are executed as PRINT instructions #----------------------------------------------------------------------- .include 'iterator.pasm' .include 'except_severity.pasm' .include 'except_types.pasm' .include 'cclass.pasm' .include 'warnings.pasm' .loadlib 'io_ops' .loadlib 'debug_ops' .loadlib 'trans_ops' #----------------------------------------------------------------------- .sub pirric_aux_loadbytecode .param string bcname load_bytecode bcname .end .HLL 'parrot' #----------------------------------------------------------------------- .const int PIRRIC_ERROR_NORMAL = 0 .const int PIRRIC_ERROR_EXIT = 1 .const int PIRRIC_ERROR_GOTO = 2 #----------------------------------------------------------------------- .sub init :load :init warningson .PARROT_WARNINGS_DEPRECATED_FLAG .local pmc func func = get_global ['Tokenizer'], 'newTokenizer' set_global 'newTokenizer', func .local pmc cl cl = newclass ['Tokenizer'] addattribute cl, 'line' addattribute cl, 'pos' addattribute cl, 'last' addattribute cl, 'pending' .local pmc progclass progclass = newclass ['Program'] addattribute progclass, 'text' addattribute progclass, 'lines' .local pmc runnerclass runnerclass = newclass ['Runner'] addattribute runnerclass, 'program' addattribute runnerclass, 'exitcode' addattribute runnerclass, 'errormode' addattribute runnerclass, 'errorvalue' addattribute runnerclass, 'curline' addattribute runnerclass, 'vars' addattribute runnerclass, 'stack' addattribute runnerclass, 'debugger' addattribute runnerclass, 'tron' $P0 = get_class 'String' cl = newclass 'Literal' addparent cl, $P0 set_global 'Literal', cl .local pmc keywords, methods # Get methods hash to verify methods = inspect runnerclass, 'methods' keywords = new 'Hash' setkeyword(methods, keywords, 'CLEAR') setkeyword(methods, keywords, 'CONT') setkeyword(methods, keywords, 'END') setkeyword(methods, keywords, 'EXIT') setkeyword(methods, keywords, 'ERROR') setkeyword(methods, keywords, 'FOR') setkeyword(methods, keywords, 'GOSUB') setkeyword(methods, keywords, 'GOTO') setkeyword(methods, keywords, 'IF') setkeyword(methods, keywords, 'LIST') setkeyword(methods, keywords, 'LOAD') setkeyword(methods, keywords, 'NEXT') setkeyword(methods, keywords, 'NEW') setkeyword(methods, keywords, 'ON') setkeyword(methods, keywords, 'PRINT') setkeyword(methods, keywords, 'REM') setkeyword(methods, keywords, 'RETURN') setkeyword(methods, keywords, 'RUN') setkeyword(methods, keywords, 'SAVE') setkeyword(methods, keywords, 'STOP') setkeyword(methods, keywords, 'TROFF') setkeyword(methods, keywords, 'TRON') set_global 'keywords', keywords .local pmc predefs predefs = new 'Hash' setpredef(methods, predefs, 'NEW') setpredef(methods, predefs, 'ISA') setpredef(methods, predefs, 'GETPARROTINTERP') setpredef(methods, predefs, 'CHR$', 'CHR_S') setpredef(methods, predefs, 'ASC') setpredef(methods, predefs, 'LEN') setpredef(methods, predefs, 'LEFT$', 'LEFT_S') setpredef(methods, predefs, 'RIGHT$', 'RIGHT_S') setpredef(methods, predefs, 'MID$', 'MID_S') setpredef(methods, predefs, 'COMPLEX') setpredef(methods, predefs, 'COMPREG') setpredef(methods, predefs, 'EXP') setpredef(methods, predefs, 'LN') setpredef(methods, predefs, 'SIN') setpredef(methods, predefs, 'SINH') setpredef(methods, predefs, 'COS') setpredef(methods, predefs, 'COSH') setpredef(methods, predefs, 'TAN') setpredef(methods, predefs, 'TANH') setpredef(methods, predefs, 'ASIN') setpredef(methods, predefs, 'ACOS') setpredef(methods, predefs, 'ATAN') setpredef(methods, predefs, 'SQR') set_global 'predefs', predefs # Create classes for control flow exceptions .local pmc pircontrol pircontrol = newclass ['pircontrol'] .local pmc basejump basejump = subclass pircontrol, ['basejump'] addattribute basejump, 'jumpline' .local pmc endclass endclass = subclass pircontrol, ['End'] .local pmc exitclass exitclass = subclass pircontrol, ['Exit'] .local pmc returnclass returnclass = subclass pircontrol, ['Return'] .local pmc nextclass nextclass = subclass basejump, ['Next'] .local pmc jumpclass jumpclass = subclass basejump, ['Jump'] addattribute jumpclass, 'jumptype' .local pmc stopclass stopclass = subclass pircontrol, ['Stop'] .local pmc contclass stopclass = subclass pircontrol,['Cont'] .local pmc forclass forclass = subclass basejump, ['For'] addattribute forclass, 'controlvar' addattribute forclass, 'increment' addattribute forclass, 'limit' .end #----------------------------------------------------------------------- .sub main :main .param pmc args .local pmc program program = new ['Program'] .local pmc runner runner = new ['Runner'] setattribute runner, 'program', program $I0 = args $I1 = 1 read_args: le $I0, $I1, no_prog .local string arg arg = args[$I1] if arg == '-d' goto opt_debugger if arg == '-t' goto opt_tron if arg == '-p' goto print_items #say arg program.'load'(arg) $I0 = 1 goto start opt_debugger: debug_init runner.'debugger'() inc $I1 goto read_args opt_tron: runner.'trace'(1) inc $I1 goto read_args print_items: .local pmc tokenizer inc $I1 le $I0, $I1, print_end $S9 = args [$I1] tokenizer = newTokenizer($S9) runner.'func_PRINT'(tokenizer) null tokenizer goto print_items print_end: exit 0 no_prog: $I0 = 0 start: $I1 = runner.'runloop'($I0) exit $I1 .end #----------------------------------------------------------------------- .sub setkeyword .param pmc methods .param pmc keywords .param string key .local string funcname funcname = concat 'func_', key .local pmc func func = methods[funcname] $I0 = defined func if $I0 goto good print funcname die ': No func!' exit 1 good: keywords [key] = func .end #----------------------------------------------------------------------- .sub setpredef .param pmc methods .param pmc predefs .param string key .param string name :optional .param int has_name :opt_flag if has_name goto setfuncname name = key setfuncname: .local string funcname funcname = concat 'predef_', name .local pmc func func = methods[funcname] $I0 = defined func if $I0 goto good print funcname say ': no func!' exit 1 good: predefs [key] = func .end #----------------------------------------------------------------------- .sub FatalError .param string msg .local pmc excep excep = new 'Exception' .local pmc aux aux = new 'String' aux = msg setattribute excep, 'message', aux aux = new 'Integer' aux = .EXCEPT_FATAL setattribute excep, 'severity', aux throw excep .end #----------------------------------------------------------------------- .sub UserError .param string msg .local pmc excep, message, severity message = new 'String' message = 'ERROR: ' message = concat message, msg severity = new 'Integer' severity = .EXCEPT_ERROR excep = new 'Exception' setattribute excep, 'message', message setattribute excep, 'severity', severity throw excep .end #----------------------------------------------------------------------- .sub SyntaxError .local pmc excep excep = new 'Exception' .local pmc aux aux = new 'String' aux = 'Syntax error' setattribute excep, 'message', aux aux = new 'Integer' aux = .EXCEPT_ERROR setattribute excep, 'severity', aux throw excep .end #----------------------------------------------------------------------- .sub VarNotDefined .local pmc excep excep = new 'Exception' .local pmc aux aux = new 'String' aux = 'Variable not found' setattribute excep, 'message', aux aux = new 'Integer' aux = .EXCEPT_ERROR setattribute excep, 'severity', aux throw excep .end #----------------------------------------------------------------------- .sub readlinebas .param pmc file .param int interactive :optional .local string line if interactive goto read_inter line = readline file goto read_done read_inter: line = file.'readline_interactive'() read_done: $I1 = length line checkline: if $I1 < 1 goto done dec $I1 $I2 = is_cclass .CCLASS_NEWLINE, line, $I1 unless $I2 goto done line = substr line, 0, $I1 goto checkline done: .return(line) .end ######################################################################## .namespace ['Runner'] #----------------------------------------------------------------------- .sub init :vtable $P0 = new 'Integer' $P0 = 0 setattribute self, 'tron', $P0 $P0 = new 'Integer' $P0 = 0 setattribute self, 'debugger', $P0 $P1 = new 'ResizablePMCArray' setattribute self, 'stack', $P1 $P2 = new 'Integer' $P2 = PIRRIC_ERROR_NORMAL setattribute self, 'errormode', $P2 $P3 = new 'Integer' setattribute self, 'errorvalue', $P3 $P4 = new 'Integer' setattribute self, 'exitcode', $P4 self.'clear_vars'() .end #----------------------------------------------------------------------- .sub clear_vars :method .local pmc vars vars = new 'Hash' setattribute self, 'vars', vars .end #----------------------------------------------------------------------- .sub get_var :method .param string varname .local pmc vars, var vars = getattribute self, 'vars' varname = upcase varname var = vars[varname] .return(var) .end #----------------------------------------------------------------------- .sub set_var :method .param string varname .param pmc value .local pmc vars, var vars = getattribute self, 'vars' varname = upcase varname vars[varname] = value .end #----------------------------------------------------------------------- .sub set_error_exit :method .param int code $P0 = getattribute self, 'errormode' $P0 = PIRRIC_ERROR_EXIT $P1 = getattribute self, 'errorvalue' $P1 = code .end #----------------------------------------------------------------------- .sub set_error_goto :method .param int code .local int newmode newmode = PIRRIC_ERROR_GOTO ne code, 0, setmode # ON ERROR GOTO 0 means use default error handling newmode = PIRRIC_ERROR_NORMAL setmode: $P0 = getattribute self, 'errormode' $P0 = newmode $P1 = getattribute self, 'errorvalue' $P1 = code .end #----------------------------------------------------------------------- .sub clear_all :method .local pmc stack self.'clear_vars'() stack = getattribute self, 'stack' stack = 0 .end #----------------------------------------------------------------------- .sub set_program :method .param pmc program setattribute self, 'program', program .end #----------------------------------------------------------------------- .sub getcurline :method $P0 = getattribute self, 'curline' $S0 = $P0 .return($S0) .end #----------------------------------------------------------------------- .sub debugger :method $P0 = getattribute self, 'debugger' $P0 = 1 .end #----------------------------------------------------------------------- .sub trace :method .param int level $P0 = getattribute self, 'tron' $P0 = level .end #----------------------------------------------------------------------- .sub get_numeric_arg :method .param pmc tokenizer .local pmc arg arg = self.'evaluate'(tokenizer) $P0 = tokenizer.'get'() $I0 = defined $P0 unless $I0 goto fail ne $P0, ')', fail $I0 = isa arg, 'Integer' unless $I0 goto done $I0 = arg $N0 = $I0 arg = new 'Float' arg = $N0 done: .return(arg) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub get_1_arg :method .param pmc tokenizer .local pmc arg arg = self.'evaluate'(tokenizer) $P0 = tokenizer.'get'() $I0 = defined $P0 unless $I0 goto fail ne $P0, ')', fail .return(arg) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub get_2_args :method .param pmc tokenizer .local pmc arg1, arg2 arg1 = self.'evaluate'(tokenizer) $P0 = tokenizer.'get'() if_null $P0, fail $I0 = defined $P0 unless $I0 goto fail ne $P0, ',', fail arg2 = self.'evaluate'(tokenizer) $P0 = tokenizer.'get'() if_null $P0, fail $I0 = defined $P0 unless $I0 goto fail ne $P0, ')', fail .return(arg1, arg2) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub get_args :method .param pmc tokenizer .local pmc args .local pmc arg .local pmc token .local pmc delim args = new 'ResizablePMCArray' token = tokenizer.'get'() $I0 = defined token unless $I0 goto fail eq token, ')', empty null arg arg = self.'evaluate'(tokenizer, token) nextarg: push args, arg null arg delim = tokenizer.'get'() if_null delim, fail $I0 = defined delim unless $I0 goto fail eq delim, ')', endargs ne delim, ',', fail arg = self.'evaluate'(tokenizer) goto nextarg endargs: .return(args) empty: null $P0 .return($P0) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_NEW :method .param pmc tokenizer .local pmc args .local int nargs .local string name .local pmc obj $P1 = tokenizer.'get'() ne $P1, '(', fail args = self.'get_args'(tokenizer) $I0 = defined args unless $I0 goto fail nargs = args name = args [0] #print 'NEW: ' #say name eq nargs, 1, noarg .local pmc arg1 arg1 = args [1] #say arg1 obj = new name, arg1 goto done noarg: obj = new name done: .return(obj) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_ISA :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail ($P1, $P2) = self.'get_2_args'(tokenizer) $I0 = isa $P1, $P2 $P0 = new 'Integer' $P0 = $I0 .return($P0) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_GETPARROTINTERP :method .param pmc tokenizer $P0 = getinterp .return($P0) .end #----------------------------------------------------------------------- .sub predef_CHR_S :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_1_arg'(tokenizer) $I0 = $P2 $S0 = chr $I0 $I1 = find_encoding 'utf8' $S0 = trans_encoding $S0, $I1 $P3 = new 'String' $P3 = $S0 .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_ASC :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_1_arg'(tokenizer) $S0 = $P2 $I0 = ord $S0 $P3 = new 'Integer' $P3 = $I0 .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_LEN :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail null $P5 $P5 = self.'get_1_arg'(tokenizer) $S5 = $P5 $I0 = length $S5 $P6 = new 'Integer' $P6 = $I0 .return($P6) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_LEFT_S :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail null $P5 null $P6 ($P5, $P6) = self.'get_2_args'(tokenizer) $S0 = $P5 $I0 = $P6 $S1 = substr $S0, 0, $I0 $P7 = new 'String' $P7 = $S1 .return($P7) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_RIGHT_S :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail null $P5 null $P6 ($P5, $P6) = self.'get_2_args'(tokenizer) $S0 = $P5 $I0 = $P6 $I1 = $S0 $I0 = $I1 - $I0 $S1 = substr $S0, $I0 $P7 = new 'String' $P7 = $S1 .return($P7) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_MID_S :method .param pmc tokenizer $P0 = tokenizer.'get'() ne $P0, '(', fail $P1 = self.'get_args'(tokenizer) $I0 = $P1 lt $I0, 2, fail gt $I0, 3, fail $S0 = $P1[0] $I1 = $P1[1] dec $I1 lt $I0, 3, mid_nolen $I2 = $P1[2] $S1 = substr $S0, $I1, $I2 goto mid_result mid_nolen: $S1 = substr $S0, $I1 mid_result: $P2 = new 'String' $P2 = $S1 .return($P2) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_COMPLEX :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail null $P5 null $P6 ($P5, $P6) = self.'get_2_args'(tokenizer) $P7 = new 'Complex' $N5 = $P5 $N6 = $P6 $P7[0] = $N5 $P7[1] = $N6 .return($P7) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_COMPREG :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_1_arg'(tokenizer) $S1 = $P2 $P3 = compreg $S1 .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_EXP :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'exp'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_LN :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'ln'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_SIN :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'sin'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_SINH :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'sinh'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_COS :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'cos'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_COSH :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'cosh'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_TAN :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'tan'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_TANH :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'tanh'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_ASIN :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'asin'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_ACOS :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'acos'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_ATAN :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'atan'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub predef_SQR :method .param pmc tokenizer $P1 = tokenizer.'get'() ne $P1, '(', fail $P2 = self.'get_numeric_arg'(tokenizer) $P3 = $P2.'sqrt'() .return($P3) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub get_args_and_call :method .param pmc tokenizer .param pmc fun .local pmc args, result args = self.'get_args'(tokenizer) $I0 = defined args unless $I0 goto emptyargs result = fun(args :flat) goto done emptyargs: result = fun() done: .return(result) .end #----------------------------------------------------------------------- .sub eval_base :method .param pmc tokenizer .param pmc token :optional .local pmc arg .local pmc args $I0 = defined token if $I0 goto check token = tokenizer.'get'() check: $I0 = defined token unless $I0 goto fail eq token, '(', parenexp $I0 = isa token, 'Literal' if $I0 goto isliteral $I0 = isa token, 'Integer' if $I0 goto isinteger $I0 = isa token, 'Float' if $I0 goto isfloat $I0 = isa token, 'String' unless $I0 goto fail $S0 = token $S0 = upcase $S0 #print $S0 # Some predefined functions: .local pmc predefs predefs = get_hll_global 'predefs' .local pmc func func = predefs[$S0] $I0 = defined func unless $I0 goto no_predef $P0 = self.func(tokenizer) .return($P0) no_predef: #say $S0 .local pmc var var = self.'get_var'($S0) unless_null var, getvar $P0 = get_namespace token $I0 = defined $P0 if $I0 goto spaced $P0 = get_root_namespace token $I0 = defined $P0 if $I0 goto spaced $P1 = tokenizer.'get'() $S1 = $P1 ne $S1, '(', var_not_defined $S0 = token #say $S0 var = get_hll_global $S0 if_null var, fail args = self.'get_args'(tokenizer) $P9 = var(args) .return($P9) spaced: # say "namespace" $P1 = tokenizer.'get'() ne $P1, '.', fail $P1 = tokenizer.'get'() $S1 = $P1 $P2 = $P0 [$S1] $P4 = tokenizer.'get'() eq $P4, '(', getargs tokenizer.'back'() .return($P2) isliteral: .return(token) isinteger: .return(token) isfloat: .return(token) getargs: args = self.'get_args'(tokenizer) $I0 = defined args unless $I0 goto emptyargs endargs: $P3 = $P2(args :flat) .return($P3) emptyargs: $P3 = $P2() .return($P3) getvar: $P2 = tokenizer.'get'() if_null $P2, donevar eq $P2, '.', dotted eq $P2, '(', isfunctor tokenizer.'back'() donevar: .return(var) isfunctor: #say 'Functor' $P3 = self.'get_args_and_call'(tokenizer, var) .return($P3) dotted: $P3 = tokenizer.'get'() $P4 = tokenizer.'get'() eq $P4, '(', methodcall tokenizer.'back'() $S1 = $P3 $P5 = getattribute token, $S1 .return($P5) methodcall: $S2 = $P3 #say $S2 .local pmc methargs methargs = self.'get_args'(tokenizer) $I0 = defined methargs unless $I0 goto memptyargs $P5 = var.$S2(methargs :flat) .return($P5) memptyargs: $P2 = var.$S2() .return($P2) parenexp: $P1 = self.'evaluate'(tokenizer) token = tokenizer.'get'() ne token, ')', fail .return($P1) var_not_defined: VarNotDefined() fail: SyntaxError() .end #----------------------------------------------------------------------- .sub eval_base_1 :method .param pmc tokenizer .param pmc token :optional $P0 = self.'eval_base'(tokenizer, token) again: $P1 = tokenizer.'get'() if_null $P1, done $I0 = defined $P1 unless $I0 goto done eq $P1, '[', keyit tokenizer.'back'() done: .return($P0) keyit: $P2 = self.'evaluate'(tokenizer) $P1 = tokenizer.'get'() if_null $P1, fail eq $P1, ']', last ne $P1, ',', fail $P3 = $P0 [$P2] null $P2 null $P0 $P0 = $P3 null $P3 goto keyit last: $P3 = $P0 [$P2] null $P0 $P0 = $P3 null $P3 goto again fail: SyntaxError() .end #----------------------------------------------------------------------- .sub eval_pow :method .param pmc tokenizer .param pmc token :optional $P0 = self.'eval_base_1'(tokenizer, token) more: $P1 = tokenizer.'get'() if_null $P1, done eq $P1, '^', dopow tokenizer.'back'() done: .return($P0) dopow: $P2 = self.'eval_unary'(tokenizer) null $P3 $P3 = pow $P0, $P2 set $P0, $P3 null $P2 goto more .end #----------------------------------------------------------------------- .sub eval_mod :method .param pmc tokenizer .param pmc token :optional $P0 = self.'eval_pow'(tokenizer, token) more: $P1 = tokenizer.'get'() if_null $P1, done eq $P1, 'MOD', domod tokenizer.'back'() done: .return($P0) domod: $P2 = self.'eval_pow'(tokenizer) $P3 = clone $P0 mod $P3, $P2 set $P0, $P3 goto more .end #----------------------------------------------------------------------- .sub eval_unary :method .param pmc tokenizer .param pmc token :optional $I0 = defined token if $I0 goto check token = tokenizer.'get'() $I0 = defined token unless $I0 goto fail check: # Quick fix to MMD problem $I0 = isa token, 'Literal' if $I0 goto notoken eq token, '-', unaryminus eq token, '+', unaryplus notoken: $P0 = self.'eval_mod'(tokenizer, token) .return($P0) unaryplus: $P0 = self.'eval_unary'(tokenizer) .return($P0) unaryminus: $P0 = self.'eval_unary'(tokenizer) $P1 = clone $P0 $P1 = 0 $P1 = $P1 - $P0 .return($P1) fail: SyntaxError() .end #----------------------------------------------------------------------- .sub eval_mul :method .param pmc tokenizer .param pmc token :optional $P0 = self.'eval_unary'(tokenizer, token) more: $P1 = tokenizer.'get'() if_null $P1, done eq $P1, '*', domul eq $P1, '/', dodiv tokenizer.'back'() done: .return($P0) domul: $P2 = self.'eval_unary'(tokenizer) $P3 = clone $P0 mul $P3, $P2 set $P0, $P3 goto more dodiv: $P2 = self.'eval_unary'(tokenizer) $P3 = clone $P0 div $P3, $P2 set $P0, $P3 goto more .end #----------------------------------------------------------------------- .sub eval_add :method .param pmc tokenizer .param pmc token :optional $P0 = self.'eval_mul'(tokenizer, token) more: $P1 = tokenizer.'get'() if_null $P1, done eq $P1, '+', doadd eq $P1, '-', dosub tokenizer.'back'() done: .return($P0) doadd: $P2 = self.'eval_mul'(tokenizer) clone $P3, $P0 $I3 = isa $P3, 'String' if $I3 goto str_add $I2 = isa $P2, 'String' if $I2 goto str_add add $P3, $P2 set $P0, $P3 goto more str_add: $S0 = $P3 $S1 = $P2 $S3 = concat $S0, $S1 $P3 = $S3 set $P0, $P3 goto more dosub: $P2 = self.'eval_mul'(tokenizer) clone $P3, $P0 sub $P3, $P2 set $P0, $P3 goto more .end #----------------------------------------------------------------------- .sub eval_comp :method .param pmc tokenizer .param pmc token :optional $P0 = self.'eval_add'(tokenizer, token) more: $P1 = tokenizer.'get'() if_null $P1, done eq $P1, '=', doequal eq $P1, '<', doless eq $P1, '>', dogreat tokenizer.'back'() done: .return($P0) doequal: $P2 = self.'eval_add'(tokenizer) set $P3, $P0 $I0 = iseq $P3, $P2 goto next doless: $P2 = self.'eval_add'(tokenizer) set $P3, $P0 $I0 = islt $P3, $P2 goto next dogreat: $P2 = self.'eval_add'(tokenizer) set $P3, $P0 $I0 = isgt $P3, $P2 next: null $P0 $P0 = new 'Integer' set $P0, $I0 goto more .end #----------------------------------------------------------------------- .sub evaluate :method .param pmc tokenizer .param pmc token :optional $P0 = self.'eval_comp'(tokenizer, token) # $I0 = isa $P0, 'Integer' # unless $I0 goto done # say '<Integer' #done: .return($P0) .end #----------------------------------------------------------------------- .sub findline :method .param int linenum .local pmc program program = getattribute self, 'program' .local pmc iter iter = program.'begin'() .local int fline nextline: unless iter goto noline shift fline, iter gt fline, linenum, noline lt fline, linenum, nextline .return(iter) noline: null iter .return(iter) .end #----------------------------------------------------------------------- .sub runloop :method .param int start :optional .local pmc program .local pmc stack .local pmc iter .local pmc debugger .local pmc tron .local pmc pircontrol .local int stopline .local int curline .local pmc pcurline .local int target pircontrol = get_class ['pircontrol'] program = getattribute self, 'program' stack = getattribute self, 'stack' tron = getattribute self, 'tron' debugger = getattribute self, 'debugger' stopline = 0 pcurline = new 'Integer' setattribute self, 'curline', pcurline iter = program.'begin'() push_eh handle_excep curline = 0 unless start goto next shift curline, iter next: if curline goto runit self.'interactive'() goto next runit: pcurline = curline unless tron goto executeline print '[' print curline print ']' unless debugger goto executeline debug_break executeline: program = getattribute self, 'program' $S1 = program [curline] .local pmc tokenizer tokenizer = newTokenizer($S1) self.'execute'(tokenizer) unless iter goto endprog shift curline, iter goto next endprog: curline = 0 goto next handle_excep: .local pmc excep, type, severity .local int itype .get_results(excep) type = getattribute excep, 'type' itype = type severity = getattribute excep, 'severity' eq severity, .EXCEPT_EXIT, finish eq itype, .CONTROL_RETURN, handle_return $P1 = getattribute excep, 'payload' $I1 = defined $P1 unless $I1 goto unhandled $I1 = isa $P1, pircontrol unless $I1 goto unhandled $I1 = isa $P1, 'Jump' if $I1 goto handle_jump $I1 = isa $P1, 'Next' if $I1 goto handle_next $I1 = isa $P1, 'Return' if $I1 goto handle_return $I1 = isa $P1, 'Stop' if $I1 goto handle_stop $I1 = isa $P1, 'Cont' if $I1 goto handle_cont $I1 = isa $P1, 'End' if $I1 goto prog_end $I1 = isa $P1, 'Exit' if $I1 goto finish FatalError('Unhandled control type') handle_stop: print 'Stopped' goto linenum_msg handle_cont: unless stopline goto cannot_cont iter = self.'findline'(stopline) shift curline, iter stopline = 0 push_eh handle_excep goto next cannot_cont: print 'Cannot CONTinue' goto linenum_msg handle_jump: $P2 = getattribute $P1, 'jumpline' $I1 = $P2 eq $I1, 0, prog_end eq $I1, -1, prog_end $S2 = curline target = $P2 do_jump: iter = self.'findline'(target) if_null iter, noline curline = target $P3 = getattribute $P1, 'jumptype' $I1 = defined $P3 unless $I1 goto handled_jump eq $P3, 1, handle_gosub goto handled_jump handle_gosub: push stack, $S2 goto handled_jump handle_next: $P2 = getattribute $P1, 'jumpline' $I1 = $P2 iter = self.'findline'($I1) curline = shift iter handled_jump: push_eh handle_excep goto runit handle_return: .local pmc stack stack = getattribute self, 'stack' $I0 = stack unless $I0 goto no_gosub $P0 = pop stack curline = $P0 iter = self.'findline'(curline) curline = shift iter #say curline push_eh handle_excep goto next no_gosub: print 'RETURN without GOSUB' goto linenum_msg prog_end: curline = 0 null iter push_eh handle_excep goto next unhandled: $P3 = getattribute self, 'errormode' $I0 = $P3 eq $I0, PIRRIC_ERROR_GOTO, goto_error ne $I0, PIRRIC_ERROR_NORMAL, exit_error $P1 = getattribute excep, 'message' print $P1 goto linenum_msg exit_error: $P4 = getattribute self, 'errorvalue' $I0 = $P4 $P5 = getattribute self, 'exitcode' $P5 = $I0 goto finish goto_error: $P4 = getattribute self, 'errorvalue' $I1 = PIRRIC_ERROR_NORMAL $P3 = $I1 $I0 = $P4 iter = self.'findline'($I0) if_null iter, noline curline = $I0 push_eh handle_excep goto runit noline: print 'Line does not exist' linenum_msg: unless curline goto endmsg print ' in ' print curline endmsg: say '' curline = 0 push_eh handle_excep goto next finish: $P9 = getattribute self, 'exitcode' $I0 = $P9 .return($I0) .end #----------------------------------------------------------------------- .sub interactive :method .local pmc stdin stdin = getstdin .local pmc program program = getattribute self, 'program' .local string line .local pmc debugger debugger = getattribute self, 'debugger' say 'Ready' reinit: unless debugger goto doreadline debug_break doreadline: line = readlinebas(stdin, 1) .local pmc tokenizer .local pmc token tokenizer = newTokenizer(line) token = tokenizer.'get'() if_null token, reinit $I0 = isa token, 'Integer' unless $I0 goto execute # Have line number: if has content store it, else delete $I0 = token line = tokenizer.'getall'() $I1 = length line unless $I1 goto deleteit program.'storeline'($I0, line) goto reinit deleteit: program.'deleteline'($I0) goto reinit execute: self.'execute'(tokenizer, token) .end #----------------------------------------------------------------------- .sub execute :method .param pmc tokenizer .param pmc token :optional .param int has :opt_flag if has goto check token = tokenizer.'get'() check: unless token goto next .local string key key = token unless key == '?' goto findkey key = 'PRINT' findkey: key = upcase key .local pmc keywords keywords = get_hll_global 'keywords' $I0 = keywords .local pmc func func = keywords [key] $I0 = defined func if $I0 goto exec .local pmc op op = tokenizer.'get'() eq op, '=', assign eq op, '[', keyed goto fail assign: .local pmc value value = self.'evaluate'(tokenizer) self.'set_var'(key, value) goto next keyed: .local pmc obj, index, auxobj obj = self.'get_var'(key) keyed_next: index = self.'evaluate'(tokenizer) op = tokenizer.'get'() if_null op, fail eq op, ']', last ne op, ',', fail auxobj = obj[index] null index null obj obj = auxobj null auxobj goto keyed_next last: op = tokenizer.'get'() ne op, '=', fail value = self.'evaluate'(tokenizer) obj[index] = value goto next fail: SyntaxError() exec: self.func(tokenizer) next: .end #----------------------------------------------------------------------- .sub throw_typed .param pmc payload .param int type :optional .param int has_type :opt_flag .local pmc excep, ex_severity excep = new 'Exception' ex_severity = new 'Integer' ex_severity= .EXCEPT_NORMAL unless has_type goto setattrs .local pmc ex_type ex_type = new 'Integer' ex_type = type setattribute excep, 'type', ex_type setattrs: setattribute excep, 'severity', ex_severity setattribute excep, 'payload', payload throw excep .end #----------------------------------------------------------------------- .sub throw_jump .param pmc payload .param int jumpline $P0 = new 'Integer' $P0 = jumpline setattribute payload, 'jumpline', $P0 throw_typed(payload) .end #----------------------------------------------------------------------- .sub func_CLEAR :method .param pmc tokenizer self.'clear_all'() .end .sub func_CONT :method .param pmc tokenizer .local pmc cont cont = new 'Cont' throw_typed(cont) .end .sub func_END :method .param pmc tokenizer .local pmc end end = new 'End' throw_typed(end) .end .sub func_EXIT :method .param pmc tokenizer .local pmc ex_exit ex_exit = new 'Exit' throw_typed(ex_exit) .end .sub func_ERROR :method .param pmc tokenizer .local pmc arg arg = self.'evaluate'(tokenizer) .local string msg msg = arg UserError(msg) .end .sub func_FOR :method .param pmc tokenizer .local pmc pvar pvar = tokenizer.'get'() .local string var var = pvar var = upcase var $P0 = tokenizer.'get'() ne $P0, '=', fail .local pmc value value = self.'evaluate'(tokenizer) $P0 = tokenizer.'get'() $S0 = $P0 $S0 = upcase $S0 ne $S0, 'TO', fail .local pmc limit limit = self.'evaluate'(tokenizer) .local pmc increment $P0 = tokenizer.'get'() $I0 = defined $P0 unless $I0 goto default_step $S0 = $P0 $S0 = upcase $S0 ne $S0, 'STEP', fail increment = self.'evaluate'(tokenizer) goto prepare default_step: increment = new 'Integer' increment = 1 prepare: .local pmc for for = new 'For' .local pmc line line = self.'getcurline'() setattribute for, 'jumpline', line setattribute for, 'increment', increment setattribute for, 'limit', limit .local pmc vars, controlvar vars = getattribute self, 'vars' vars[var] = value controlvar = vars[var] $P0 = new 'String' $P0 = var setattribute for, 'controlvar', $P0 .local pmc stack stack = getattribute self, 'stack' push stack, for .return() fail: SyntaxError() .end .sub func_GOTO :method .param pmc tokenizer .local pmc arg arg = tokenizer.'get'() $I0 = defined arg unless $I0 goto fail $I0 = arg .local pmc line line = new 'Jump' throw_jump(line, $I0) fail: SyntaxError() .end .sub func_GOSUB :method .param pmc tokenizer .local pmc arg arg = tokenizer.'get'() $I0 = defined arg unless $I0 goto fail $I0 = arg .local pmc line line = new 'Jump' $P1 = new 'Integer' $P1 = 1 setattribute line, 'jumptype', $P1 throw_jump(line, $I0) fail: SyntaxError() .end .sub func_IF :method .param pmc tokenizer .local pmc arg .local pmc token arg = self.'evaluate'(tokenizer) token = tokenizer.'get'() $I0 = defined token unless $I0 goto fail $S0 = token $S0 = upcase $S0 ne $S0, 'THEN', fail $I0 = defined arg unless $I0 goto is_false $I0 = arg unless $I0 goto is_false self.'execute'(tokenizer) goto finish is_false: .local int level level = 1 # Search for ELSE, taking nested IF into account nextitem: $P0 = tokenizer.'get' () $I0 = defined $P0 unless $I0 goto finish $I0 = isa $P0, 'String' unless $I0 goto nextitem $S0 = $P0 $S0 = upcase $S0 eq $S0, 'ELSE', is_else eq $S0, 'IF', is_if goto nextitem is_if: inc level goto nextitem is_else: dec level if level > 0 goto nextitem self.'execute'(tokenizer) finish: .return() fail: SyntaxError() .end .sub func_LIST :method .param pmc tokenizer .local pmc program program = getattribute self, 'program' program.'list'(0, 0) .end .sub func_LOAD :method .param pmc tokenizer .local pmc arg arg = self.'evaluate'(tokenizer) $P1 = tokenizer.'get'() if_null $P1, notype $I1 = defined $P1 unless $I1 goto notype ne $P1, ',', notype $P1 = tokenizer.'get'() $I1 = defined $P1 unless $I1 goto fail $S1 = $P1 $S1 = upcase $S1 ne $S1, 'B', fail $S1 = arg pirric_aux_loadbytecode($S1) .return() notype: .local pmc program, newprogram newprogram = new ['Program'] .local string filename filename = arg newprogram.'load'(filename) setattribute self, 'program', newprogram .local pmc end end = new 'End' throw_typed(end) fail: SyntaxError() .end .sub func_NEXT :method .param pmc tokenizer .local pmc stack stack = getattribute self, 'stack' $I0 = stack dec $I0 .local pmc for for = stack[$I0] .local pmc controlvar, varvalue, increment, limit controlvar = getattribute for, 'controlvar' varvalue = self.'get_var'(controlvar) increment = getattribute for, 'increment' limit = getattribute for, 'limit' $P0 = clone varvalue add $P0, increment self.'set_var'(controlvar, $P0) lt increment, 0, negstep gt $P0, limit, endloop goto jump negstep: lt $P0, limit, endloop jump: .local pmc jumpline jumpline = getattribute for, 'jumpline' .local pmc line line = new 'Next' throw_jump(line,jumpline) .return() endloop: $P0 = pop stack .end .sub func_NEW :method .param pmc tokenizer .local pmc newprogram newprogram = new ['Program'] setattribute self, 'program', newprogram self.'clear_all'() .local pmc end end = new 'End' throw_typed(end) .end .sub func_ON :method .param pmc tokenizer .local pmc token token = tokenizer.'get'() $S0 = token $S0 = upcase $S0 if $S0 == 'ERROR' goto on_error goto fail on_error: token = tokenizer.'get'() $S0 = token $S0 = upcase $S0 if $S0 == 'GOTO' goto on_error_goto if $S0 == 'EXIT' goto on_error_exit goto fail on_error_exit: $P0 = self.'evaluate'(tokenizer) $I0 = $P0 self.'set_error_exit'($I0) goto finish on_error_goto: $P0 = tokenizer.'get'() $I0 = defined $P0 unless $I0 goto fail $I0 = $P0 self.'set_error_goto'($I0) goto finish fail: SyntaxError() finish: .end .sub func_PRINT :method .param pmc tokenizer .local pmc arg arg = tokenizer.'get'() $I0 = defined arg unless $I0 goto endline item: $S0 = arg $S0 = upcase $S0 eq $S0, 'ELSE', endline arg = self.'evaluate'(tokenizer, arg) print_it: print arg arg = tokenizer.'get'() $I0 = defined arg unless $I0 goto endline eq arg, ';', nextitem eq arg, ',', comma $S0 = arg $S0 = upcase $S0 eq $S0, 'ELSE', endline SyntaxError() comma: print "\t" goto nextitem fail: SyntaxError() endline: say '' .return() nextitem: arg = tokenizer.'get'() $I0 = defined arg unless $I0 goto finish $S0 = arg $S0 = upcase $S0 eq $S0, 'ELSE', finish goto item finish: .end .sub func_REM :method .param pmc tokenizer # Do nothing .end .sub func_RETURN :method .param pmc tokenizer .local pmc line line = new 'Return' throw_typed(line, .CONTROL_RETURN) fail: SyntaxError() .end .sub func_RUN :method .param pmc tokenizer self.'clear_all'() .local pmc program, iter program = getattribute self, 'program' iter = program.'begin'() .local int numline numline = 0 unless iter goto doit numline = shift iter doit: .local pmc line line = new 'Jump' throw_jump(line, numline) .end .sub func_SAVE :method .param pmc tokenizer .local pmc arg arg = self.'evaluate'(tokenizer) $P1 = tokenizer.'get'() $I1 = defined $P1 if $I1 goto fail .local string filename filename = arg .local pmc program program = getattribute self, 'program' program.'save'(filename) .return() fail: SyntaxError() .end .sub func_STOP :method .param pmc tokenizer .local pmc line line = new 'Stop' throw_typed(line) .end .sub func_TROFF :method .param pmc tokenizer self.'trace'(0) .end .sub func_TRON :method .param pmc tokenizer self.'trace'(1) .end ######################################################################## .namespace [ 'Tokenizer' ] #----------------------------------------------------------------------- .sub 'newTokenizer' .param string line .local pmc tkn .local pmc l tkn = new ['Tokenizer'] l = new 'String' l = line setattribute tkn, 'line', l $P0 = new 'Integer' $P0 = 0 setattribute tkn, 'pos', $P0 .return(tkn) .end #----------------------------------------------------------------------- .sub get :method .local pmc pending .local pmc last pending = getattribute self, 'pending' if_null pending, getnext null $P1 setattribute self, 'pending', $P1 last = clone pending setattribute self, 'last', last .return(pending) getnext: .local string line $P0 = getattribute self, 'line' line = $P0 .local pmc pos pos = getattribute self, 'pos' .local int i, l l = length line i = pos .local string result result = '' .local pmc objres .local string c loop: ge i, l, endline c = substr line, i, 1 inc i eq c, ' ', loop eq c, "\n", endline eq c, '.', operator eq c, ',', operator eq c, ';', operator eq c, '=', operator eq c, '+', operator eq c, '-', operator eq c, '*', operator eq c, '/', operator eq c, '^', operator eq c, '<', operator eq c, '>', operator eq c, '(', operator eq c, ')', operator eq c, '[', operator eq c, ']', operator eq c, '?', operator eq c, '"', str $I0 = ord c $I1 = ord '9' gt $I0, $I1, nextchar $I1 = ord '0' lt $I0, $I1, nextchar # Number .local string snum snum = '' snum = concat snum, c #say value nextnum: ge i, l, endnum c = substr line, i, 1 eq c, '.', floatnum $I0 = ord c $I1 = ord '9' gt $I0, $I1, endnum $I1 = ord '0' lt $I0, $I1, endnum inc i snum = concat snum, c #say value goto nextnum endnum: .local int value value = snum objres = new 'Integer' objres = value goto doit floatnum: snum = concat snum, c inc i nextfloat: ge i, l, endfloat c = substr line, i, 1 $I0 = ord c $I1 = ord '9' gt $I0, $I1, endfloat $I1 = ord '0' lt $I0, $I1, endfloat inc i snum = concat snum, c goto nextfloat endfloat: .local num floatvalue #say snum floatvalue = snum objres = new 'Float' objres = floatvalue goto doit operator: result = c goto endtoken nextchar: result = concat result, c ge i, l, endtoken c = substr line, i , 1 eq c, ' ', endtoken eq c, "\n", endtoken eq c, '"', endtoken eq c, '.', endtoken eq c, ',', endtoken eq c, ';', endtoken eq c, '=', endtoken eq c, '+', endtoken eq c, '-', endtoken eq c, '*', endtoken eq c, '/', endtoken eq c, '^', endtoken eq c, '<', endtoken eq c, '>', endtoken eq c, '(', endtoken eq c, ')', endtoken eq c, '[', endtoken eq c, ']', endtoken inc i goto nextchar endtoken: objres = new 'String' objres = result goto doit str: ge i, l, endstr c = substr line, i, 1 inc i eq c, '"', checkquote result = concat result, c goto str checkquote: ge i, l, endstr c = substr line, i, 1 ne c, '"', endstr inc i result = concat result, c goto str endstr: objres = new 'Literal' objres = result goto doit endline: # last = new 'Undef' null last setattribute self, 'last', last .local pmc none # none = new 'Undef' null none .return(none) doit: pos = i last = clone objres setattribute self, 'last', last .return(objres) .end #----------------------------------------------------------------------- .sub back :method $P0 = getattribute self, 'last' setattribute self, 'pending', $P0 .end #----------------------------------------------------------------------- .sub getall :method .local string line $P0 = getattribute self, 'line' line = $P0 .local pmc pos pos = getattribute self, 'pos' .local int i, l l = length line i = pos loop: ge i, l, endline .local string c c = substr line, i, 1 inc i eq c, ' ', loop eq c, "\n", endline dec i endline: .local string str str = substr line, i .return(str) .end ######################################################################## .namespace ['Program'] #----------------------------------------------------------------------- .sub init :vtable .local pmc text .local pmc lines # say 'Program.init' text = new 'Hash' lines = new 'ResizableIntegerArray' setattribute self, 'text', text setattribute self, 'lines', lines .end #----------------------------------------------------------------------- .sub elements :method :vtable .local pmc text text = getattribute self, 'text' $I0 = text .return($I0) .end #;----------------------------------------------------------------------- .sub get_string_keyed :vtable .param pmc key #say key .local pmc text text = getattribute self, 'text' $S0 = text[key] .return($S0) .end #----------------------------------------------------------------------- .sub begin :method .local pmc text text = getattribute self, 'lines' iter $P0, text set $P0, .ITERATE_FROM_START .return($P0) .end #----------------------------------------------------------------------- .sub storeline :method .param int linenum .param string line .local pmc text, lines .local int n, i, j, curnum text = getattribute self, 'text' lines = getattribute self, 'lines' n = lines i = 0 next: ge i, n, storenum curnum = lines [i] ge curnum, linenum, storeit inc i goto next storeit: eq curnum, linenum, storeline j = n nextmove: dec j curnum = lines [j] lines [n] = curnum dec n gt n, i, nextmove storenum: lines [i] = linenum storeline: text [linenum] = line .end #----------------------------------------------------------------------- .sub deleteline :method .param int linenum .local pmc text, lines .local int n, i, j, curnum text = getattribute self, 'text' lines = getattribute self, 'lines' n = lines i = 0 next: ge i, n, notexist curnum = lines [i] ge curnum, linenum, foundnum inc i goto next foundnum: gt i, n, notexist delete text[linenum] delete lines[i] .return() notexist: .end #----------------------------------------------------------------------- .sub load :method .param string filename .local pmc file .local string line .local pmc tokenizeline .local pmc token .local int linenum .local int linecount #say filename open file, filename, 'r' linecount = 0 nextline: line = readlinebas(file) unless line goto eof unless linecount == 0 goto enterline $S0 = substr line, 0, 1 if $S0 == '#' goto nextline enterline: inc linecount tokenizeline = newTokenizer(line) token = tokenizeline.'get'() linenum = token unless linenum goto fail line = tokenizeline.'getall'() self.'storeline'(linenum, line) goto nextline eof: close file $I0 = self.'elements'() unless $I0 == linecount goto fatal .return() fail: SyntaxError() fatal: FatalError('Incorrect count when loading file') .end #----------------------------------------------------------------------- .sub save :method .param string filename .local pmc file .local pmc program open file, filename, 'w' self.'list'(0, 0, file) close file .end #----------------------------------------------------------------------- .sub list :method .param int start .param int stop .param pmc file :optional .param int has_file :opt_flag if has_file goto do_list file = getstdout do_list: gt start, stop, finish .local pmc lines, text lines = getattribute self, 'lines' text = getattribute self, 'text' .local int i, n, linenum .local string content n = lines # say n i = 0 nextline: ge i, n, finish linenum = lines [i] lt linenum, start, skip unless stop > 0 goto list_it gt linenum, stop, finish list_it: content = text [linenum] print file, linenum print file, ' ' print file, content print file, "\n" skip: inc i goto nextline finish: .end ######################################################################## # Local Variables: # mode: pir # fill-column: 100 # End: # vim: expandtab shiftwidth=4 ft=pir: