That way, we no longer need to manually remove the trailing slash before calling `join_path`.
Pull-Request: #833
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
--- /dev/null
+# Brainfuck
+
+Brainfuck is as its name implies a simple Brainfuck interpreter written in Nit.
+
+It has almost as much purposes as the language itself, except it provides a good example for Nit programs that work while being concise.
+
+[Specification](http://www.muppetlabs.com/~breadbox/bf/)
+
+The language is designed to need only a few things :
+
+* One instruction pointer to the current instruction
+* One array of Bytes for all manipulations of data
+* One data pointer to select where to write/read data
+
+Brainfuck a small instruction set, only eight instructions :
+
+* `>`: Increments the data pointer
+* `<`: Decrements the data pointer
+* `+`: Increments the byte in the current cell
+* `-`: Decrements the byte in the current cell
+* `[`: If the current cell's value is 0, jumps to the matching `]`
+* `]`: If the current cell's value is non-zero, jumps to the matching `[`
+* `.`: Writes the current cell's value to stdout
+* `,`: Reads a char from stdin and stores it in the current cell
+
+## How to use
+
+First, compile the interpreter with the Nit compiler/interpreter, and launch the program on a brainfuck source file for interpretation.
+
+Example:
+~~~
+nitg ./brainfuck.nit
+./brainfuck ./examples/hello.bf
+~~~
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Simple brainfuck interpreter
+module brainfuck
+
+# Interpreter for Brainfuck source code.
+class BFInterpret
+ # Data cells
+ var dr = new Array[Char]
+ # Data pointer
+ var dp = 0
+ # Instruction pointer
+ var ip = 0
+
+ # The program being interpreted
+ var program: String
+
+ # Contains the set of valid instructions, used in next
+ var valid_instr: Set[Char]
+
+ # Starts interpretation of file `filename`
+ init(filename: String) do
+ var ifs = new IFStream.open(filename.simplify_path)
+ valid_instr = new HashSet[Char]
+ valid_instr.add_all "><[].,+-".chars
+ dr.add 0.ascii
+ program = ifs.read_all
+ start
+ end
+
+ # Starts the interpretation of the loaded program
+ fun start do
+ loop
+ if ip >= program.length then break
+ eval
+ next
+ end
+ end
+
+ # Go to the next executable instruction
+ fun next do
+ ip += 1
+ while ip < program.length and not valid_instr.has(program[ip]) do
+ ip += 1
+ end
+ end
+
+ # Evaluates the current instruction
+ fun eval do
+ var instr = program[ip]
+ if instr == '.' then printn dr[dp]
+ if instr == '[' then
+ if dr[dp] == 0.ascii then
+ ip = find_matching_rbra
+ return
+ end
+ end
+ if instr == ']' then
+ if dr[dp] != 0.ascii then
+ ip = find_matching_lbra
+ return
+ end
+ end
+ if instr == '>' then
+ dp += 1
+ if dp >= dr.length then dr.add(0.ascii)
+ end
+ if instr == '<' then
+ dp -= 1
+ if dp < 0 then abort
+ end
+ if instr == '+' then
+ dr[dp] = (dr[dp].ascii + 1).ascii
+ end
+ if instr == '-' then
+ dr[dp] = (dr[dp].ascii - 1).ascii
+ end
+ if instr == ',' then
+ dr[dp] = getc
+ end
+ end
+
+ # Seeks for the position of the matching `]` for the `[` located at `ip`
+ fun find_matching_rbra: Int do
+ var pos = ip + 1
+ var lbracnt = 0
+ loop
+ if pos > program.length then abort
+ if program[pos] == ']' then
+ if lbracnt > 0 then
+ lbracnt -= 1
+ else
+ break
+ end
+ end
+ if program[pos] == '[' then lbracnt += 1
+ pos += 1
+ end
+ return pos
+ end
+
+ # Seeks for the position of the matching `[` for the `]` located at `ip`
+ fun find_matching_lbra: Int do
+ var pos = ip - 1
+ var rbracnt = 0
+ loop
+ if pos < 0 then abort
+ if program[pos] == '[' then
+ if rbracnt > 0 then
+ rbracnt -= 1
+ else
+ break
+ end
+ end
+ if program[pos] == ']' then rbracnt += 1
+ pos -= 1
+ end
+ return pos
+ end
+end
+
+var i = new BFInterpret(args[0])
--- /dev/null
+++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
--- /dev/null
+[ This program prints "Hello World!" and a newline to the screen, its
+ length is 106 active command characters [it is not the shortest.]
+ This loop is a "comment loop", it's a simple way of adding a comment
+ to a BF program such that you don't have to worry about any command
+ characters. Any ".", ",", "+", "-", "<" and ">" characters are simply
+ ignored, the "[" and "]" characters just have to be balanced.
+]
++++++ +++ Set Cell #0 to 8
+[
+ >++++ Add 4 to Cell #1; this will always set Cell #1 to 4
+ [ as the cell will be cleared by the loop
+ >++ Add 2 to Cell #2
+ >+++ Add 3 to Cell #3
+ >+++ Add 3 to Cell #4
+ >+ Add 1 to Cell #5
+ <<<<- Decrement the loop counter in Cell #1
+ ] Loop till Cell #1 is zero; number of iterations is 4
+ >+ Add 1 to Cell #2
+ >+ Add 1 to Cell #3
+ >- Subtract 1 from Cell #4
+ >>+ Add 1 to Cell #6
+ [<] Move back to the first zero cell you find; this will
+ be Cell #1 which was cleared by the previous loop
+ <- Decrement the loop Counter in Cell #0
+] Loop till Cell #0 is zero; number of iterations is 8
+The result of this is:
+Cell No : 0 1 2 3 4 5 6
+Contents: 0 0 72 104 88 32 8
+Pointer : ^
+>>. Cell #2 has value 72 which is 'H'
+>---. Subtract 3 from Cell #3 to get 101 which is 'e'
++++++++..+++. Likewise for 'llo' from Cell #3
+>>. Cell #5 is 32 for the space
+<-. Subtract 1 from Cell #4 for 87 to give a 'W'
+<. Cell #3 was set to 'o' from the end of 'Hello'
++++.------.--------. Cell #3 for 'rl' and 'd'
+>>+. Add 1 to Cell #5 gives us an exclamation point
+>++. And finally a newline from Cell #6
--- /dev/null
+-,+[ Read first character and start outer character reading loop
+ -[ Skip forward if character is 0
+ >>++++[>++++++++<-] Set up divisor (32) for division loop
+ (MEMORY LAYOUT: dividend copy remainder divisor quotient zero zero)
+ <+<-[ Set up dividend (x minus 1) and enter division loop
+ >+>+>-[>>>] Increase copy and remainder / reduce divisor / Normal case: skip forward
+ <[[>+<-]>>+>] Special case: move remainder back to divisor and increase quotient
+ <<<<<- Decrement dividend
+ ] End division loop
+ ]>>>[-]+ End skip loop; zero former divisor and reuse space for a flag
+ >--[-[<->+++[-]]]<[ Zero that flag unless quotient was 2 or 3; zero quotient; check flag
+ ++++++++++++<[ If flag then set up divisor (13) for second division loop
+ (MEMORY LAYOUT: zero copy dividend divisor remainder quotient zero zero)
+ >-[>+>>] Reduce divisor; Normal case: increase remainder
+ >[+[<+>-]>+>>] Special case: increase remainder / move it back to divisor / increase quotient
+ <<<<<- Decrease dividend
+ ] End division loop
+ >>[<+>-] Add remainder back to divisor to get a useful 13
+ >[ Skip forward if quotient was 0
+ -[ Decrement quotient and skip forward if quotient was 1
+ -<<[-]>> Zero quotient and divisor if quotient was 2
+ ]<<[<<->>-]>> Zero divisor and subtract 13 from copy if quotient was 1
+ ]<<[<<+>>-] Zero divisor and add 13 to copy if quotient was 0
+ ] End outer skip loop (jump to here if ((character minus 1)/32) was not 2 or 3)
+ <[-] Clear remainder from first division if second division was skipped
+ <.[-] Output ROT13ed character from copy and clear it
+ <-,+ Read next character
+] End character reading loop
fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
- # Transform varargs, in raw arguments, into a single argument of type `Array`
- # Note: this method modify the given `args`
- # If there is no vararg, then `args` is not modified.
- fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
+ # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
+ # This method is used to manage varargs in signatures and returns the real array
+ # of runtime variables to use in the call.
+ fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
do
- var recv = args.first
- var vararg_rank = msignature.vararg_rank
- if vararg_rank >= 0 then
- assert args.length >= msignature.arity + 1 # because of self
- var rawargs = args
- args = new Array[RuntimeVariable]
-
- args.add(rawargs.first) # recv
+ var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+ var res = new Array[RuntimeVariable]
+ res.add(recv)
- for i in [0..vararg_rank[ do
- args.add(rawargs[i+1])
- end
-
- var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
- var vararg = new Array[RuntimeVariable]
- for i in [vararg_rank..vararg_lastrank] do
- vararg.add(rawargs[i+1])
- end
+ if args.is_empty then return res
- var elttype = msignature.mparameters[vararg_rank].mtype
- args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
+ var vararg_rank = msignature.vararg_rank
+ var vararg_len = args.length - msignature.arity
+ if vararg_len < 0 then vararg_len = 0
- for i in [vararg_lastrank+1..rawargs.length-1[ do
- args.add(rawargs[i+1])
+ for i in [0..msignature.arity[ do
+ if i == vararg_rank then
+ var ne = args[i]
+ if ne isa AVarargExpr then
+ var e = self.expr(ne.n_expr, null)
+ res.add(e)
+ continue
+ end
+ var vararg = new Array[RuntimeVariable]
+ for j in [vararg_rank..vararg_rank+vararg_len] do
+ var e = self.expr(args[j], null)
+ vararg.add(e)
+ end
+ var elttype = msignature.mparameters[vararg_rank].mtype
+ var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
+ res.add(arg)
+ else
+ var j = i
+ if i > vararg_rank then j += vararg_len
+ var e = self.expr(args[j], null)
+ res.add(e)
end
- rawargs.clear
- rawargs.add_all(args)
end
+ return res
end
# Type handling
redef fun expr(v)
do
var recv = v.expr(self.n_expr, null)
- var args = [recv]
- for a in self.raw_arguments do
- args.add(v.expr(a, null))
- end
- return v.compile_callsite(self.callsite.as(not null), args)
+ var callsite = self.callsite.as(not null)
+ var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ return v.compile_callsite(callsite, args)
end
end
redef fun stmt(v)
do
var recv = v.expr(self.n_expr, null)
- var args = [recv]
- for a in self.raw_arguments do
- args.add(v.expr(a, null))
- end
+ var callsite = self.callsite.as(not null)
+ var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+
var value = v.expr(self.n_value, null)
- var left = v.compile_callsite(self.callsite.as(not null), args)
+ var left = v.compile_callsite(callsite, args)
assert left != null
var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
redef fun expr(v)
do
var recv = v.frame.arguments.first
- var args = [recv]
- for a in self.n_args.n_exprs do
- args.add(v.expr(a, null))
- end
var callsite = self.callsite
if callsite != null then
- # Add additionnals arguments for the super init call
+ var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+
+ # Add additional arguments for the super init call
if args.length == 1 then
for i in [0..callsite.msignature.arity[ do
args.add(v.frame.arguments[i+1])
return res
end
+ var mpropdef = self.mpropdef.as(not null)
+ var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
if args.length == 1 then
args = v.frame.arguments
end
# stantard call-next-method
- return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
+ return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
end
end
else
recv = v.new_expr("({ctype})0/*special!*/", mtype)
end
- var args = [recv]
- for a in self.n_args.n_exprs do
- args.add(v.expr(a, null))
- end
- var res2 = v.compile_callsite(self.callsite.as(not null), args)
+
+ var callsite = self.callsite.as(not null)
+ var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ var res2 = v.compile_callsite(callsite, args)
if res2 != null then
#self.debug("got {res2} from {mproperty}. drop {recv}")
return res2
return recvtype
end
- # Subpart of old call function
- # Gets the receiver boxed and casted if necessary
- private fun get_recv(recvtype: MClassType, args: Array[RuntimeVariable]): RuntimeVariable
+ redef fun call(m, recvtype, args)
do
- return self.autoadapt(self.autobox(args.first, recvtype), recvtype)
- end
+ var recv_type = get_recvtype(m, recvtype, args)
+ var recv = self.autoadapt(self.autobox(args.first, recvtype), recvtype)
+ if m.is_extern then recv = unbox_extern(recv, recv_type)
+
+ args = args.to_a
+ args.first = recv
- # Finalizes a call to a method ´m´ on type ´recvtype´ with arguments ´args´
- private fun finalize_call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
- do
assert args.length == m.msignature.arity + 1 else debug("Invalid arity for {m}. {args.length} arguments given.")
var rm = new CustomizedRuntimeFunction(m, recvtype)
return rm.call(self, args)
end
- redef fun call(m, recvtype, args)
- do
- var recv_type = get_recvtype(m, recvtype, args)
- var recv = get_recv(recv_type, args)
- if m.is_extern then recv = unbox_extern(recv, recv_type)
- var new_args = args.to_a
- self.varargize(m, m.msignature.as(not null), new_args)
- new_args.first = recv
- return finalize_call(m, recv_type, new_args)
- end
-
- # Does a call without encapsulating varargs into an array
- # Avoids multiple encapsulation when calling a super in a variadic function
- fun call_without_varargize(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
- do
- var recv_type = get_recvtype(m, recvtype, args)
- var recv = get_recv(recv_type, args)
- if m.is_extern then recv = unbox_extern(recv, recv_type)
- var new_args = args.to_a
- new_args.first = recv
- return finalize_call(m, recv_type, new_args)
- end
-
redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
do
var types = self.collect_types(args.first)
return res
end
var propdef = m.lookup_next_definition(self.compiler.mainmodule, mclasstype)
- var res2 = self.call_without_varargize(propdef, mclasstype, args)
+ var res2 = self.call(propdef, mclasstype, args)
if res != null then self.assign(res, res2.as(not null))
return res
end
else
self.add("case {self.compiler.classid(t)}: /* test {t} */")
end
- var res2 = self.call_without_varargize(propdef, t, args)
+ var res2 = self.call(propdef, t, args)
if res != null then self.assign(res, res2.as(not null))
self.add "break;"
end
var tgs = rta.live_targets(callsite)
if tgs.length == 1 then
# DIRECT CALL
- self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
var res0 = before_send(mmethod, args)
var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
if res0 != null then
end
redef fun send(mmethod, arguments)
do
- self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
-
if arguments.first.mcasttype.ctype != "val*" then
# In order to shortcut the primitive, we need to find the most specific method
# Howverr, because of performance (no flattening), we always work on the realmainmodule
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2013 Lucas Bajolet <lucas.bajolet@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Classes and methods relative to the management of Breakpoints for the Debugger
-module breakpoint
-
-# Contains all the informations of a Breakpoint for the Debugger
-class Breakpoint
-
- # Line to break on
- var line: Int
-
- # File concerned by the breakpoint
- var file: String
-
- # Maximum times to break on self
- var max_breaks: Int
-
- init(line: Int, file: String)
- do
- self.line = line
- self.file = file
- self.max_breaks = -1
- end
-
- fun set_max_breaks(breaks: Int)
- do
- self.max_breaks = breaks
- end
-
- # When the breakpoint is encountered, the check-in function should be called
- fun check_in
- do
- if self.max_breaks > 0 then self.max_breaks -= 1
- end
-
- # Checks if the breakpoint is still valid (that is, if it has a remaining breaks number > 0 or == -1)
- fun is_valid: Bool
- do
- if max_breaks == 0 then
- return false
- else
- return true
- end
- end
-
-end
# Debugging of a nit program using the NaiveInterpreter
module debugger
-import breakpoint
intrude import naive_interpreter
import nitx
intrude import semantize::local_var_init
end
end
+# Contains all the informations of a Breakpoint for the Debugger
+class Breakpoint
+
+ # Line to break on
+ var line: Int
+
+ # File concerned by the breakpoint
+ var file: String
+
+ redef init do
+ if not file.has_suffix(".nit") then file += ".nit"
+ end
+end
+
# The class extending `NaiveInterpreter` by adding debugging methods
class Debugger
super NaiveInterpreter
end
# Same as a regular call but for a runtime injected module
- #
fun rt_call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
do
- args = call_commons(mpropdef, args)
- return rt_call_without_varargs(mpropdef, args)
- end
-
- # Common code to call and this function
- #
- # Call only executes the variadic part, this avoids
- # double encapsulation of variadic parameters into an Array
- fun rt_call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
- do
if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then
self.discover_call_trace.add mpropdef
self.debug("Discovered {mpropdef}")
var breakpoint = find_breakpoint(curr_file, n.location.line_start)
if breakpoints.keys.has(curr_file) and breakpoint != null then
-
- breakpoint.check_in
-
- if not breakpoint.is_valid
- then
- remove_breakpoint(curr_file, n.location.line_start)
- end
-
n.debug("Execute stmt {n.to_s}")
while read_cmd do end
end
#
# Returns a boolean value, representing whether or not to
# continue reading commands from the console input
- fun process_debug_command(command:String): Bool
+ fun process_debug_command(command: String): Bool
do
# Step-out command
if command == "finish"
# Step-over command
else if command == "n" then
return step_over
+ # Shows help
+ else if command == "help" then
+ help
+ return true
# Opens a new NitIndex prompt on current model
else if command == "nitx" then
new NitIndex.with_infos(modelbuilder, self.mainmodule).prompt
return true
+ else if command == "bt" or command == "backtrack" then
+ print stack_trace
+ return true
# Continues execution until the end
else if command == "c" then
return continue_exec
print stack_trace
exit(0)
else
- var parts_of_command = command.split_with(' ')
+ var parts = command.split_with(' ')
+ var cname = parts.first
# Shows the value of a variable in the current frame
- if parts_of_command[0] == "p" or parts_of_command[0] == "print" then
- print_command(parts_of_command)
+ if cname == "p" or cname == "print" then
+ print_command(parts)
# Places a breakpoint on line x of file y
- else if parts_of_command[0] == "break" or parts_of_command[0] == "b"
- then
- process_place_break_fun(parts_of_command)
- # Places a temporary breakpoint on line x of file y
- else if parts_of_command[0] == "tbreak" and (parts_of_command.length == 2 or parts_of_command.length == 3)
- then
- process_place_tbreak_fun(parts_of_command)
+ else if cname == "break" or cname == "b" then
+ process_place_break_fun(parts)
# Removes a breakpoint on line x of file y
- else if parts_of_command[0] == "d" or parts_of_command[0] == "delete" then
- process_remove_break_fun(parts_of_command)
+ else if cname == "d" or cname == "delete" then
+ process_remove_break_fun(parts)
# Sets an alias for a variable
- else if parts_of_command.length == 3 and parts_of_command[1] == "as"
- then
- add_alias(parts_of_command[0], parts_of_command[2])
+ else if parts.length == 2 and parts[1] == "as" then
+ process_alias(parts)
# Modifies the value of a variable in the current frame
- else if parts_of_command.length >= 3 and parts_of_command[1] == "=" then
- process_mod_function(parts_of_command)
+ else if parts.length == 3 and parts[1] == "=" then
+ process_mod_function(parts)
# Traces the modifications on a variable
- else if parts_of_command.length >= 2 and parts_of_command[0] == "trace" then
- process_trace_command(parts_of_command)
+ else if cname == "trace" then
+ process_trace_command(parts)
# Untraces the modifications on a variable
- else if parts_of_command.length == 2 and parts_of_command[0] == "untrace" then
- process_untrace_command(parts_of_command)
+ else if cname == "untrace" then
+ process_untrace_command(parts)
else
- print "Unknown command \"{command}\""
+ bad_command(command)
end
end
return true
end
+ # Produces help for the commands of the debugger
+ fun help do
+ print ""
+ print "Help :"
+ print "-----------------------------------"
+ print ""
+ print "Variables"
+ print " * Modification: var_name = value (Warning: var_name must be primitive)"
+ print " * Alias: var_name as alias"
+ print ""
+ print "Printing"
+ print " * Variables: p(rint) var_name (Use * to print all local variables)"
+ print " * Collections: p(rint) var_name '[' start_index (.. end_index) ']'"
+ print ""
+ print "Breakpoints"
+ print " * File/line: b(reak) file_name line_number"
+ print " * Remove: d(elete) id"
+ print ""
+ print "Tracepoints"
+ print " * Variable: trace var_name break/print"
+ print " * Untrace variable: untrace var_name"
+ print ""
+ print "Flow control"
+ print " * Next instruction (same-level): n"
+ print " * Next instruction: s"
+ print " * Finish current method: finish"
+ print " * Continue until next breakpoint or end: c"
+ print ""
+ print "General commands"
+ print " * quit: Quits the debugger"
+ print " * abort: Aborts the interpretation, prints the stack trace before leaving"
+ print " * nitx: Ask questions to the model about its entities (classes, methods, etc.)"
+ print " * nit: Inject dynamic code for interpretation"
+ print ""
+ end
+
#######################################################################
## Processing specific command functions ##
#######################################################################
return false
end
+ fun bad_command(cmd: String) do
+ print "Unrecognized command {cmd}. Use 'help' to show help."
+ end
+
# Prints the demanded variable in the command
#
# The name of the variable in in position 1 of the array 'parts_of_command'
- fun print_command(parts_of_command: Array[String])
+ fun print_command(parts: Array[String])
do
- if parts_of_command[1] == "*" then
+ if parts.length != 2 then
+ bad_command(parts.join(" "))
+ return
+ end
+ if parts[1] == "*" then
var map_of_instances = frame.map
var self_var = seek_variable("self", frame)
for instance in map_of_instances.keys do
print "{instance.to_s}: {map_of_instances[instance].to_s}"
end
- else if parts_of_command[1] == "stack" then
- print self.stack_trace
- else if parts_of_command[1].chars.has('[') and parts_of_command[1].chars.has(']') then
- process_array_command(parts_of_command)
+ else if parts[1].chars.has('[') and parts[1].chars.has(']') then
+ process_array_command(parts)
else
- var instance = seek_variable(get_real_variable_name(parts_of_command[1]), frame)
+ var instance = seek_variable(get_real_variable_name(parts[1]), frame)
if instance != null
then
print_instance(instance)
else
- print "Cannot find variable {parts_of_command[1]}"
+ print "Cannot find variable {parts[1]}"
end
end
end
+ # Process the input command to set an alias for a variable
+ fun process_alias(parts: Array[String]) do
+ if parts.length != 3 then
+ bad_command(parts.join(" "))
+ return
+ end
+ add_alias(parts.first, parts.last)
+ end
+
# Processes the input string to know where to put a breakpoint
- fun process_place_break_fun(parts_of_command: Array[String])
+ fun process_place_break_fun(parts: Array[String])
do
- var bp = get_breakpoint_from_command(parts_of_command)
+ if parts.length != 3 then
+ bad_command(parts.join(" "))
+ return
+ end
+ var bp = get_breakpoint_from_command(parts)
if bp != null then
place_breakpoint(bp)
end
end
# Returns a breakpoint containing the informations stored in the command
- fun get_breakpoint_from_command(parts_of_command: Array[String]): nullable Breakpoint
+ fun get_breakpoint_from_command(parts: Array[String]): nullable Breakpoint
do
- if parts_of_command[1].is_numeric then
- return new Breakpoint(parts_of_command[1].to_i, curr_file)
- else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
- return new Breakpoint(parts_of_command[2].to_i, parts_of_command[1])
+ if parts[1].is_numeric then
+ return new Breakpoint(parts[1].to_i, curr_file)
+ else if parts.length >= 3 and parts[2].is_numeric then
+ return new Breakpoint(parts[2].to_i, parts[1])
else
return null
end
end
# Processes the command of removing a breakpoint on specified line and file
- fun process_remove_break_fun(parts_of_command: Array[String])
+ fun process_remove_break_fun(parts: Array[String])
do
- if parts_of_command[1].is_numeric then
- remove_breakpoint(self.curr_file, parts_of_command[1].to_i)
- else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
- remove_breakpoint(parts_of_command[1], parts_of_command[2].to_i)
+ if parts.length != 2 then
+ bad_command(parts.join(" "))
+ return
+ end
+ if parts[1].is_numeric then
+ remove_breakpoint(self.curr_file, parts[1].to_i)
+ else if parts.length >= 3 and parts[2].is_numeric then
+ remove_breakpoint(parts[1], parts[2].to_i)
end
end
# Processes an array print command
- fun process_array_command(parts_of_command: Array[String])
+ fun process_array_command(parts: Array[String])
do
- var index_of_first_brace = parts_of_command[1].chars.index_of('[')
- var variable_name = get_real_variable_name(parts_of_command[1].substring(0,index_of_first_brace))
- var braces = parts_of_command[1].substring_from(index_of_first_brace)
+ var index_of_first_brace = parts[1].chars.index_of('[')
+ var variable_name = get_real_variable_name(parts[1].substring(0,index_of_first_brace))
+ var braces = parts[1].substring_from(index_of_first_brace)
var indexes = remove_braces(braces)
# Processes the modification function to modify a variable dynamically
#
# Command of type variable = value
- fun process_mod_function(parts_of_command: Array[String])
+ fun process_mod_function(parts: Array[String])
do
- parts_of_command[0] = get_real_variable_name(parts_of_command[0])
- var parts_of_variable = parts_of_command[0].split_with(".")
+ if parts.length != 3 then
+ bad_command(parts.join(" "))
+ return
+ end
+ var p0 = parts[0]
+ p0 = get_real_variable_name(p0)
+ var parts_of_variable = p0.split_with(".")
if parts_of_variable.length > 1 then
var last_part = parts_of_variable.pop
- var first_part = parts_of_command[0].substring(0,parts_of_command[0].length - last_part.length - 1)
+ var first_part = p0.substring(0,p0.length - last_part.length - 1)
var papa = seek_variable(first_part, frame)
if papa != null and papa isa MutableInstance then
var attribute = get_attribute_in_mutable_instance(papa, last_part)
if attribute != null then
- modify_argument_of_complex_type(papa, attribute, parts_of_command[2])
+ modify_argument_of_complex_type(papa, attribute, parts[2])
end
end
else
var target = seek_variable(parts_of_variable[0], frame)
if target != null then
- modify_in_frame(target, parts_of_command[2])
+ modify_in_frame(target, parts[2])
end
end
end
# Processes the untrace variable command
#
# Command pattern : "untrace variable"
- fun process_untrace_command(parts_of_command: Array[String])
+ fun process_untrace_command(parts: Array[String])
do
- var variable_name = get_real_variable_name(parts_of_command[1])
+ if parts.length != 2 then
+ bad_command(parts.join(" "))
+ return
+ end
+ var variable_name = get_real_variable_name(parts[1])
if untrace_variable(variable_name) then
- print "Untraced variable {parts_of_command[1]}"
+ print "Untraced variable {parts[1]}"
else
- print "{parts_of_command[1]} is not traced"
+ print "{parts[1]} is not traced"
end
end
# Processes the trace variable command
#
# Command pattern : "trace variable [break/print]"
- fun process_trace_command(parts_of_command: Array[String])
+ fun process_trace_command(parts: Array[String])
do
- var variable_name = get_real_variable_name(parts_of_command[1])
+ if parts.length != 3 then
+ bad_command(parts.join(" "))
+ return
+ end
+ var variable_name = get_real_variable_name(parts[1])
var breaker:Bool
if seek_variable(variable_name, frame) == null then
- print "Cannot find a variable called {parts_of_command[1]}"
+ print "Cannot find a variable called {parts[1]}"
return
end
- if parts_of_command.length == 3 then
- if parts_of_command[2] == "break" then
- breaker = true
- else
- breaker = false
- end
+ if parts[2] == "break" then
+ breaker = true
else
breaker = false
end
trace_variable(variable_name, breaker)
- print "Successfully tracing {parts_of_command[1]}"
+ print "Successfully tracing {parts[1]}"
end
#######################################################################
end
end
- #Places a breakpoint that will trigger once and be destroyed afterwards
- fun process_place_tbreak_fun(parts_of_command: Array[String])
- do
- var bp = get_breakpoint_from_command(parts_of_command)
- if bp != null
- then
- bp.set_max_breaks(1)
- place_breakpoint(bp)
- end
- end
-
#######################################################################
## Breakpoint removing functions ##
#######################################################################
# Store known methods, used to trace methods as they are reached
var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]
- # Common code for calls to injected methods and normal methods
- fun call_commons(mpropdef: MMethodDef, args: Array[Instance]): Array[Instance]
+ # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
+ # This method is used to manage varargs in signatures and returns the real array
+ # of instances to use in the call.
+ # Return `null` if one of the evaluation of the arguments return null.
+ fun varargize(mpropdef: MMethodDef, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
do
- var vararg_rank = mpropdef.msignature.vararg_rank
- if vararg_rank >= 0 then
- assert args.length >= mpropdef.msignature.arity + 1 # because of self
- var rawargs = args
- args = new Array[Instance]
+ var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+ var res = new Array[Instance]
+ res.add(recv)
- args.add(rawargs.first) # recv
+ if args.is_empty then return res
- for i in [0..vararg_rank[ do
- args.add(rawargs[i+1])
- end
-
- var vararg_lastrank = vararg_rank + rawargs.length-1-mpropdef.msignature.arity
- var vararg = new Array[Instance]
- for i in [vararg_rank..vararg_lastrank] do
- vararg.add(rawargs[i+1])
- end
- # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis
- var elttype = mpropdef.msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, args.first.mtype.as(MClassType))
- args.add(self.array_instance(vararg, elttype))
+ var vararg_rank = msignature.vararg_rank
+ var vararg_len = args.length - msignature.arity
+ if vararg_len < 0 then vararg_len = 0
- for i in [vararg_lastrank+1..rawargs.length-1[ do
- args.add(rawargs[i+1])
+ for i in [0..msignature.arity[ do
+ if i == vararg_rank then
+ var ne = args[i]
+ if ne isa AVarargExpr then
+ var e = self.expr(ne.n_expr)
+ if e == null then return null
+ res.add(e)
+ continue
+ end
+ var vararg = new Array[Instance]
+ for j in [vararg_rank..vararg_rank+vararg_len] do
+ var e = self.expr(args[j])
+ if e == null then return null
+ vararg.add(e)
+ end
+ var elttype = msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
+ res.add(self.array_instance(vararg, elttype))
+ else
+ var j = i
+ if i > vararg_rank then j += vararg_len
+ var e = self.expr(args[j])
+ if e == null then return null
+ res.add(e)
end
end
- return args
+ return res
end
# Execute `mpropdef` for a `args` (where `args[0]` is the receiver).
# The call is direct/static. There is no message-sending/late-binding.
fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
do
- args = call_commons(mpropdef, args)
- return call_without_varargs(mpropdef, args)
- end
-
- # Common code to call and this function
- #
- # Call only executes the variadic part, this avoids
- # double encapsulation of variadic parameters into an Array
- fun call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
- do
if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then
self.discover_call_trace.add mpropdef
self.debug("Discovered {mpropdef}")
if auto_super_call then
# standard call-next-method
var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype)
- v.call_without_varargs(superpd, arguments)
+ v.call(superpd, arguments)
end
if mpropdef.is_intern or mpropdef.is_extern then
if not mpropdef.is_intro then
# standard call-next-method
var superpd = mpropdef.lookup_next_definition(v.mainmodule, args.first.mtype)
- v.call_without_varargs(superpd, args)
+ v.call(superpd, args)
end
return null
else
do
var recv = v.expr(self.n_expr)
if recv == null then return null
- var args = [recv]
- for a in self.raw_arguments do
- var i = v.expr(a)
- if i == null then return null
- args.add(i)
- end
+ var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ if args == null then return null
var res = v.callsite(callsite, args)
return res
do
var recv = v.expr(self.n_expr)
if recv == null then return
- var args = [recv]
- for a in self.raw_arguments do
- var i = v.expr(a)
- if i == null then return
- args.add(i)
- end
+ var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+ if args == null then return
var value = v.expr(self.n_value)
if value == null then return
redef fun expr(v)
do
var recv = v.frame.arguments.first
- var args = [recv]
- for a in self.n_args.n_exprs do
- var i = v.expr(a)
- if i == null then return null
- args.add(i)
- end
var callsite = self.callsite
if callsite != null then
+ var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ if args == null then return null
# Add additional arguments for the super init call
if args.length == 1 then
for i in [0..callsite.msignature.arity[ do
return res
end
- if args.length == 1 then
- args = v.frame.arguments
- end
-
# standard call-next-method
var mpropdef = self.mpropdef
mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
- var res = v.call_without_varargs(mpropdef, args)
+
+ var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
+ if args == null then return null
+
+ if args.length == 1 then
+ args = v.frame.arguments
+ end
+ var res = v.call(mpropdef, args)
return res
end
end
var mtype = v.unanchor_type(self.mtype.as(not null))
var recv: Instance = new MutableInstance(mtype)
v.init_instance(recv)
- var args = [recv]
- for a in self.n_args.n_exprs do
- var i = v.expr(a)
- if i == null then return null
- args.add(i)
- end
+ var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+ if args == null then return null
var res2 = v.callsite(callsite, args)
if res2 != null then
#self.debug("got {res2} from {mproperty}. drop {recv}")
if is_fixed(mmodule, resolved_reciever) then return res
# If the resolved type isa intern class, then there is no possible valid redefinition in any potential subclass. self is just fixed. so simply return the resolution
if res isa MClassType and res.mclass.kind == enum_kind then return res
- # TODO: Add 'fixed' virtual type in the specification.
# TODO: What if bound to a MParameterType?
# Note that Nullable types can always be redefined by the non nullable version, so there is no specific case on it.
#
# Note that parameter types are shared among class refinements.
# Therefore parameter only have an internal name (see `to_s` for details).
-# TODO: Add a `name_for` to get better messages.
class MParameterType
super MType
self.visit_expr_subtype(args[j], paramtype)
end
if vararg_rank >= 0 then
- var varargs = new Array[AExpr]
var paramtype = msignature.mparameters[vararg_rank].mtype
- for j in [vararg_rank..vararg_rank+vararg_decl] do
- varargs.add(args[j])
- self.visit_expr_subtype(args[j], paramtype)
+ var first = args[vararg_rank]
+ if vararg_decl == 0 and first isa AVarargExpr then
+ var mclass = get_mclass(node, "Array")
+ if mclass == null then return false # Forward error
+ var array_mtype = mclass.get_mtype([paramtype])
+ self.visit_expr_subtype(first.n_expr, array_mtype)
+ first.mtype = first.n_expr.mtype
+ else
+ for j in [vararg_rank..vararg_rank+vararg_decl] do
+ self.visit_expr_subtype(args[j], paramtype)
+ end
end
end
return true
end
end
+redef class AVarargExpr
+ redef fun accept_typing(v)
+ do
+ # This kind of pseudo-expression can be only processed trough a signature
+ # See `check_signature`
+ # Other cases are a syntax error.
+ v.error(self, "Syntax error: unexpected `...`")
+ end
+end
+
###
redef class ADebugTypeExpr
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import array
+
+class A
+ fun foo(a: Int...) do a.first.output
+end
+
+class B
+ super A
+ redef fun foo(a)
+ do
+ 'a'.output
+ super
+ 'b'.output
+ super(a...)
+ 'c'.output
+ super(4,5,6)
+ 'd'.output
+ super([5,6,7]...)
+ #alt3#super(a)
+ #alt4#super(1...)
+ end
+end
+
+var a = new A
+a.foo(1,2,3)
+a.foo([2,3,4]...)
+#alt1#a.foo([1,2,3])
+#alt2#a.foo(1...)
+
+var b = new B
+b.foo(3,4,5)
--- /dev/null
+1
+2
+a3
+b3
+c4
+d5
--- /dev/null
+alt/base_vararg3_alt1.nit:41,7--13: Type error: expected Int, got Array[Int]
--- /dev/null
+alt/base_vararg3_alt2.nit:42,7: Type error: expected Array[Int], got Int
--- /dev/null
+alt/base_vararg3_alt3.nit:33,9: Type error: expected Int, got Array[Int]
--- /dev/null
+alt/base_vararg3_alt4.nit:34,9: Type error: expected Array[Int], got Int