As a new addition to the parser, as requested in #1261, literal floating-point values in exponent notation.
Pull-Request: #1427
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
`}
# FIXME: Java's `char` are encoded on 16-bits whereas Nit's are on 8-bits.
fun put_char(key: JavaString, value: Char) in "Java" `{
- self.putChar(key, value);
+ self.putChar(key, (char)value);
`}
fun put_short(key: JavaString, value: Int) in "Java" `{
self.putShort(key, (short) value);
char[] java_array = new char[(int)Array_of_Char_length(value)];
for(int i=0; i < java_array.length; ++i)
- java_array[i] = Array_of_Char__index(value, i);
+ java_array[i] = (char)Array_of_Char__index(value, i);
self.putCharArray(key, java_array);
`}
return self.getByte(key, (byte) def_value);
`}
# FIXME: Java's `char` are encoded on 16-bits whereas Nit's are on 8-bits.
- fun get_char(key: JavaString): Char in "Java" `{ return self.getChar(key); `}
+ fun get_char(key: JavaString): Char in "Java" `{ return (int)self.getChar(key); `}
# FIXME: Java's `char` are encoded on 16-bits whereas Nit's are on 8-bits.
fun get_char_with_def_value(key: JavaString, def_value: Char): Char in "Java" `{
- return self.getChar(key, def_value);
+ return (int)self.getChar(key, (char)def_value);
`}
fun get_short(key: JavaString): Int in "Java" `{ return (short) self.getShort(key); `}
fun get_short_with_def_value(key: JavaString, def_value: Int): Int in "Java" `{
if (java_array == null) return nit_array;
for(int i=0; i < java_array.length; ++i)
- Array_of_Char_add(nit_array, java_array[i]);
+ Array_of_Char_add(nit_array, (int)java_array[i]);
return nit_array;
`}
`}
# FIXME: Java's `char` are encoded on 16-bits whereas Nit's are on 8-bits.
fun char_extra(name: JavaString, def_value: Char): Char in "Java" `{
- return self.getCharExtra(name, def_value);
+ return (int)self.getCharExtra(name, (char)def_value);
`}
fun char_sequence_array_extra(name: JavaString): Array[String]
import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
char[] java_array = new char[(int)Array_of_Char_length(value)];
for (int i=0; i < java_array.length; ++i)
- java_array[i] = Array_of_Char__index(value, i);
+ java_array[i] = (char)Array_of_Char__index(value, i);
return self.putExtra(name, java_array);
`}
#include <endian.h>
// Android compatibility
+ #ifndef be32toh
+ #define be32toh(val) betoh32(val)
+ #define le32toh(val) letoh32(val)
+ #endif
+
#ifndef be64toh
#define be64toh(val) betoh64(val)
- #endif
- #ifndef le64toh
#define le64toh(val) letoh64(val)
#endif
`}
super BinaryStream
# Read a single byte and return `true` if its value is different than 0
+ #
+ # Returns `false` when an error is pending (`last_error != null`).
fun read_bool: Bool do return read_byte != 0
# Get an `Array` of 8 `Bool` by reading a single byte
#
# To be used with `BinaryWriter::write_bits`.
+ #
+ # Returns an array of `false` when an error is pending (`last_error != null`).
fun read_bits: Array[Bool]
do
var int = read_byte
# Read a null terminated string
#
# To be used with `Writer::write_string`.
+ #
+ # Returns a truncated string when an error is pending (`last_error != null`).
fun read_string: String
do
var buf = new FlatBuffer
loop
var byte = read_byte
- if byte == 0x00 then return buf.to_s
+ if byte == null or byte == 0x00 then return buf.to_s
buf.chars.add byte.ascii
end
end
# Read the length as a 64 bits integer, then the content of the block
#
# To be used with `Writer::write_block`.
+ #
+ # Returns a truncated string when an error is pending (`last_error != null`).
fun read_block: String
do
var length = read_int64
#
# Using this format may result in a loss of precision as it uses less bits
# than Nit `Float`.
+ #
+ # Returns `0.0` when an error is pending (`last_error != null`).
fun read_float: Float
do
if last_error != null then return 0.0
`}
# Read a floating point on 64 bits and return it as a `Float`
+ #
+ # Returns `0.0` when an error is pending (`last_error != null`).
fun read_double: Float
do
if last_error != null then return 0.0
#
# Using this format may result in a loss of precision as the length of a
# Nit `Int` may be less than 64 bits on some platforms.
+ #
+ # Returns `0` when an error is pending (`last_error != null`).
fun read_int64: Int
do
if last_error != null then return 0
redef class NativeString
# Get `self` as a `CppString`
fun to_cpp_string(length: Int): CppString in "C++" `{
- return new std::string(self, length);
+ return new std::string(reinterpret_cast<char*>(self), length);
`}
end
# The first power of `exp` greater or equal to `self`
private fun next_pow(exp: Int): Int
do
- var p = 0
+ var p = 1
while p < self do p = p*exp
return p
end
closed = true
return
end
- var hostname = socket.gethostbyname(host)
- addrin = new NativeSocketAddrIn.with_hostent(hostname, port)
+ var hostname = sys.gethostbyname(host.to_cstring)
+ if hostname.address_is_null then
+ # Error in name lookup
+ var err = sys.h_errno
+ last_error = new IOError(err.to_s)
+
+ closed = true
+ end_reached = true
+
+ return
+ end
+
+ addrin = new NativeSocketAddrIn.with_hostent(hostname, port)
address = addrin.address
init(addrin.port, hostname.h_name)
closed = not internal_connect
end_reached = closed
+ if closed then
+ # Connection failed
+ last_error = new IOError(errno.strerror)
+ end
end
# Creates a client socket, this is meant to be used by accept only
fun descriptor: Int `{ return *self; `}
- fun gethostbyname(n: String): NativeSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `}
-
fun connect(addrIn: NativeSocketAddrIn): Int `{
return connect(*self, (struct sockaddr*)addrIn, sizeof(*addrIn));
`}
return self | other;
`}
end
+
+redef class Sys
+ # Get network host entry
+ fun gethostbyname(name: NativeString): NativeSocketHostent `{
+ return gethostbyname(name);
+ `}
+
+ # Last error raised by `gethostbyname`
+ fun h_errno: HErrno `{ return h_errno; `}
+end
+
+# Error code of `Sys::h_errno`
+extern class HErrno `{ int `}
+ # The specified host is unknown
+ fun host_not_found: Bool `{ return self == HOST_NOT_FOUND; `}
+
+ # The requested name is valid but does not have an IP address
+ #
+ # Same as `no_data`.
+ fun no_address: Bool `{ return self == NO_ADDRESS; `}
+
+ # The requested name is valid byt does not have an IP address
+ #
+ # Same as `no_address`.
+ fun no_data: Bool `{ return self == NO_DATA; `}
+
+ # A nonrecoverable name server error occurred
+ fun no_recovery: Bool `{ return self == NO_RECOVERY; `}
+
+ # A temporary error occurred on an authoritative name server, try again later
+ fun try_again: Bool `{ return self == TRY_AGAIN; `}
+
+ redef fun to_s
+ do
+ if host_not_found then
+ return "The specified host is unknown"
+ else if no_address then
+ return "The requested name is valid but does not have an IP address"
+ else if no_recovery then
+ return "A nonrecoverable name server error occurred"
+ else if try_again then
+ return "A temporary error occurred on an authoritative name server, try again later"
+ else
+ # This may happen if another call was made to `gethostbyname`
+ # before we fetch the error code.
+ return "Unknown error on `gethostbyname`"
+ end
+ end
+end
`--no-main`
: Do not generate main entry point.
-`--stacktrace`
-: Control the generation of stack traces.
+`--no-stacktrace`
+: The compiled program will not display stack traces on runtime errors.
+
+ Because stack traces rely on libunwind, this option might be useful in order to generate more portable binaries
+ since libunwind might be non available on the runtime system (or available with an ABI incompatible version).
+
+ The generated C is API-portable and can be reused, distributed and compiled on any supported system.
+ If the option `--no-stacktrace` is not used but the development files of the library `libunwind` are not available, then a warning will be displayed
+ and stack trace will be disabled.
+
+ Note that the `--no-stacktrace` option (or this absence) can be toggled manually in the generated Makefile (search `NO_STACKTRACE` in the Makefile).
+ Moreover, the environment variable `NIT_NO_STACK` (see bellow) can also be used at runtime to disable stack traces.
`--max-c-lines`
: Maximum number of lines in generated C files. Use 0 for unlimited.
* large: disable the GC and just allocate a large memory area to use for all instantiation.
* help: show the list of available options.
+`NIT_NO_STACK`
+: Runtime control of stack traces.
+
+ By default, stack traces are printed when a runtime errors occurs during the execution of a compiled program.
+ When setting this environment variable to a non empty value, such stack traces are disabled.
+
+ The environment variable is used when programs are executed, not when they are compiled.
+ Thus, you do not need to recompile programs in order to disable generated stack traces.
+
+ Note that stack traces require that, during the compilation, development files of the library `libunwind` are available.
+ If they are not available, then programs are compiled without any stack trace support.
+
+ To completely disable stack traces, see the option `--no-stacktrace`.
+
# SEE ALSO
The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
# --isset-checks-metrics
var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
- # --stacktrace
- var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
+ # --no-stacktrace
+ var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
# --no-gcc-directives
var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
# --release
self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
- self.option_context.add_option(self.opt_stacktrace)
+ self.option_context.add_option(self.opt_no_stacktrace)
self.option_context.add_option(self.opt_no_gcc_directive)
self.option_context.add_option(self.opt_release)
self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
do
super
- var st = opt_stacktrace.value
- if st == "none" or st == "libunwind" or st == "nitstack" then
- # Fine, do nothing
- else if st == "auto" or st == null then
- # Default is nitstack
- opt_stacktrace.value = "nitstack"
- else
- print "Option Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
- exit(1)
- end
-
if opt_output.value != null and opt_dir.value != null then
print "Option Error: cannot use both --dir and --output"
exit(1)
fun write_files(compile_dir: String, cfiles: Array[String])
do
var platform = compiler.target_platform
- if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
+ if platform.supports_libunwind then compiler.build_c_to_nit_bindings
var cc_opt_with_libgc = "-DWITH_LIBGC"
if not platform.supports_libgc then cc_opt_with_libgc = ""
makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n")
- var ost = toolcontext.opt_stacktrace.value
- if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n")
+ makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
+ if platform.supports_libunwind then
+ if toolcontext.opt_no_stacktrace.value then
+ makefile.write "NO_STACKTRACE=True"
+ else
+ makefile.write "NO_STACKTRACE= # Set to `True` to enable"
+ end
+ end
# Dynamic adaptations
# While `platform` enable complex toolchains, they are statically applied
# For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
+ makefile.write "\n\n"
# Check and adapt the targeted system
makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
- makefile.write("ifeq ($(uname_S),Darwin)\n")
- # remove -lunwind since it is already included on macosx
- makefile.write("\tNEED_LIBUNWIND :=\n")
- makefile.write("endif\n\n")
# Check and adapt for the compiler used
# clang need an additionnal `-Qunused-arguments`
makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n")
- makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
+ if platform.supports_libunwind then
+ makefile.write """
+ifneq ($(NO_STACKTRACE), True)
+ # Check and include lib-unwind in a portable way
+ ifneq ($(uname_S),Darwin)
+ # already included on macosx, but need to get the correct flags in other supported platforms.
+ ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0)
+ LDLIBS += `pkg-config --libs libunwind`
+ CFLAGS += `pkg-config --cflags libunwind`
+ else
+ $(warning "[_] stack-traces disabled. Please install libunwind-dev.")
+ CFLAGS += -D NO_STACKTRACE
+ endif
+ endif
+else
+ # Stacktraces disabled
+ CFLAGS += -D NO_STACKTRACE
+endif
+
+"""
+ else
+ makefile.write("CFLAGS += -D NO_STACKTRACE\n\n")
+ end
makefile.write("all: {outpath}\n")
if outpath != real_outpath then
self.header.add_decl("#include <string.h>")
self.header.add_decl("#include <sys/types.h>\n")
self.header.add_decl("#include <unistd.h>\n")
+ self.header.add_decl("#include <stdint.h>\n")
self.header.add_decl("#include \"gc_chooser.h\"")
self.header.add_decl("#ifdef ANDROID")
self.header.add_decl(" #include <android/log.h>")
do
var v = self.new_visitor
v.add_decl("#include <signal.h>")
- var ost = modelbuilder.toolcontext.opt_stacktrace.value
var platform = target_platform
- if not platform.supports_libunwind then ost = "none"
-
var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
- if ost == "nitstack" or ost == "libunwind" then
+ if platform.supports_libunwind then
+ v.add_decl("#ifndef NO_STACKTRACE")
v.add_decl("#define UNW_LOCAL_ONLY")
v.add_decl("#include <libunwind.h>")
- if ost == "nitstack" then
- v.add_decl("#include \"c_functions_hash.h\"")
- end
+ v.add_decl("#include \"c_functions_hash.h\"")
+ v.add_decl("#endif")
end
v.add_decl("int glob_argc;")
v.add_decl("char **glob_argv;")
end
v.add_decl("static void show_backtrace(void) \{")
- if ost == "nitstack" or ost == "libunwind" then
+ if platform.supports_libunwind then
+ v.add_decl("#ifndef NO_STACKTRACE")
v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
v.add_decl("unw_cursor_t cursor;")
v.add_decl("if(opt==NULL)\{")
v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
v.add_decl("while (unw_step(&cursor) > 0) \{")
v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
- if ost == "nitstack" then
v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));")
v.add_decl(" if (recv != NULL)\{")
v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);")
v.add_decl(" \}else\{")
v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);")
v.add_decl(" \}")
- else
- v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);")
- end
v.add_decl("\}")
v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
v.add_decl("free(procname);")
v.add_decl("\}")
+ v.add_decl("#endif /* NO_STACKTRACE */")
end
v.add_decl("\}")
else if mclass.name == "Bool" then
return "short int"
else if mclass.name == "Char" then
- return "char"
+ return "uint32_t"
else if mclass.name == "Float" then
return "double"
else if mclass.name == "Byte" then
return "unsigned char"
else if mclass.name == "NativeString" then
- return "char*"
+ return "unsigned char*"
else if mclass.name == "NativeArray" then
return "val*"
else
v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
return true
else if pname == "ascii" then
- v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+ v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
return true
end
else if cname == "Char" then
if pname == "output" then
- v.add("printf(\"%c\", {arguments.first});")
+ v.add("printf(\"%c\", ((unsigned char){arguments.first}));")
return true
else if pname == "object_id" then
v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
return true
else if pname == "ascii" then
- v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
+ v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
return true
end
else if cname == "Byte" then
end
else if cname == "NativeString" then
if pname == "[]" then
- v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
+ v.ret(v.new_expr("(uint32_t){arguments[0]}[{arguments[1]}]", ret.as(not null)))
return true
else if pname == "[]=" then
- v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
+ v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};")
return true
else if pname == "copy_to" then
v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
return true
else if pname == "new" then
- v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
+ v.ret(v.new_expr("(unsigned char*)nit_alloc({arguments[1]})", ret.as(not null)))
return true
end
else if cname == "NativeArray" then
v.ret(v.new_expr("glob_sys", ret.as(not null)))
return true
else if pname == "calloc_string" then
- v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
+ v.ret(v.new_expr("(unsigned char*)nit_alloc({arguments[1]})", ret.as(not null)))
return true
else if pname == "calloc_array" then
v.calloc_array(ret.as(not null), arguments)
ensure_compile_nitni_base(v)
nitni_ccu.header_c_types.add("#include \"{c_name}._ffi.h\"\n")
+ nitni_ccu.header_c_types.add("#include <stdint.h>\n")
nitni_ccu.header_c_types.add """
extern void nitni_global_ref_incr(void*);
extern void nitni_global_ref_decr(void*);
if mtype.name == "Int" then
return self.new_expr("(long)({value})>>2", mtype)
else if mtype.name == "Char" then
- return self.new_expr("(char)((long)({value})>>2)", mtype)
+ return self.new_expr("(uint32_t)((long)({value})>>2)", mtype)
else if mtype.name == "Bool" then
return self.new_expr("(short int)((long)({value})>>2)", mtype)
else
if ftype isa ForeignJavaType then return ftype.java_type.
replace('/', ".").replace('$', ".").replace(' ', "").replace('\n',"")
if mclass.name == "Bool" then return "boolean"
- if mclass.name == "Char" then return "char"
+ if mclass.name == "Char" then return "int"
if mclass.name == "Int" then return "long"
if mclass.name == "Float" then return "double"
if mclass.name == "Byte" then return "byte"
var ftype = mclass.ftype
if ftype isa ForeignJavaType then return "jobject"
if mclass.name == "Bool" then return "jboolean"
- if mclass.name == "Char" then return "jchar"
+ if mclass.name == "Char" then return "jint"
if mclass.name == "Int" then return "jlong"
if mclass.name == "Float" then return "jdouble"
if mclass.name == "Byte" then return "jbyte"
return "L{jni_type};"
end
if mclass.name == "Bool" then return "Z"
- if mclass.name == "Char" then return "C"
+ if mclass.name == "Char" then return "I"
if mclass.name == "Int" then return "J"
if mclass.name == "Float" then return "D"
if mclass.name == "Byte" then return "B"
if ftype isa ForeignJavaType then return "Object"
if mclass.name == "Bool" then return "Boolean"
- if mclass.name == "Char" then return "Char"
+ if mclass.name == "Char" then return "Int"
if mclass.name == "Int" then return "Long"
if mclass.name == "Float" then return "Double"
if mclass.name == "Byte" then return "Byte"
var h_file = "{base_name}.h"
var guard = "{mmodule.c_name.to_upper}_NIT_H"
- write_header_to_file(mmodule, "{compdir}/{h_file}", new Array[String], guard)
+ write_header_to_file(mmodule, "{compdir}/{h_file}", ["<stdint.h>"], guard)
var c_file = "{base_name}.c"
- write_body_to_file(mmodule, "{compdir}/{c_file}", ["<stdlib.h>", "<stdio.h>", "\"{h_file}\""])
+ write_body_to_file(mmodule, "{compdir}/{c_file}", ["<stdlib.h>", "<stdio.h>", "<stdint.h>", "\"{h_file}\""])
files.add( "{compdir}/{c_file}" )
end
# Regeneration of c_src from the current nitc
rm -r ../c_src
-./nitc nith.nit --stacktrace none --semi-global --compile-dir ../c_src --output ../c_src/nitg --no-cc
+./nitc nith.nit --semi-global --compile-dir ../c_src --output ../c_src/nitg --no-cc
# Remove old compilation flags
sed -i -e 's/OLDNITCOPT=.*/OLDNITCOPT=/' Makefile
do
var name = mclass.name
if name == "Bool" then return "int"
- if name == "Char" then return "char"
+ if name == "Char" then return "uint32_t"
if name == "Float" then return "double"
if name == "Int" then return "long"
if name == "Byte" then return "unsigned char"
- if name == "NativeString" then return "char*"
+ if name == "NativeString" then return "unsigned char*"
if mclass.kind == extern_kind then
var ctype = mclass.ctype
assert ctype != null
redef fun cname_blind do
var name = mclass.name
if name == "Bool" then return "int"
- if name == "Char" then return "char"
+ if name == "Char" then return "uint32_t"
if name == "Float" then return "double"
if name == "Int" then return "long"
if name == "Byte" then return "unsigned char"
- if name == "NativeString" then return "char*"
+ if name == "NativeString" then return "unsigned char*"
if mclass.kind == extern_kind then return "void*"
return "struct nitni_instance *"
end
# May have disappeared in the AST
var next_token: nullable Token = null
+ # Is `self` a token discarded from the AST?
+ #
+ # Loose tokens are not present in the AST.
+ # It means they were identified by the lexer but were discarded by the parser.
+ # It also means that they are not visited or manipulated by AST-related functions.
+ #
+ # Each loose token is attached to the non-loose token that precedes or follows it.
+ # The rules are the following:
+ #
+ # * tokens that follow a non-loose token on a same line are attached to it.
+ # See `next_looses`.
+ # * other tokens, thus that precede a non-loose token on the same line or the next one,
+ # are attached to this one. See `prev_looses`.
+ #
+ # Loose tokens are mostly end of lines (`TEol`) and comments (`TComment`).
+ # Whitespace are ignored by the lexer, so they are not even considered as loose tokens.
+ # See `blank_before` to get the whitespace that separate tokens.
+ var is_loose = false
+
+ # Loose tokens that precede `self`.
+ #
+ # These tokens start the line or belong to a line with only loose tokens.
+ var prev_looses = new Array[Token] is lazy
+
+ # Loose tokens that follow `self`
+ #
+ # These tokens are on the same line than `self`.
+ var next_looses = new Array[Token] is lazy
+
# The verbatim blank text between `prev_token` and `self`
fun blank_before: String
do
var node1 = pop
assert node1 isa AModule
var node = new Start(node1, node2)
- (new ComputeProdLocationVisitor).enter_visit(node)
+ node2.parent = node
+ (new ComputeProdLocationVisitor(lexer.file.first_token)).enter_visit(node)
return node
else if action_type == 3 then # ERROR
# skip injected tokens
# Uses existing token locations to infer location of productions.
private class ComputeProdLocationVisitor
super Visitor
+
+ # The current (or starting) cursor on the token sequence used to collect loose tokens
+ var token: nullable Token
+
# Currently visited productions that need a first token
var need_first_prods = new Array[Prod]
# Already visited epsilon productions that waits something after them
var need_after_epsilons = new Array[Prod]
- # Location of the last visited token in the current production
- var last_location: nullable Location = null
+ # The last visited token in the current production
+ var last_token: nullable Token = null
redef fun visit(n: ANode)
do
if n isa Token then
+ # Skip injected tokens
if not isset n._location then return
+
+ # Collect loose tokens (not in the AST) and attach them to token in the AST
+ var cursor = token
+ if n != cursor then
+ var lt = last_token
+ # In order, we have the tokens:
+ # * `lt` the previous visited token in the AST (if any)
+ # * then `cursor` the loose tokens to attach
+ # * then `n` the current visited token in the AST
+
+ # In the following, we advance `cursor` to add them to `lt.next_looses` or to `n.prev_looses`.
+ if lt != null then
+ var ltl = lt.location.line_end
+ # floating tokens on the same line of a AST-token follows it
+ while cursor != null and cursor != n and ltl == cursor.location.line_start do
+ cursor.is_loose = true
+ lt.next_looses.add cursor
+ cursor = cursor.next_token
+ end
+ end
+ # other loose tokens precede the next AST-token
+ while cursor != null and cursor != n do
+ cursor.is_loose = true
+ n.prev_looses.add cursor
+ cursor = cursor.next_token
+ end
+ end
+ token = n.next_token
+
var loc = n._location
- _last_location = loc
+ _last_token = n
# Add a first token to productions that need one
if not _need_first_prods.is_empty then
var startl = n._first_location
if startl != null then
# Non-epsilon production
- var endl = _last_location
- assert endl != null
+ var endl = _last_token.location
if startl == endl then
n.location = startl
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
Edge
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
Edge
`}
fun print_a(str: String) import String.to_cstring in "C++" `{
- puts(String_to_cstring(str));
+ puts(reinterpret_cast<char*>(String_to_cstring(str)));
`}
print_a "Hello from `a`."
`}
fun print_b(str: String) import String.to_cstring in "C++" `{
- puts(String_to_cstring(str));
+ puts(reinterpret_cast<char*>(String_to_cstring(str)));
`}
print_a "Hello from `a`."