It is standard in compiled programs to be able to setup some program configuration at compile-time.
Eg. C compilers accept a command-line option `-D` to define macros that will be used inside programs.
It is useful for configuring some string (like `-D PREFIX=/opt/nit/`, or `-D PORT=8081`) or activate some parts (conditional compilation, eg `-D WITH_SSL`).
This PR brings an equivalent capability to Nit engines through the new `-D` (`--define`) option.
The design behind the -D it to enable specific refinement of top-level functions at link-time.
Thus, basically
~~~sh
$ cat my_foo.nit
import foo
redef fun prefix do return "/opt/nit/"
$ nitg my_foo.nit
~~~
can be simplified into
~~~sh
$ nitg foo.nit -D prefix=/opt/nit/
~~~
Like `-m`, the `-D` creates a fictive module that refines the main module of the program.
`-D` also use the return type of the refined method to know how to interpret the text of the value.
Currently only Int, String and Bool is supported.
~~~nit
module foo
fun str: String do return "test"
fun num: Int do return 1
fun flag: Bool do return false
print str
print num
print flag
~~~
~~~sh
$ nitg foo.nit
$ ./foo
test
1
false
$ nitg foo.nit -D str=hello -D num=42 -D flag
$ ./foo
hello
42
true
~~~
The code of the PR is quite straightforward and show again that the new model is quite robust.
As usual, the first commits are some cleanup. The fun stuff is in the latter commits.
Pull-Request: #815
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
# See the License for the specific language governing permissions and
# limitations under the License.
-# Simple numerical statistical analysis and presentation
+# Defines some ANSI Terminal Control Escape Sequences.
module console
-# Redef String class to add a function to color the string
-redef class String
- private fun add_escape_char(escapechar: String): String do
- return "{escapechar}{self}{esc}[0m"
+# A ANSI/VT100 escape sequence.
+abstract class TermEscape
+ # The US-ASCII ESC character.
+ protected fun esc: Char do return 27.ascii
+end
+
+# ANSI/VT100 code to switch character attributes (SGR).
+#
+# By default, resets everything to the terminal’s defaults.
+#
+# Note:
+#
+# The escape sequence inserted at the end of the string by terminal-related
+# methods of `String` resets all character attributes to the terminal’s
+# defaults. So, when combining format `a` and `b`, something like
+# `("foo".a + " bar").b` will not work as expected, but `"foo".a.b + " bar".b`
+# will. You may also use `TermCharFormat` (this class).
+#
+# Usage example:
+#
+# print "{(new TermCharFormat).yellow_fg.bold}a{(new TermCharFormat).yellow_fg}b{new TermCharFormat}"
+class TermCharFormat
+ super TermEscape
+
+ private var attributes: Array[String] = new Array[String]
+
+ # Copies the attributes from the specified format.
+ init from(format: TermCharFormat) do
+ attributes.add_all(format.attributes)
end
- private fun esc: Char do return 27.ascii
- fun gray: String do return add_escape_char("{esc}[30m")
- fun red: String do return add_escape_char("{esc}[31m")
- fun green: String do return add_escape_char("{esc}[32m")
- fun yellow: String do return add_escape_char("{esc}[33m")
- fun blue: String do return add_escape_char("{esc}[34m")
- fun purple: String do return add_escape_char("{esc}[35m")
- fun cyan: String do return add_escape_char("{esc}[36m")
- fun light_gray: String do return add_escape_char("{esc}[37m")
- fun bold: String do return add_escape_char("{esc}[1m")
- fun underline: String do return add_escape_char("{esc}[4m")
+ redef fun to_s: String do return "{esc}[{attributes.join(";")}m"
+
+ # Apply the specified SGR and return `self`.
+ private fun apply(sgr: String): TermCharFormat do
+ attributes.add(sgr)
+ return self
+ end
+
+ # Apply normal (default) format and return `self`.
+ fun default: TermCharFormat do return apply("0")
+
+ # Apply bold weight and return `self`.
+ fun bold: TermCharFormat do return apply("1")
+
+ # Apply underlining and return `self`.
+ fun underline: TermCharFormat do return apply("4")
+
+ # Apply blinking or bold weight and return `self`.
+ fun blink: TermCharFormat do return apply("5")
+
+ # Apply reverse video and return `self`.
+ fun inverse: TermCharFormat do return apply("7")
+
+ # Apply normal weight and return `self`.
+ fun normalWeight: TermCharFormat do return apply("22")
+
+ # Add the attribute that disable inderlining and return `self`.
+ fun not_underlined: TermCharFormat do return apply("24")
+
+ # Add the attribute that disable blinking and return `self`.
+ fun steady: TermCharFormat do return apply("25")
+
+ # Add the attribute that disable reverse video and return `self`.
+ fun positive: TermCharFormat do return apply("27")
+
+ # Apply a black foreground and return `self`.
+ fun black_fg: TermCharFormat do return apply("30")
+
+ # Apply a red foreground and return `self`.
+ fun red_fg: TermCharFormat do return apply("31")
+
+ # Apply a green foreground and return `self`.
+ fun green_fg: TermCharFormat do return apply("32")
+
+ # Apply a yellow foreground and return `self`.
+ fun yellow_fg: TermCharFormat do return apply("33")
+
+ # Apply a blue foreground and return `self`.
+ fun blue_fg: TermCharFormat do return apply("34")
+
+ # Apply a mangenta foreground and return `self`.
+ fun magenta_fg: TermCharFormat do return apply("35")
+
+ # Apply a cyan foreground and return `self`.
+ fun cyan_fg: TermCharFormat do return apply("36")
+
+ # Apply a white foreground and return `self`.
+ fun white_fg: TermCharFormat do return apply("37")
+
+ # Apply the default foreground and return `self`.
+ fun default_fg: TermCharFormat do return apply("39")
+
+ # Apply a black backgroud and return `self`.
+ fun black_bg: TermCharFormat do return apply("40")
+
+ # Apply a red backgroud and return `self`.
+ fun red_bg: TermCharFormat do return apply("41")
+
+ # Apply a green backgroud and return `self`.
+ fun green_bg: TermCharFormat do return apply("42")
+
+ # Apply a yellow backgroud and return `self`.
+ fun yellow_bg: TermCharFormat do return apply("43")
+
+ # Apply a blue backgroud and return `self`.
+ fun blue_bg: TermCharFormat do return apply("44")
+
+ # Apply a mangenta backgroud and return `self`.
+ fun magenta_bg: TermCharFormat do return apply("45")
+
+ # Apply a cyan backgroud and return `self`.
+ fun cyan_bg: TermCharFormat do return apply("46")
+
+ # Apply a white backgroud and return `self`.
+ fun white_bg: TermCharFormat do return apply("47")
+
+ # Apply the default backgroud and return `self`.
+ fun default_bg: TermCharFormat do return apply("49")
end
+# Redefine the `String` class to add functions to color the string.
+redef class String
+ private fun apply_format(f: TermCharFormat): String do
+ return "{f}{self}{normal}"
+ end
+
+ private fun normal: TermCharFormat do return new TermCharFormat
+
+ # Make the text appear in dark gray (or black) in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun gray: String do return apply_format(normal.black_fg)
+
+ # Make the text appear in red in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun red: String do return apply_format(normal.red_fg)
+
+ # Make the text appear in green in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun green: String do return apply_format(normal.green_fg)
+
+ # Make the text appear in yellow in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun yellow: String do return apply_format(normal.yellow_fg)
+
+ # Make the text appear in blue in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun blue: String do return apply_format(normal.blue_fg)
+
+ # Make the text appear in mangenta in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun purple: String do return apply_format(normal.magenta_fg)
+
+ # Make the text appear in cyan in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun cyan: String do return apply_format(normal.cyan_fg)
+
+ # Make the text appear in light gray (or white) in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun light_gray: String do return apply_format(normal.white_fg)
+
+ # Make the text appear in bold in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun bold: String do return apply_format(normal.bold)
+
+ # Make the text underlined in a ANSI/VT100 terminal.
+ #
+ # WARNING: SEE: `TermCharFormat`
+ fun underline: String do return apply_format(normal.underline)
+end
# Iterate over `self`
fun iterator: Iterator[E] do return self
+
+ # Post-iteration hook.
+ #
+ # Used to inform `self` that the iteration is over.
+ # Specific iterators can use this to free some resources.
+ #
+ # Is automatically invoked at the end of `for` structures.
+ #
+ # Do nothing by default.
+ fun finish do end
end
# A collection that contains only one item.
# Set a new `item` at `key`.
#fun item=(item: E) is abstract
+
+ # Post-iteration hook.
+ #
+ # Used to inform `self` that the iteration is over.
+ # Specific iterators can use this to free some resources.
+ #
+ # Is automatically invoked at the end of `for` structures.
+ #
+ # Do nothing by default.
+ fun finish do end
end
# Iterator on a 'keys' point of view of a map
redef fun close
do
var i = _file.io_close
+ _buffer.clear
end_reached = true
end
v.compile_callsite(next_meth, [it])
v.add("\}")
v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+
+ var method_finish = self.method_finish
+ if method_finish != null then
+ # TODO: Find a way to call this also in long escape (e.g. return)
+ v.compile_callsite(method_finish, [it])
+ end
end
end
#self.debug("iter {iter}")
loop
var isok = v.callsite(method_is_ok, [iter]).as(not null)
- if not isok.is_true then return
+ if not isok.is_true then break
if self.variables.length == 1 then
var item = v.callsite(method_item, [iter]).as(not null)
#self.debug("item {item}")
abort
end
v.stmt(self.n_block)
- if v.is_break(self.escapemark) then return
+ if v.is_break(self.escapemark) then break
v.is_continue(self.escapemark) # Clear the break
- if v.is_escaping then return
+ if v.is_escaping then break
v.callsite(method_next, [iter])
end
+ var method_finish = self.method_finish
+ if method_finish != null then
+ v.callsite(method_finish, [iter])
+ end
end
end
abort
end
v.add_callsite(self.method_next)
+ var mf = self.method_finish
+ if mf != null then v.add_callsite(mf)
end
end
return callsite
end
+ fun try_get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
+ do
+ var unsafe_type = self.anchor_to(recvtype)
+ var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
+ if mproperty == null then return null
+ return get_method(node, recvtype, name, recv_is_self)
+ end
+
+
# Visit the expressions of args and check their conformity with the corresponding type in signature
# The point of this method is to handle varargs correctly
# Note: The signature must be correctly adapted
var method_item: nullable CallSite
var method_next: nullable CallSite
var method_key: nullable CallSite
+ var method_finish: nullable CallSite
private fun do_type_iterator(v: TypeVisitor, mtype: MType)
do
end
self.method_next = nextdef
+ self.method_finish = v.try_get_method(self, ittype, "finish", false)
+
if is_map then
var keydef = v.get_method(self, ittype, "key", false)
if keydef == null then
--- /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 MyC
+ var data: Collection[Object]
+ fun iterator: MyI do return new MyI(data.iterator)
+end
+
+class MyI
+ super Iterator[Object]
+ var iter: Iterator[Object]
+ redef fun is_ok do return iter.is_ok
+ redef fun item do return iter.item
+ redef fun next do iter.next
+ redef fun finish do 0.output
+end
+
+fun test(a: MyC)
+do
+ for x in a do
+ x.output
+ for y in [10,20] do
+ y.output
+ if x == 2 then return
+ end
+ 100.output
+ end
+ 200.output
+end
+
+var a = new MyC([1,2,3])
+
+for x in a do
+ x.output
+end
+
+'\n'.output
+
+for x in a do
+ x.output
+ if x == 2 then break
+ 100.output
+end
+
+'\n'.output
+
+test(a)
--- /dev/null
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
+0
--- /dev/null
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
--- /dev/null
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
--- /dev/null
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
--- /dev/null
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
# This file is part of NIT ( http://www.nitlanguage.org ).
+f is closed? false
+f is closed? true
+* ** **** *******
import for_abuse
+var escape_f: nullable IStream = null
for f in file_open("test_for_abuse.nit") do
+ escape_f = f
print f.read_line
+ print "f is closed? {f.eof}"
end
+print "f is closed? {escape_f.eof}"
var array = ["*", "****", "**", "*******"]
for q in array.sort_fa do
- q.res = q.b.length <=> q.b.length
+ # IN:
+ # q.a
+ # q-b
+ # OUT
+ # q.res
+ q.res = q.a.length <=> q.b.length
end
-
+print array.join(" ")
END
}
+# Run a command with a timeout and a time count.
+# Options:
+# -o file write the user time into file (REQUIRED). see `-o` in `man time`
+# -a append the time to the file (instead of overwriting it). see `-a` in `man time`
+saferun()
+{
+ local stop=false
+ local o=
+ local a=
+ while [ $stop = false ]; do
+ case $1 in
+ -o) o="$2"; shift; shift;;
+ -a) a="-a"; shift;;
+ *) stop=true
+ esac
+ done
+ if test -n "$TIME"; then
+ $TIME -o "$o" $a $TIMEOUT "$@"
+ else
+ $TIMEOUT "$@"
+ if test -n "$a"; then echo 0 >> "$o"; else echo 0 > "$o"; fi
+ fi
+}
+
+# Output a timestamp attribute for XML, or an empty line
+timestamp()
+{
+ if test -n "$TIMESTAMP"; then
+ echo "timestamp='`$TIMESTAMP`'"
+ else
+ echo ""
+ fi
+
+}
+
+# Get platform specific commands ##########################
+
+# Detect a working timeout
+if sh -c "timelimit echo" 1>/dev/null 2>&1; then
+ TIMEOUT="timelimit -t 600"
+elif sh -c "timeout 1 echo" 1>/dev/null 2>&1; then
+ TIMEOUT="timeout 600s"
+else
+ echo "No timelimit or timeout command detected. Tests may hang :("
+fi
+
+# Detect a working time command
+if env time --quiet -f%U true 2>/dev/null; then
+ TIME="env time --quiet -f%U"
+elif env time -f%U true 2>/dev/null; then
+ TIME="env time -f%U"
+else
+ TIME=
+fi
+
+# Detect a working date command
+if date -Iseconds >/dev/null 2>&1; then
+ TIMESTAMP="date -Iseconds"
+else
+ TIMESTAMP=
+fi
+
# $1 is the pattern of the test
# $2 is the file to compare to
# the result is:
OLD=""
LIST=""
FIRST=""
- echo >>$xml "<testcase classname='$pack' name='$description' time='`cat $outdir/$pattern.time.out`' timestamp='`date -Iseconds`'>"
+ echo >>$xml "<testcase classname='$pack' name='$description' time='`cat $outdir/$pattern.time.out`' `timestamp`>"
#for sav in "sav/$engine/fixme/$pattern.res" "sav/$engine/$pattern.res" "sav/fixme/$pattern.res" "sav/$pattern.res" "sav/$pattern.sav"; do
for savdir in $savdirs; do
sav=$savdir/fixme/$pattern.res
test "$noskip" = true && return 1
if echo "$1" | grep -f "$engine.skip" >/dev/null 2>&1; then
echo "=> $2: [skip]"
- echo >>$xml "<testcase classname='$3' name='$2' timestamp='`date -Iseconds`'><skipped/></testcase>"
+ echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
return 0
fi
if test -n "$isinterpret" && echo "$1" | grep -f "exec.skip" >/dev/null 2>&1; then
echo "=> $2: [skip exec]"
- echo >>$xml "<testcase classname='$3' name='$2' timestamp='`date -Iseconds`'><skipped/></testcase>"
+ echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
return 0
fi
return 1
# Set NIT_DIR if needed
[ -z "$NIT_DIR" ] && export NIT_DIR=..
-if sh -c "timelimit echo" 1>/dev/null 2>&1; then
- TIMEOUT="timelimit -t 600"
-elif sh -c "timeout 1 echo" 1>/dev/null 2>&1; then
- TIMEOUT="timeout 600s"
-else
- echo "No timelimit or timeout command detected. Tests may hang :("
-fi
-
# Mark to distinguish files among tests
# MARK=
echo $NITC --no-color $OPT -o "$ffout" "$i" "$includes" $nocc
fi
NIT_NO_STACK=1 JNI_LIB_PATH=$JNI_LIB_PATH JAVA_HOME=$JAVA_HOME \
- /usr/bin/time -f%U -o "$ff.time.out" $TIMEOUT $NITC --no-color $OPT -o "$ffout" "$i" $includes $nocc 2> "$ff.cmp.err" > "$ff.compile.log"
+ saferun -o "$ff.time.out" $NITC --no-color $OPT -o "$ffout" "$i" $includes $nocc 2> "$ff.cmp.err" > "$ff.compile.log"
ERR=$?
if [ "x$verbose" = "xtrue" ]; then
cat "$ff.compile.log"
chmod +x "$ff.bin"
if grep "Fatal Error: more than one primitive class" "$ff.compile.log" > /dev/null; then
echo " [skip] do no not imports kernel"
- echo >>$xml "<testcase classname='$pack' name='$bf' timestamp='`date -Iseconds`'><skipped/></testcase>"
+ echo >>$xml "<testcase classname='$pack' name='$bf' `timestamp`><skipped/></testcase>"
continue
fi
fi
echo "NIT_NO_STACK=1 $ff.bin" $args
fi
NIT_NO_STACK=1 LD_LIBRARY_PATH=$JNI_LIB_PATH \
- /usr/bin/time -f%U -a -o "$ff.time.out" $TIMEOUT "$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err"
+ saferun -a -o "$ff.time.out" "$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err"
mv $ff.time.out $ff.times.out
awk '{ SUM += $1} END { print SUM }' $ff.times.out > $ff.time.out
echo -n "==> $name "
echo "$ff.bin $args" > "$fff.bin"
chmod +x "$fff.bin"
- WRITE="$fff.write" /usr/bin/time -f%U -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $TIMEOUT $fff.bin < $ffinputs > $fff.res 2>$fff.err"
+ WRITE="$fff.write" saferun -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $fff.bin < $ffinputs > $fff.res 2>$fff.err"
if [ "x$verbose" = "xtrue" ]; then
cat "$fff.res"
cat >&2 "$fff.err"