From 865a43050d17bef796da956f080913076cf8e866 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 3 Jun 2013 01:21:21 +0200 Subject: [PATCH 01/83] fixes #385 --- compiler/ast.nim | 33 +- compiler/sigmatch.nim | 21 +- .../argument_parser/argument_parser.nim | 494 ++++++++++++++++++ 3 files changed, 544 insertions(+), 4 deletions(-) create mode 100644 tests/manyloc/argument_parser/argument_parser.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index e69b83181..6e5a17d99 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -920,7 +920,10 @@ proc discardSons(father: PNode) = father.sons = nil when defined(useNodeIds): - const nodeIdToDebug = 140600 + const nodeIdToDebug = 612777 # 612794 + #612840 # 612905 # 614635 # 614637 # 614641 + # 423408 + #429107 # 430443 # 441048 # 441090 # 441153 var gNodeId: int proc newNode(kind: TNodeKind): PNode = @@ -933,6 +936,7 @@ proc newNode(kind: TNodeKind): PNode = when defined(useNodeIds): result.id = gNodeId if result.id == nodeIdToDebug: + echo "KIND ", result.kind writeStackTrace() inc gNodeId @@ -973,6 +977,12 @@ proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode = new(result) result.kind = kind result.info = info + when defined(useNodeIds): + result.id = gNodeId + if result.id == nodeIdToDebug: + echo "KIND ", result.kind + writeStackTrace() + inc gNodeId proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = new(result) @@ -980,6 +990,12 @@ proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = result.info = info if children > 0: newSeq(result.sons, children) + when defined(useNodeIds): + result.id = gNodeId + if result.id == nodeIdToDebug: + echo "KIND ", result.kind + writeStackTrace() + inc gNodeId proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[], typ: PType = nil): PNode = @@ -989,6 +1005,12 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[], result.typ = typ # XXX use shallowCopy here for ownership transfer: result.sons = sons + when defined(useNodeIds): + result.id = gNodeId + if result.id == nodeIdToDebug: + echo "KIND ", result.kind + writeStackTrace() + inc gNodeId proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = result = newNode(kind) @@ -1178,6 +1200,9 @@ proc copyNode(src: PNode): PNode = result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags + when defined(useNodeIds): + if result.id == nodeIdToDebug: + echo "COMES FROM ", src.id case src.Kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal @@ -1193,6 +1218,9 @@ proc shallowCopy*(src: PNode): PNode = result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags + when defined(useNodeIds): + if result.id == nodeIdToDebug: + echo "COMES FROM ", src.id case src.Kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal @@ -1209,6 +1237,9 @@ proc copyTree(src: PNode): PNode = result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags + when defined(useNodeIds): + if result.id == nodeIdToDebug: + echo "COMES FROM ", src.id case src.Kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4ca3e9d43..b90e73a64 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -851,6 +851,20 @@ proc prepareNamedParam(a: PNode) = var info = a.sons[0].info a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info) +proc arrayConstr(c: PContext, n: PNode): PType = + result = newTypeS(tyArrayConstr, c) + rawAddSon(result, makeRangeType(c, 0, 0, n.info)) + addSonSkipIntLit(result, skipTypes(n.typ, {tyGenericInst, tyVar, tyOrdinal})) + +proc arrayConstr(c: PContext, info: TLineInfo): PType = + result = newTypeS(tyArrayConstr, c) + rawAddSon(result, makeRangeType(c, 0, -1, info)) + rawAddSon(result, newTypeS(tyEmpty, c)) # needs an empty basetype! + +proc incrIndexType(t: PType) = + assert t.kind == tyArrayConstr + inc t.sons[0].n.sons[1].intVal + proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var TIntSet) = template checkConstraint(n: expr) {.immediate, dirty.} = @@ -901,7 +915,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, checkConstraint(n.sons[a].sons[1]) if m.baseTypeMatch: assert(container == nil) - container = newNodeI(nkBracket, n.sons[a].info) + container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) addSon(container, arg) setSon(m.call, formal.position + 1, container) if f != formalLen - 1: container = nil @@ -927,6 +941,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n.sons[a], nOrig.sons[a]) if (arg != nil) and m.baseTypeMatch and (container != nil): addSon(container, arg) + incrIndexType(container.typ) else: m.state = csNoMatch return @@ -952,7 +967,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, return if m.baseTypeMatch: assert(container == nil) - container = newNodeI(nkBracket, n.sons[a].info) + container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) addSon(container, arg) setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) @@ -985,7 +1000,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if not ContainsOrIncl(marker, formal.position): if formal.ast == nil: if formal.typ.kind == tyVarargs: - var container = newNodeI(nkBracket, n.info) + var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info)) addSon(m.call, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) else: diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim new file mode 100644 index 000000000..95c71c08c --- /dev/null +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -0,0 +1,494 @@ +## Command line parsing module for Nimrod. +## +## `Nimrod `_ provides the `parseopt module +## `_ to parse options from the +## commandline. This module tries to provide functionality to prevent you from +## writing commandline parsing and let you concentrate on providing the best +## possible experience for your users. +## +## Source code for this module can be found at +## https://github.com/gradha/argument_parser. + +import os, strutils, tables, math, parseutils, sequtils, sets, algorithm, + unicode + +const + VERSION_STR* = "0.1.2" ## Module version as a string. + VERSION_INT* = (major: 0, minor: 1, maintenance: 2) ## \ + ## Module version as an integer tuple. + ## + ## Major versions changes mean a break in API backwards compatibility, either + ## through removal of symbols or modification of their purpose. + ## + ## Minor version changes can add procs (and maybe default parameters). Minor + ## odd versions are development/git/unstable versions. Minor even versions + ## are public stable releases. + ## + ## Maintenance version changes mean bugfixes or non API changes. + +# - Types + +type + Tparam_kind* = enum ## Different types of results for parameter parsing. + PK_EMPTY, PK_INT, PK_FLOAT, PK_STRING, PK_BOOL, + PK_BIGGEST_INT, PK_BIGGEST_FLOAT, PK_HELP + + Tparameter_callback* = + proc (parameter: string; value: var Tparsed_parameter): string ## \ + ## Prototype of parameter callbacks + ## + ## A parameter callback is just a custom proc you provide which is invoked + ## after a parameter is parsed passing the basic type validation. The + ## `parameter` parameter is the string which triggered the option. The + ## `value` parameter contains the string passed by the user already parsed + ## into the basic type you specified for it. + ## + ## The callback proc has modification access to the Tparsed_parameter + ## `value` parameter that will be put into Tcommandline_results: you can + ## read it and also modify it, maybe changing its type. In fact, if you + ## need special parsing, most likely you will end up specifying PK_STRING + ## in the parameter input specification so that the parse() proc doesn't + ## *mangle* the string before you can process it yourself. + ## + ## If the callback decides to abort the validation of the parameter, it has + ## to put into result a non zero length string with a message for the user + ## explaining why the validation failed, and maybe offer a hint as to what + ## can be done to pass validation. + + Tparameter_specification* = object ## \ + ## Holds the expectations of a parameter. + ## + ## You create these objects and feed them to the parse() proc, which then + ## uses them to detect parameters and turn them into something uself. + names*: seq[string] ## List of possible parameters to catch for this. + consumes*: Tparam_kind ## Expected type of the parameter (empty for none) + custom_validator*: Tparameter_callback ## Optional custom callback + ## to run after type conversion. + help_text*: string ## Help for this group of parameters. + + Tparsed_parameter* = object ## \ + ## Contains the parsed value from the user. + ## + ## This implements an object variant through the kind field. You can 'case' + ## this field to write a generic proc to deal with parsed parameters, but + ## nothing prevents you from accessing directly the type of field you want + ## if you expect only one kind. + case kind*: Tparam_kind + of PK_EMPTY: nil + of PK_INT: int_val*: int + of PK_BIGGEST_INT: big_int_val*: biggestInt + of PK_FLOAT: float_val*: float + of PK_BIGGEST_FLOAT: big_float_val*: biggestFloat + of PK_STRING: str_val*: string + of PK_BOOL: bool_val*: bool + of PK_HELP: nil + + Tcommandline_results* = object of TObject ## \ + ## Contains the results of the parsing. + ## + ## Usually this is the result of the parse() call, but you can inherit from + ## it to add your own fields for convenience. + ## + ## Note that you always have to access the ``options`` ordered table with + ## the first variant of a parameter name. For instance, if you have an + ## option specified like ``@["-s", "--silent"]`` and the user types + ## ``--silent`` at the commandline, you have to use + ## ``options.hasKey("-s")`` to test for it. This standarizes access through + ## the first name variant for all options to avoid you repeating the test + ## with different keys. + positional_parameters*: seq[Tparsed_parameter] + options*: TOrderedTable[string, Tparsed_parameter] + + +# - Tparam_kind procs + +proc `$`*(value: Tparam_kind): string {.procvar.} = + ## Stringifies the type, used to generate help texts. + case value: + of PK_EMPTY: result = "" + of PK_INT: result = "INT" + of PK_BIGGEST_INT: result = "BIG_INT" + of PK_FLOAT: result = "FLOAT" + of PK_BIGGEST_FLOAT: result = "BIG_FLOAG" + of PK_STRING: result = "STRING" + of PK_BOOL: result = "BOOL" + of PK_HELP: result = "" + +# - Tparameter_specification procs + +proc init*(param: var Tparameter_specification, consumes = PK_EMPTY, + custom_validator: Tparameter_callback = nil, help_text = "", + names: varargs[string]) = + ## Initialization helper with default parameters. + ## + ## You can decide to miss some if you like the defaults, reducing code. You + ## can also use new_parameter_specification() for single assignment + ## variables. + param.names = @names + param.consumes = consumes + param.custom_validator = custom_validator + param.help_text = help_text + +proc new_parameter_specification*(consumes = PK_EMPTY, + custom_validator: Tparameter_callback = nil, help_text = "", + names: varargs[string]): Tparameter_specification = + ## Initialization helper for single assignment variables. + result.init(consumes, custom_validator, help_text, names) + +# - Tparsed_parameter procs + +proc `$`*(data: Tparsed_parameter): string {.procvar.} = + ## Stringifies the value, mostly for debug purposes. + ## + ## The proc will display the value followed by non string type in brackets. + ## The non string types would be PK_INT (i), PK_BIGGEST_INT (I), PK_FLOAT + ## (f), PK_BIGGEST_FLOAT (F), PK_BOOL (b). The string type would be enclosed + ## inside quotes. PK_EMPTY produces the word `nil`, and PK_HELP produces the + ## world `help`. + case data.kind: + of PK_EMPTY: result = "nil" + of PK_INT: result = "$1(i)" % $data.int_val + of PK_BIGGEST_INT: result = "$1(I)" % $data.big_int_val + of PK_FLOAT: result = "$1(f)" % $data.float_val + of PK_BIGGEST_FLOAT: result = "$1(F)" % $data.big_float_val + of PK_STRING: result = "\"" & $data.str_val & "\"" + of PK_BOOL: result = "$1(b)" % $data.bool_val + of PK_HELP: result = "help" + + +template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter = + ## Handy compile time template to build Tparsed_parameter object variants. + ## + ## The problem with object variants is that you first have to initialise them + ## to a kind, then assign values to the correct variable, and it is a little + ## bit annoying. + ## + ## Through this template you specify as the first parameter the kind of the + ## Tparsed_parameter you want to build, and directly the value it will be + ## initialised with. The template figures out at compile time what field to + ## assign the variable to, and thus you reduce code clutter and may use this + ## to initialise single assignments variables in `let` blocks. Example: + ## + ## .. code-block:: nimrod + ## let + ## parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41) + ## parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123) + ## # The following line doesn't compile due to + ## # type mismatch: got (string) but expected 'int' + ## #parsed_param3 = new_parsed_parameter(PK_INT, "231") + var result {.gensym.}: Tparsed_parameter + result.kind = tkind + when tkind == PK_EMPTY: nil + elif tkind == PK_INT: result.int_val = expr + elif tkind == PK_BIGGEST_INT: result.big_int_val = expr + elif tkind == PK_FLOAT: result.float_val = expr + elif tkind == PK_BIGGEST_FLOAT: result.big_float_val = expr + elif tkind == PK_STRING: result.str_val = expr + elif tkind == PK_BOOL: result.bool_val = expr + elif tkind == PK_HELP: nil + else: {.error: "unknown kind".} + result + +# - Tcommandline_results procs + +proc init*(param: var Tcommandline_results; + positional_parameters: seq[Tparsed_parameter] = @[]; + options: TOrderedTable[string, Tparsed_parameter] = + initOrderedTable[string, Tparsed_parameter](4)) = + ## Initialization helper with default parameters. + param.positional_parameters = positional_parameters + param.options = options + +proc `$`*(data: Tcommandline_results): string = + ## Stringifies a Tcommandline_results structure for debug output + var dict: seq[string] = @[] + for key, value in data.options: + dict.add("$1: $2" % [escape(key), $value]) + result = "Tcommandline_result{positional_parameters:[$1], options:{$2}}" % [ + join(map(data.positional_parameters, `$`), ", "), join(dict, ", ")] + +# - Parse code + +template raise_or_quit(exception, message: expr): stmt {.immediate.} = + ## Avoids repeating if check based on the default quit_on_failure variable. + ## + ## As a special case, if message has a zero length the call to quit won't + ## generate any messages or errors (used by the mechanism to echo help to the + ## user). + if quit_on_failure: + if len(message) > 0: + quit(message) + else: + quit() + else: + raise newException(exception, message) + +template run_custom_proc(parsed_parameter: Tparsed_parameter, + custom_validator: Tparameter_callback, + parameter: TaintedString) = + ## Runs the custom validator if it is not nil. + ## + ## Pass in the string of the parameter triggering the call. If the + if not custom_validator.isNil: + except: + raise_or_quit(EInvalidValue, ("Couldn't run custom proc for " & + "parameter $1:\n$2" % [escape(parameter), + getCurrentExceptionMsg()])) + let message = custom_validator(parameter, parsed_parameter) + if not message.isNil and message.len > 0: + raise_or_quit(EInvalidValue, ("Failed to validate value for " & + "parameter $1:\n$2" % [escape(parameter), message])) + + +proc parse_parameter(quit_on_failure: bool, param, value: string, + param_kind: Tparam_kind): Tparsed_parameter = + ## Tries to parse a text according to the specified type. + ## + ## Pass the parameter string which requires a value and the text the user + ## passed in for it. It will be parsed according to the param_kind. This proc + ## will raise (EInvalidValue, EOverflow) if something can't be parsed. + result.kind = param_kind + case param_kind: + of PK_INT: + try: result.int_val = value.parseInt + except EOverflow: + raise_or_quit(EOverflow, ("parameter $1 requires an " & + "integer, but $2 is too large to fit into one") % [param, + escape(value)]) + except EInvalidValue: + raise_or_quit(EInvalidValue, ("parameter $1 requires an " & + "integer, but $2 can't be parsed into one") % [param, escape(value)]) + of PK_STRING: + result.str_val = value + of PK_FLOAT: + try: result.float_val = value.parseFloat + except EInvalidValue: + raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + "float, but $2 can't be parsed into one") % [param, escape(value)]) + of PK_BOOL: + try: result.bool_val = value.parseBool + except EInvalidValue: + raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + "boolean, but $2 can't be parsed into one. Valid values are: " & + "y, yes, true, 1, on, n, no, false, 0, off") % [param, escape(value)]) + of PK_BIGGEST_INT: + try: + let parsed_len = parseBiggestInt(value, result.big_int_val) + if value.len != parsed_len or parsed_len < 1: + raise_or_quit(EInvalidValue, ("parameter $1 requires an " & + "integer, but $2 can't be parsed completely into one") % [ + param, escape(value)]) + except EInvalidValue: + raise_or_quit(EInvalidValue, ("parameter $1 requires an " & + "integer, but $2 can't be parsed into one") % [param, escape(value)]) + of PK_BIGGEST_FLOAT: + try: + let parsed_len = parseBiggestFloat(value, result.big_float_val) + if value.len != parsed_len or parsed_len < 1: + raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + "float, but $2 can't be parsed completely into one") % [ + param, escape(value)]) + except EInvalidValue: + raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + "float, but $2 can't be parsed into one") % [param, escape(value)]) + of PK_EMPTY: + nil + of PK_HELP: + nil + + +template build_specification_lookup(): + TOrderedTable[string, ptr Tparameter_specification] = + ## Returns the table used to keep pointers to all of the specifications. + var result {.gensym.}: TOrderedTable[string, ptr Tparameter_specification] + result = initOrderedTable[string, ptr Tparameter_specification]( + nextPowerOfTwo(expected.len)) + for i in 0..expected.len-1: + for param_to_detect in expected[i].names: + if result.hasKey(param_to_detect): + raise_or_quit(EInvalidKey, + "Parameter $1 repeated in input specification" % param_to_detect) + else: + result[param_to_detect] = addr(expected[i]) + result + + +proc echo_help*(expected: seq[Tparameter_specification] = @[], + type_of_positional_parameters = PK_STRING, + bad_prefixes = @["-", "--"], end_of_options = "--") + + +proc parse*(expected: seq[Tparameter_specification] = @[], + type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = nil, + bad_prefixes = @["-", "--"], end_of_options = "--", + quit_on_failure = true): Tcommandline_results = + ## Parses parameters and returns results. + ## + ## The expected array should contain a list of the parameters you want to + ## detect, which can capture additional values. Uncaptured parameters are + ## considered positional parameters for which you can specify a type with + ## type_of_positional_parameters. + ## + ## Before accepting a positional parameter, the list of bad_prefixes is + ## compared against it. If the positional parameter starts with any of them, + ## an error is displayed to the user due to ambiguity. The user can overcome + ## the ambiguity by typing the special string specified by end_of_options. + ## Note that values captured by parameters are not checked against bad + ## prefixes, otherwise it would be a problem to specify the dash as synonim + ## for standard input for many programs. + ## + ## The args sequence should be the list of parameters passed to your program + ## without the program binary (usually OSes provide the path to the binary as + ## the zeroth parameter). If args is nil, the list will be retrieved from the + ## OS. + ## + ## If there is any kind of error and quit_on_failure is true, the quit proc + ## will be called with a user error message. If quit_on_failure is false + ## errors will raise exceptions (usually EInvalidValue or EOverflow) instead + ## for you to catch and handle. + + assert type_of_positional_parameters != PK_EMPTY and + type_of_positional_parameters != PK_HELP + for bad_prefix in bad_prefixes: + assert bad_prefix.len > 0, "Can't pass in a bad prefix of zero length" + var + expected = expected + adding_options = true + result.init() + + # Prepare the input parameter list, maybe get it from the OS if not available. + var args = args + if args == nil: + let total_params = ParamCount() + #echo "Got no explicit args, retrieving from OS. Count: ", total_params + newSeq(args, total_params) + for i in 0..total_params - 1: + #echo ($i) + args[i] = paramStr(i + 1) + + # Generate lookup table for each type of parameter based on strings. + var lookup = build_specification_lookup() + + # Loop through the input arguments detecting their type and doing stuff. + var i = 0 + while i < args.len: + let arg = args[i] + block adding_positional_parameter: + if arg.len > 0 and adding_options: + if arg == end_of_options: + # Looks like we found the end_of_options marker, disable options. + adding_options = false + break adding_positional_parameter + elif lookup.hasKey(arg): + var parsed: Tparsed_parameter + let param = lookup[arg] + + # Insert check here for help, which aborts parsing. + if param.consumes == PK_HELP: + echo_help(expected, type_of_positional_parameters, + bad_prefixes, end_of_options) + raise_or_quit(EInvalidKey, "") + + if param.consumes != PK_EMPTY: + if i + 1 < args.len: + parsed = parse_parameter(quit_on_failure, + arg, args[i + 1], param.consumes) + run_custom_proc(parsed, param.custom_validator, arg) + i += 1 + else: + raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + "value, but none was provided") % [arg]) + result.options[param.names[0]] = parsed + break adding_positional_parameter + else: + for bad_prefix in bad_prefixes: + if arg.startsWith(bad_prefix): + raise_or_quit(EInvalidValue, ("Found ambiguos parameter '$1' " & + "starting with '$2', put '$3' as the previous parameter " & + "if you want to force it as positional parameter.") % [arg, + bad_prefix, end_of_options]) + + # Unprocessed, add the parameter to the list of positional parameters. + result.positional_parameters.add(parse_parameter(quit_on_failure, + $(1 + i), arg, type_of_positional_parameters)) + + i += 1 + + +proc toString(runes: seq[TRune]): string = + result = "" + for rune in runes: result.add(rune.toUTF8) + + +proc ascii_cmp(a, b: string): int = + ## Comparison ignoring non ascii characters, for better switch sorting. + let a = filterIt(toSeq(runes(a)), it.isAlpha()) + # Can't use filterIt twice, github bug #351. + let b = filter(toSeq(runes(b)), proc(x: TRune): bool = x.isAlpha()) + return system.cmp(toString(a), toString(b)) + + +proc build_help*(expected: seq[Tparameter_specification] = @[], + type_of_positional_parameters = PK_STRING, + bad_prefixes = @["-", "--"], end_of_options = "--"): seq[string] = + ## Builds basic help text and returns it as a sequence of strings. + ## + ## Note that this proc doesn't do as much sanity checks as the normal parse() + ## proc, though it's unlikely you will be using one without the other, so if + ## you had a parameter specification problem you would find out soon. + result = @["Usage parameters: "] + + # Generate lookup table for each type of parameter based on strings. + let quit_on_failure = false + var + expected = expected + lookup = build_specification_lookup() + keys = toSeq(lookup.keys()) + + # First generate the joined version of input parameters in a list. + var + seen = initSet[string]() + prefixes: seq[string] = @[] + helps: seq[string] = @[] + for key in keys: + if seen.contains(key): + continue + + # Add the joined string to the list. + let param = lookup[key][] + var param_names = param.names + sort(param_names, ascii_cmp) + var prefix = join(param_names, ", ") + # Don't forget about the type, if the parameter consumes values + if param.consumes != PK_EMPTY and param.consumes != PK_HELP: + prefix &= " " & $param.consumes + prefixes.add(prefix) + helps.add(param.help_text) + # Ignore future elements. + for name in param.names: seen.incl(name) + + # Calculate the biggest width and try to use that + let width = prefixes.map(proc (x: string): int = 3 + len(x)).max + + for line in zip(prefixes, helps): + result.add(line.a & repeatChar(width - line.a.len) & line.b) + + +proc echo_help*(expected: seq[Tparameter_specification] = @[], + type_of_positional_parameters = PK_STRING, + bad_prefixes = @["-", "--"], end_of_options = "--") = + ## Prints out help on the terminal. + ## + ## This is just a wrapper around build_help. Note that calling this proc + ## won't exit your program, you should call quit() yourself. + for line in build_help(expected, + type_of_positional_parameters, bad_prefixes, end_of_options): + echo line + + +when isMainModule: + # Simply tests code embedded in docs. + let + parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41) + parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123) + #parsed_param3 = new_parsed_parameter(PK_INT, "231") From b3ac785f95950791ae342d8973864684c78cc09b Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 3 Jun 2013 08:05:32 +0200 Subject: [PATCH 02/83] fixes semicolon parsing issue --- compiler/parser.nim | 14 +++++++++----- doc/grammar.txt | 2 +- tests/run/tstmtexprs.nim | 5 +++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 7cfd35a41..46294925d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1748,7 +1748,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = proc parseStmt(p: var TParser): PNode = #| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED) - #| / simpleStmt + #| / simpleStmt ^+ ';' if p.tok.indent > p.currInd: result = newNodeP(nkStmtList, p) withInd(p): @@ -1779,10 +1779,14 @@ proc parseStmt(p: var TParser): PNode = parMessage(p, errComplexStmtRequiresInd) result = ast.emptyNode else: - if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) - result = simpleStmt(p) - if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) - #while p.tok.tokType == tkSemicolon: getTok(p) + result = newNodeP(nkStmtList, p) + while true: + if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) + let a = simpleStmt(p) + if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) + result.add(a) + if p.tok.tokType != tkSemicolon: break + getTok(p) proc parseAll(p: var TParser): PNode = result = newNodeP(nkStmtList, p) diff --git a/doc/grammar.txt b/doc/grammar.txt index 3aa556ce3..741e9b907 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -186,4 +186,4 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt | bindStmt | mixinStmt) / simpleStmt stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED) - / simpleStmt + / simpleStmt ^+ ';' diff --git a/tests/run/tstmtexprs.nim b/tests/run/tstmtexprs.nim index a69acd98b..497a2f6d0 100644 --- a/tests/run/tstmtexprs.nim +++ b/tests/run/tstmtexprs.nim @@ -64,3 +64,8 @@ proc p2(a: int): int = q() echo p(), " ", p2(2) + +proc semiProblem() = + if false: echo "aye"; echo "indeed" + +semiProblem() From 7d6556ffe701628b5e3428292599b0d705416c85 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 3 Jun 2013 19:33:20 +0200 Subject: [PATCH 03/83] tiny doc improvement --- todo.txt | 1 - web/question.txt | 5 ----- 2 files changed, 6 deletions(-) diff --git a/todo.txt b/todo.txt index b59f7502e..e0d7ef477 100644 --- a/todo.txt +++ b/todo.txt @@ -17,7 +17,6 @@ Bugs - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO -- instantiated generics are listed in error messages - sneaking with qualifiedLookup() is really broken! - aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' -- use a qualifier diff --git a/web/question.txt b/web/question.txt index a54d8605f..89e242e35 100644 --- a/web/question.txt +++ b/web/question.txt @@ -129,8 +129,3 @@ Abbreviation C/C++ Compiler If your C compiler is not in the above list, try using the *generic UNIX C compiler* (``ucc``). If the C compiler needs different command line arguments try the ``--passc`` and ``--passl`` switches. -Unsupported compilers contain serious bugs that keep them from bootstrapping -Nimrod. - - - From b767f34b6b57fe8dfc5777b8f06a24eb0ba35391 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Jun 2013 00:58:43 +0200 Subject: [PATCH 04/83] manyloc test should be green again --- lib/impure/re.nim | 1 - lib/pure/pegs.nim | 1 - .../dependencies/genpacket/macro_dsl.nim | 9 ---- .../keineschweine/enet_server/enet_client.nim | 2 +- tests/manyloc/keineschweine/lib/sg_assets.nim | 41 ++++++++++--------- 5 files changed, 22 insertions(+), 32 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 5041ed3ba..ef02a3b1d 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -242,7 +242,6 @@ template `=~` *(s: string, pattern: TRegEx): expr = bind maxSubPatterns when not definedInScope(matches): var matches {.inject.}: array[0..maxSubPatterns-1, string] - {.warning: "injected 'matches' might be affected by new scoping rules in 0.9.4".} match(s, pattern, matches) # ------------------------- more string handling ------------------------------ diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index dd694fe04..b6877696c 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -870,7 +870,6 @@ template `=~`*(s: string, pattern: TPeg): bool = bind maxSubpatterns when not definedInScope(matches): var matches {.inject.}: array[0..maxSubpatterns-1, string] - {.warning: "injected 'matches' might be affected by new scoping rules in 0.9.4".} match(s, pattern, matches) # ------------------------- more string handling ------------------------------ diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim index cbae1334e..c7b577b3d 100644 --- a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim +++ b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim @@ -33,18 +33,9 @@ proc emptyNode*(): PNimrodNode {.compileTime.} = proc dot*(left, right: PNimrodNode): PNimrodNode {.compileTime.} = result = newNimNode(nnkDotExpr).und(left, right) -proc postfix*(a: PNimrodNode, b: string): PNimrodNode {.compileTime.} = - result = newNimNode(nnkPostfix).und(newIdentNode(!b), a) proc prefix*(a: string, b: PNimrodNode): PNimrodNode {.compileTime.} = result = newNimNode(nnkPrefix).und(newIdentNode(!a), b) -proc infix*(a, b, c: PNimrodNode): PNimrodNode {.compileTime.} = - ## 5.infix("+", 10) ## => 5 + 10 - result = newNimNode(nnkInfix).und(b, a, c) -proc infix*(a: PNimrodNode; b: string; c: PNimrodNode): PNimrodNode {.compileTime.} = - ## Infix operation: infix(5, "+", 10) ## => - result = newNimNode(nnkInfix).und(newIdentNode(b), a, c) - proc quoted2ident*(a: PNimrodNode): PNimrodNode {.compileTime.} = if a.kind != nnkAccQuoted: return a diff --git a/tests/manyloc/keineschweine/enet_server/enet_client.nim b/tests/manyloc/keineschweine/enet_server/enet_client.nim index f4c8732e3..0c903a733 100644 --- a/tests/manyloc/keineschweine/enet_server/enet_client.nim +++ b/tests/manyloc/keineschweine/enet_server/enet_client.nim @@ -145,7 +145,7 @@ proc lobbyInit*() = var pos = vec2f(10, 10) u_alias = gui.newTextEntry( - if s.existsKey("alias"): s["alias"].str else: "alias", + if s.hasKey("alias"): s["alias"].str else: "alias", pos) pos.y += 20 u_passwd = gui.newTextEntry("buzz", pos) diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim index 766bf5d82..ccd1d9280 100644 --- a/tests/manyloc/keineschweine/lib/sg_assets.nim +++ b/tests/manyloc/keineschweine/lib/sg_assets.nim @@ -227,12 +227,13 @@ template checkFile(path: expr): stmt {.dirty, immediate.} = cacheImpl newSprite, SpriteSheets, PSpriteSheet: result.file = filename - if not(filename =~ re"\S+_(\d+)x(\d+)\.\S\S\S"): + if filename =~ re"\S+_(\d+)x(\d+)\.\S\S\S": + result.framew = strutils.parseInt(matches[0]) + result.frameh = strutils.parseInt(matches[1]) + checkFile("data/gfx"/result.file) + else: errors.add "Bad file: "&filename&" must be in format name_WxH.png" return - result.framew = strutils.parseInt(matches[0]) - result.frameh = strutils.parseInt(matches[1]) - checkFile("data/gfx"/result.file) cacheImpl newSound, SoundCache, PSoundRecord: result.file = filename @@ -288,23 +289,23 @@ proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool = if settings.kind != JObject: addError("Settings root must be an object") return - if not settings.existsKey("vehicles"): + if not settings.hasKey("vehicles"): addError("Vehicles section missing") - if not settings.existsKey("objects"): + if not settings.hasKey("objects"): errors.add("Objects section is missing") result = false - if not settings.existsKey("level"): + if not settings.hasKey("level"): errors.add("Level settings section is missing") result = false else: let lvl = settings["level"] - if lvl.kind != JObject or not lvl.existsKey("size"): + if lvl.kind != JObject or not lvl.hasKey("size"): errors.add("Invalid level settings") result = false - elif not lvl.existsKey("size") or lvl["size"].kind != JArray or lvl["size"].len != 2: + elif not lvl.hasKey("size") or lvl["size"].kind != JArray or lvl["size"].len != 2: errors.add("Invalid/missing level size") result = false - if not settings.existsKey("items"): + if not settings.hasKey("items"): errors.add("Items section missing") result = false else: @@ -363,7 +364,7 @@ proc loadSettings*(rawJson: string, errors: var seq[string]): bool = nameToVehID[veh.name] = veh.id inc vID vID = 0 - if settings.existsKey("bullets"): + if settings.hasKey("bullets"): for blt in settings["bullets"].items: var bullet = importBullet(blt, errors) bullet.id = bID @@ -408,21 +409,21 @@ proc fetchBullet(name: string): PBulletRecord = return cfg.bullets[nameToBulletID[name]] proc getField(node: PJsonNode, field: string, target: var float) = - if not node.existsKey(field): + if not node.hasKey(field): return if node[field].kind == JFloat: target = node[field].fnum elif node[field].kind == JInt: target = node[field].num.float proc getField(node: PJsonNode, field: string, target: var int) = - if not node.existsKey(field): + if not node.hasKey(field): return if node[field].kind == JInt: target = node[field].num.int elif node[field].kind == JFloat: target = node[field].fnum.int proc getField(node: PJsonNode; field: string; target: var bool) = - if not node.existsKey(field): + if not node.hasKey(field): return case node[field].kind of JBool: @@ -434,7 +435,7 @@ proc getField(node: PJsonNode; field: string; target: var bool) = else: nil template checkKey(node: expr; key: string): stmt = - if not existsKey(node, key): + if not hasKey(node, key): return proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord = @@ -450,17 +451,17 @@ proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings = checkKey(data, "level") var level = data["level"] - if level.existsKey("size") and level["size"].kind == JArray and level["size"].len == 2: + if level.hasKey("size") and level["size"].kind == JArray and level["size"].len == 2: result.size.x = level["size"][0].num.cint result.size.y = level["size"][1].num.cint - if level.existsKey("starfield"): + if level.hasKey("starfield"): for star in level["starfield"].items: result.starfield.add(newSprite(star.str, errors)) proc importPhys(data: PJsonNode): TPhysicsRecord = result.radius = 20.0 result.mass = 10.0 - if data.existsKey("physics") and data["physics"].kind == JObject: + if data.hasKey("physics") and data["physics"].kind == JObject: let phys = data["physics"] phys.getField("radius", result.radius) phys.getField("mass", result.mass) @@ -489,10 +490,10 @@ proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord = result.delay = 1000.0 result.spriteSheet = nil - if data.existsKey("anim"): + if data.hasKey("anim"): let anim = data["anim"] if anim.kind == JObject: - if anim.existsKey("file"): + if anim.hasKey("file"): result.spriteSheet = newSprite(anim["file"].str, errors) anim.getField "angle", result.angle From b487ebaaba9711fe27d4cb213c0ad16001464f88 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Jun 2013 01:11:55 +0200 Subject: [PATCH 05/83] new scoping rules for 'if' now active --- compiler/options.nim | 2 +- doc/manual.txt | 2 -- todo.txt | 2 +- web/news.txt | 4 ++++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 6d93048fe..5f173d240 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -14,7 +14,7 @@ const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true hasFFI* = defined(useFFI) - newScopeForIf* = false # XXX activate for 0.9.4 + newScopeForIf* = true type # please make sure we have under 32 options # (improves code efficiency a lot!) diff --git a/doc/manual.txt b/doc/manual.txt index 08e5f723b..f163e0d5f 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1920,8 +1920,6 @@ the corresponding *then* block: In the example the scopes have been enclosed in ``{| |}``. -**Note**: These scoping rules will be active in 0.9.4. - Case statement -------------- diff --git a/todo.txt b/todo.txt index e0d7ef477..700d685b1 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,7 @@ version 0.9.4 ============= - make 'bind' default for templates and introduce 'mixin'; - special rule for ``[]=`` +- special rule for ``[]=`` - ``=`` should be overloadable; requires specialization for ``=``; general lift mechanism in the compiler is already implemented for 'fields' - mocking support with ``tyProxy`` that does: fallback for ``.`` operator diff --git a/web/news.txt b/web/news.txt index 5d79d8271..f133968c2 100644 --- a/web/news.txt +++ b/web/news.txt @@ -18,6 +18,10 @@ Library Additions Changes affecting backwards compatibility ----------------------------------------- +- The scoping rules for the ``if`` statement changed for better interaction + with the new syntactic construct ``(;)``. + + Compiler Additions ------------------ From 2f5d5e3ba73f8fcb11341b88bb6f9d855fe42fed Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Jun 2013 07:48:46 +0200 Subject: [PATCH 06/83] fixes #451 --- compiler/semstmts.nim | 4 +++- tests/reject/tneedsdiscard.nim | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/reject/tneedsdiscard.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b63c20548..6f372472a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -146,7 +146,9 @@ proc discardCheck(result: PNode) = if result.typ.kind == tyNil: fixNilType(result) else: - localError(result.info, errDiscardValue) + var n = result + while n.kind in skipForDiscardable: n = n.lastSon + localError(n.info, errDiscardValue) proc semIf(c: PContext, n: PNode): PNode = result = n diff --git a/tests/reject/tneedsdiscard.nim b/tests/reject/tneedsdiscard.nim new file mode 100644 index 000000000..24f5b2eee --- /dev/null +++ b/tests/reject/tneedsdiscard.nim @@ -0,0 +1,12 @@ +discard """ + line: 10 + errormsg: "value returned by statement has to be discarded" +""" + +proc p = + var f: TFile + echo "hi" + + open(f, "arg.txt") + +p() From f7c0cc976ddb4e86e2341352b0674b9787005a4a Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Jun 2013 08:02:53 +0200 Subject: [PATCH 07/83] fixes #456 --- lib/pure/parsecfg.nim | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 6a0f2ed49..e8014cece 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -67,7 +67,8 @@ type # implementation const - SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.'} + SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', + '/', '\\'} proc rawGetTok(c: var TCfgParser, tok: var TToken) @@ -149,7 +150,7 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) = of 't', 'T': add(tok.literal, '\t') Inc(c.bufpos) - of '\'', '\"': + of '\'', '"': add(tok.literal, c.buf[c.bufpos]) Inc(c.bufpos) of '\\': @@ -178,7 +179,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = var pos = c.bufPos + 1 # skip " var buf = c.buf # put `buf` in a register tok.kind = tkSymbol - if (buf[pos] == '\"') and (buf[pos + 1] == '\"'): + if (buf[pos] == '"') and (buf[pos + 1] == '"'): # long string literal: inc(pos, 2) # skip "" # skip leading newline: @@ -186,9 +187,9 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = buf = c.buf while true: case buf[pos] - of '\"': - if (buf[pos + 1] == '\"') and (buf[pos + 2] == '\"'): break - add(tok.literal, '\"') + of '"': + if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break + add(tok.literal, '"') Inc(pos) of '\c', '\L': pos = HandleCRLF(c, pos) @@ -205,7 +206,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = # ordinary string literal while true: var ch = buf[pos] - if ch == '\"': + if ch == '"': inc(pos) # skip '"' break if ch in {'\c', '\L', lexbase.EndOfFile}: @@ -278,7 +279,7 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) = tok.kind = tkBracketRi Inc(c.bufpos) tok.literal = "]" - of '\"': + of '"': getString(c, tok, false) of lexbase.EndOfFile: tok.kind = tkEof @@ -351,7 +352,7 @@ proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} = rawGetTok(c, c.tok) else: result.kind = cfgError - result.msg = errorStr(c, "\']\' expected, but found: " & c.tok.literal) + result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal) of tkInvalid, tkEquals, tkColon, tkBracketRi: result.kind = cfgError result.msg = errorStr(c, "invalid token: " & c.tok.literal) From 2aaa8f7909e51eb3d971e197f152e247c64362e9 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Jun 2013 21:58:39 +0200 Subject: [PATCH 08/83] implemented dataflow analysis; activate via --warning[Uninit]:on --- compiler/msgs.nim | 11 ++- compiler/sempass2.nim | 188 +++++++++++++++++++++++++++++++++---- compiler/semstmts.nim | 2 +- tests/compile/toverprc.nim | 4 + todo.txt | 2 +- web/news.txt | 4 + 6 files changed, 185 insertions(+), 26 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 16ef06b61..f75aec0d5 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -106,7 +106,7 @@ type warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure, - warnEachIdentIsTuple, warnShadowIdent, warnUser, + warnEachIdentIsTuple, warnShadowIdent, warnUninit, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, @@ -355,6 +355,7 @@ const warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]", warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]", warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]", + warnUninit: "read from potentially uninitialized variable: '$1' [Uninit]", warnUser: "$1 [User]", hintSuccess: "operation successful [Success]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", @@ -374,14 +375,15 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..19, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..20, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", - "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "User"] + "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "Uninit", + "User"] HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", @@ -512,7 +514,8 @@ proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc sourceLine*(i: TLineInfo): PRope var - gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent} + gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - + {warnShadowIdent, warnUninit} gErrorCounter*: int = 0 # counts the number of errors gHintCounter*: int = 0 gWarnCounter*: int = 0 diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b78e36531..64aae09c1 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -15,6 +15,7 @@ import # way had some inherent problems. Performs: # # * effect+exception tracking +# * "usage before definition" checking # * checks for invalid usages of compiletime magics (not implemented) # * checks for invalid usages of PNimNode (not implemented) # * later: will do an escape analysis for closures at least @@ -42,14 +43,6 @@ import # an array and filling it in parallel should be supported but is not easily # done: It essentially requires a built-in 'indexSplit' operation and dependent # typing. - -when false: - proc sem2call(c: PContext, n: PNode): PNode = - assert n.kind in nkCallKinds - - proc sem2sym(c: PContext, n: PNode): PNode = - assert n.kind == nkSym - # ------------------------ exception and tag tracking ------------------------- @@ -80,9 +73,44 @@ type tags: PNode # list of tags bottom: int owner: PSym + init: seq[int] # list of initialized variables + # coming soon: "guard" tracking for 'let' variables PEffects = var TEffects +proc isLocalVar(a: PEffects, s: PSym): bool = + s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner + +proc initVar(a: PEffects, n: PNode) = + if n.kind != nkSym: return + let s = n.sym + if isLocalVar(a, s): + for x in a.init: + if x == s.id: return + a.init.add s.id + +proc useVar(a: PEffects, n: PNode) = + let s = n.sym + if isLocalVar(a, s): + if s.id notin a.init: + if true: + Message(n.info, warnUninit, s.name.s) + else: + Message(n.info, errGenerated, + "read from potentially uninitialized variable: '$1'" % s.name.s) + # prevent superfluous warnings about the same variable: + a.init.add s.id + +type + TIntersection = seq[tuple[id, count: int]] # a simple count table + +proc addToIntersection(inter: var TIntersection, s: int) = + for j in 0.. 'y' not defined after block! + if oldState < 0: oldState = tracked.init.len + track(tracked, n.sons[i]) + if oldState > 0: setLen(tracked.init, oldState) + else: + track(tracked, n) + +proc isTrue(n: PNode): bool = + n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or + n.kind == nkIntLit and n.intVal != 0 + proc track(tracked: PEffects, n: PNode) = case n.kind + of nkSym: + useVar(tracked, n) of nkRaiseStmt: n.sons[0].info = n.info throws(tracked.exc, n.sons[0]) + for i in 0 .. Date: Tue, 4 Jun 2013 22:28:20 -0600 Subject: [PATCH 09/83] made macros.dumptree and dumplisp immediate, deprecated the *_imm versions --- lib/core/macros.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index bbf970102..720b15189 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -390,7 +390,7 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} = add(result, ")") -macro dumpTree*(s: stmt): stmt = echo s.treeRepr +macro dumpTree*(s: stmt): stmt {.immediate.} = echo s.treeRepr ## Accepts a block of nimrod code and prints the parsed abstract syntax ## tree using the `toTree` function. Printing is done *at compile time*. ## @@ -398,16 +398,16 @@ macro dumpTree*(s: stmt): stmt = echo s.treeRepr ## tree and to discover what kind of nodes must be created to represent ## a certain expression/statement. -macro dumpLisp*(s: stmt): stmt = echo s.lispRepr +macro dumpLisp*(s: stmt): stmt {.immediate.} = echo s.lispRepr ## Accepts a block of nimrod code and prints the parsed abstract syntax ## tree using the `toLisp` function. Printing is done *at compile time*. ## ## See `dumpTree`. -macro dumpTreeImm*(s: stmt): stmt {.immediate.} = echo s.treeRepr +macro dumpTreeImm*(s: stmt): stmt {.immediate, deprecated.} = echo s.treeRepr ## The ``immediate`` version of `dumpTree`. -macro dumpLispImm*(s: stmt): stmt {.immediate.} = echo s.lispRepr +macro dumpLispImm*(s: stmt): stmt {.immediate, deprecated.} = echo s.lispRepr ## The ``immediate`` version of `dumpLisp`. From 7ea77d93e370040b4971c9f209e4b465c49721a5 Mon Sep 17 00:00:00 2001 From: Billingsly Wetherfordshire Date: Tue, 4 Jun 2013 22:33:55 -0600 Subject: [PATCH 10/83] Update news.txt --- web/news.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/news.txt b/web/news.txt index 4f932bc05..d844b5012 100644 --- a/web/news.txt +++ b/web/news.txt @@ -36,7 +36,7 @@ Language Additions - Arrays can now be declared with a single integer literal ``N`` instead of a range; the range is then ``0..N-1``. - +- macros.dumptree and macros.dumplisp have been made immediate, dumptree_imm and dumplisp_imm are now deprecated. 2013-05-20 New website design! From 516e0cb71aa0e2515d583ae1fa36469a9e319fb2 Mon Sep 17 00:00:00 2001 From: Billingsly Wetherfordshire Date: Thu, 6 Jun 2013 03:49:31 -0600 Subject: [PATCH 11/83] use proper types in lua * ints changed to cint * the source has been cleaned up with {.push/pop.} * lua 5.2 has been removed (it is incompatible) --- lib/wrappers/lua/lauxlib.nim | 185 ++++++++++++++++------------------- 1 file changed, 84 insertions(+), 101 deletions(-) diff --git a/lib/wrappers/lua/lauxlib.nim b/lib/wrappers/lua/lauxlib.nim index bcb806f74..b985858d3 100644 --- a/lib/wrappers/lua/lauxlib.nim +++ b/lib/wrappers/lua/lauxlib.nim @@ -26,9 +26,9 @@ import proc pushstring*(L: PState, s: string) # compatibilty macros -proc getn*(L: PState, n: int): int +proc getn*(L: PState, n: cint): cint # calls lua_objlen -proc setn*(L: PState, t, n: int) +proc setn*(L: PState, t, n: cint) # does nothing! type Treg*{.final.} = object @@ -37,58 +37,45 @@ type Preg* = ptr Treg -proc openlib*(L: PState, libname: cstring, lr: Preg, nup: int){.cdecl, - dynlib: lua.LIB_NAME, importc: "luaL_openlib".} -proc register*(L: PState, libname: cstring, lr: Preg){.cdecl, - dynlib: lua.LIB_NAME, importc: "luaL_register".} -proc getmetafield*(L: PState, obj: int, e: cstring): int{.cdecl, - dynlib: lua.LIB_NAME, importc: "luaL_getmetafield".} -proc callmeta*(L: PState, obj: int, e: cstring): int{.cdecl, - dynlib: LIB_NAME, importc: "luaL_callmeta".} -proc typerror*(L: PState, narg: int, tname: cstring): int{.cdecl, - dynlib: LIB_NAME, importc: "luaL_typerror".} -proc argerror*(L: PState, numarg: int, extramsg: cstring): int{.cdecl, - dynlib: LIB_NAME, importc: "luaL_argerror".} -proc checklstring*(L: PState, numArg: int, len: ptr int): cstring{.cdecl, - dynlib: LIB_NAME, importc: "luaL_checklstring".} -proc optlstring*(L: PState, numArg: int, def: cstring, len: ptr int): cstring{. - cdecl, dynlib: LIB_NAME, importc: "luaL_optlstring".} -proc checknumber*(L: PState, numArg: int): Number{.cdecl, - dynlib: LIB_NAME, importc: "luaL_checknumber".} -proc optnumber*(L: PState, nArg: int, def: Number): Number{.cdecl, - dynlib: LIB_NAME, importc: "luaL_optnumber".} -proc checkinteger*(L: PState, numArg: int): Integer{.cdecl, - dynlib: LIB_NAME, importc: "luaL_checkinteger".} -proc optinteger*(L: PState, nArg: int, def: Integer): Integer{. - cdecl, dynlib: LIB_NAME, importc: "luaL_optinteger".} -proc checkstack*(L: PState, sz: int, msg: cstring){.cdecl, - dynlib: LIB_NAME, importc: "luaL_checkstack".} -proc checktype*(L: PState, narg, t: int){.cdecl, dynlib: LIB_NAME, - importc: "luaL_checktype".} -proc checkany*(L: PState, narg: int){.cdecl, dynlib: LIB_NAME, - importc: "luaL_checkany".} -proc newmetatable*(L: PState, tname: cstring): int{.cdecl, - dynlib: LIB_NAME, importc: "luaL_newmetatable".} -proc checkudata*(L: PState, ud: int, tname: cstring): Pointer{.cdecl, - dynlib: LIB_NAME, importc: "luaL_checkudata".} -proc where*(L: PState, lvl: int){.cdecl, dynlib: LIB_NAME, - importc: "luaL_where".} -proc error*(L: PState, fmt: cstring): int{.cdecl, varargs, - dynlib: LIB_NAME, importc: "luaL_error".} -proc checkoption*(L: PState, narg: int, def: cstring, lst: cstringArray): int{. - cdecl, dynlib: LIB_NAME, importc: "luaL_checkoption".} -proc reference*(L: PState, t: int): int{.cdecl, dynlib: LIB_NAME, - importc: "luaL_ref".} -proc unref*(L: PState, t, theref: int){.cdecl, dynlib: LIB_NAME, - importc: "luaL_unref".} -proc loadfile*(L: PState, filename: cstring): int{.cdecl, - dynlib: LIB_NAME, importc: "luaL_loadfile".} -proc loadbuffer*(L: PState, buff: cstring, size: int, name: cstring): int{. - cdecl, dynlib: LIB_NAME, importc: "luaL_loadbuffer".} -proc loadstring*(L: PState, s: cstring): int{.cdecl, dynlib: LIB_NAME, - importc: "luaL_loadstring".} -proc newstate*(): PState{.cdecl, dynlib: LIB_NAME, - importc: "luaL_newstate".} + +{.push callConv: cdecl, dynlib: lua.LIB_NAME.} +{.push importc: "luaL_$1".} + +proc openlib*(L: PState, libname: cstring, lr: Preg, nup: cint) +proc register*(L: PState, libname: cstring, lr: Preg) + +proc getmetafield*(L: PState, obj: cint, e: cstring): cint +proc callmeta*(L: PState, obj: cint, e: cstring): cint +proc typerror*(L: PState, narg: cint, tname: cstring): cint +proc argerror*(L: PState, numarg: cint, extramsg: cstring): cint +proc checklstring*(L: PState, numArg: cint, len: ptr int): cstring +proc optlstring*(L: PState, numArg: cint, def: cstring, len: ptr cint): cstring +proc checknumber*(L: PState, numArg: cint): Number +proc optnumber*(L: PState, nArg: cint, def: Number): Number +proc checkinteger*(L: PState, numArg: cint): Integer +proc optinteger*(L: PState, nArg: cint, def: Integer): Integer +proc checkstack*(L: PState, sz: cint, msg: cstring) +proc checktype*(L: PState, narg, t: cint) + +proc checkany*(L: PState, narg: cint) +proc newmetatable*(L: PState, tname: cstring): cint + +proc checkudata*(L: PState, ud: cint, tname: cstring): Pointer +proc where*(L: PState, lvl: cint) +proc error*(L: PState, fmt: cstring): cint{.varargs.} +proc checkoption*(L: PState, narg: cint, def: cstring, lst: cstringArray): cint + +proc unref*(L: PState, t, theref: cint) +proc loadfile*(L: PState, filename: cstring): cint +proc loadbuffer*(L: PState, buff: cstring, size: cint, name: cstring): cint +proc loadstring*(L: PState, s: cstring): cint +proc newstate*(): PState + +{.pop.} +proc reference*(L: PState, t: cint): cint{.importc: "luaL_ref".} + +{.pop.} + proc open*(): PState # compatibility; moved from unit lua to lauxlib because it needs luaL_newstate # @@ -96,18 +83,18 @@ proc open*(): PState #** some useful macros #** =============================================================== # -proc argcheck*(L: PState, cond: bool, numarg: int, extramsg: cstring) -proc checkstring*(L: PState, n: int): cstring -proc optstring*(L: PState, n: int, d: cstring): cstring -proc checkint*(L: PState, n: int): int -proc checklong*(L: PState, n: int): int32 -proc optint*(L: PState, n: int, d: float64): int -proc optlong*(L: PState, n: int, d: float64): int32 -proc dofile*(L: PState, filename: cstring): int -proc dostring*(L: PState, str: cstring): int +proc argcheck*(L: PState, cond: bool, numarg: cint, extramsg: cstring) +proc checkstring*(L: PState, n: cint): cstring +proc optstring*(L: PState, n: cint, d: cstring): cstring +proc checkint*(L: PState, n: cint): cint +proc checklong*(L: PState, n: cint): clong +proc optint*(L: PState, n: cint, d: float64): cint +proc optlong*(L: PState, n: cint, d: float64): clong +proc dofile*(L: PState, filename: cstring): cint +proc dostring*(L: PState, str: cstring): cint proc getmetatable*(L: PState, tname: cstring) # not translated: - # #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + # #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) # #** ======================================================= #** Generic Buffer manipulation @@ -119,7 +106,7 @@ const # note: this is just arbitrary, as it related to t type Buffer*{.final.} = object p*: cstring # current position in buffer - lvl*: int # number of strings in the stack (level) + lvl*: cint # number of strings in the stack (level) L*: PState buffer*: array[0..BUFFERSIZE - 1, Char] # warning: see note above about LUAL_BUFFERSIZE @@ -130,80 +117,76 @@ proc addchar*(B: PBuffer, c: Char) # compatibility only (alias for luaL_addchar) proc putchar*(B: PBuffer, c: Char) # warning: see note above about LUAL_BUFFERSIZE -proc addsize*(B: PBuffer, n: int) -proc buffinit*(L: PState, B: PBuffer){.cdecl, dynlib: LIB_NAME, - importc: "luaL_buffinit".} -proc prepbuffer*(B: PBuffer): cstring{.cdecl, dynlib: LIB_NAME, - importc: "luaL_prepbuffer".} -proc addlstring*(B: PBuffer, s: cstring, L: int){.cdecl, - dynlib: LIB_NAME, importc: "luaL_addlstring".} -proc addstring*(B: PBuffer, s: cstring){.cdecl, dynlib: LIB_NAME, - importc: "luaL_addstring".} -proc addvalue*(B: PBuffer){.cdecl, dynlib: LIB_NAME, - importc: "luaL_addvalue".} -proc pushresult*(B: PBuffer){.cdecl, dynlib: LIB_NAME, - importc: "luaL_pushresult".} -proc gsub*(L: PState, s, p, r: cstring): cstring{.cdecl, - dynlib: LIB_NAME, importc: "luaL_gsub".} -proc findtable*(L: PState, idx: int, fname: cstring, szhint: int): cstring{. - cdecl, dynlib: LIB_NAME, importc: "luaL_findtable".} +proc addsize*(B: PBuffer, n: cint) + +{.push callConv: cdecl, dynlib: lua.LIB_NAME, importc: "luaL_$1".} +proc buffinit*(L: PState, B: PBuffer) +proc prepbuffer*(B: PBuffer): cstring +proc addlstring*(B: PBuffer, s: cstring, L: cint) +proc addstring*(B: PBuffer, s: cstring) +proc addvalue*(B: PBuffer) +proc pushresult*(B: PBuffer) +proc gsub*(L: PState, s, p, r: cstring): cstring +proc findtable*(L: PState, idx: cint, fname: cstring, szhint: cint): cstring # compatibility with ref system # pre-defined references +{.pop.} + const NOREF* = - 2 REFNIL* = - 1 -proc unref*(L: PState, theref: int) -proc getref*(L: PState, theref: int) +proc unref*(L: PState, theref: cint) +proc getref*(L: PState, theref: cint) # #** Compatibility macros and functions # # implementation proc pushstring(L: PState, s: string) = - pushlstring(L, cstring(s), len(s)) + pushlstring(L, cstring(s), s.len.cint) -proc getn(L: PState, n: int): int = +proc getn(L: PState, n: cint): cint = Result = objlen(L, n) -proc setn(L: PState, t, n: int) = +proc setn(L: PState, t, n: cint) = # does nothing as this operation is deprecated nil proc open(): PState = Result = newstate() -proc dofile(L: PState, filename: cstring): int = +proc dofile(L: PState, filename: cstring): cint = Result = loadfile(L, filename) if Result == 0: Result = pcall(L, 0, MULTRET, 0) -proc dostring(L: PState, str: cstring): int = +proc dostring(L: PState, str: cstring): cint = Result = loadstring(L, str) if Result == 0: Result = pcall(L, 0, MULTRET, 0) proc getmetatable(L: PState, tname: cstring) = getfield(L, REGISTRYINDEX, tname) -proc argcheck(L: PState, cond: bool, numarg: int, extramsg: cstring) = +proc argcheck(L: PState, cond: bool, numarg: cint, extramsg: cstring) = if not cond: discard argerror(L, numarg, extramsg) -proc checkstring(L: PState, n: int): cstring = +proc checkstring(L: PState, n: cint): cstring = Result = checklstring(L, n, nil) -proc optstring(L: PState, n: int, d: cstring): cstring = +proc optstring(L: PState, n: cint, d: cstring): cstring = Result = optlstring(L, n, d, nil) -proc checkint(L: PState, n: int): int = - Result = toInt(checknumber(L, n)) +proc checkint(L: PState, n: cint): cint = + Result = cint(checknumber(L, n)) -proc checklong(L: PState, n: int): int32 = +proc checklong(L: PState, n: cint): clong = Result = int32(ToInt(checknumber(L, n))) -proc optint(L: PState, n: int, d: float64): int = - Result = int(ToInt(optnumber(L, n, d))) +proc optint(L: PState, n: cint, d: float64): cint = + Result = optnumber(L, n, d).cint -proc optlong(L: PState, n: int, d: float64): int32 = +proc optlong(L: PState, n: cint, d: float64): clong = Result = int32(ToInt(optnumber(L, n, d))) proc addchar(B: PBuffer, c: Char) = @@ -215,11 +198,11 @@ proc addchar(B: PBuffer, c: Char) = proc putchar(B: PBuffer, c: Char) = addchar(B, c) -proc addsize(B: PBuffer, n: int) = +proc addsize(B: PBuffer, n: cint) = B.p = cast[cstring](cast[int](B.p) + n) -proc unref(L: PState, theref: int) = +proc unref(L: PState, theref: cint) = unref(L, REGISTRYINDEX, theref) -proc getref(L: PState, theref: int) = +proc getref(L: PState, theref: cint) = rawgeti(L, REGISTRYINDEX, theref) From 05f0444c025e388f0302dac7beeef8c143d80b4b Mon Sep 17 00:00:00 2001 From: Billingsly Wetherfordshire Date: Thu, 6 Jun 2013 03:51:10 -0600 Subject: [PATCH 12/83] cleaned up lua.nim --- lib/wrappers/lua/lua.nim | 326 +++++++++++++++++---------------------- 1 file changed, 139 insertions(+), 187 deletions(-) diff --git a/lib/wrappers/lua/lua.nim b/lib/wrappers/lua/lua.nim index 0346c4285..153714cee 100644 --- a/lib/wrappers/lua/lua.nim +++ b/lib/wrappers/lua/lua.nim @@ -51,16 +51,16 @@ when defined(useLuajit): else: when defined(MACOSX): const - NAME* = "liblua(|5.2|5.1|5.0).dylib" - LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib" + NAME* = "liblua(|5.1|5.0).dylib" + LIB_NAME* = "liblua(|5.1|5.0).dylib" elif defined(UNIX): const - NAME* = "liblua(|5.2|5.1|5.0).so(|.0)" - LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)" + NAME* = "liblua(|5.1|5.0).so(|.0)" + LIB_NAME* = "liblua(|5.1|5.0).so(|.0)" else: const - NAME* = "lua(|5.2|5.1|5.0).dll" - LIB_NAME* = "lua(|5.2|5.1|5.0).dll" + NAME* = "lua(|5.1|5.0).dll" + LIB_NAME* = "lua(|5.1|5.0).dll" const VERSION* = "Lua 5.1" @@ -76,7 +76,7 @@ const ENVIRONINDEX* = - 10001 GLOBALSINDEX* = - 10002 -proc upvalueindex*(I: int): int +proc upvalueindex*(I: cint): cint const # thread status; 0 is OK constYIELD* = 1 ERRRUN* = 2 @@ -86,16 +86,16 @@ const # thread status; 0 is OK type PState* = Pointer - CFunction* = proc (L: PState): int{.cdecl.} + CFunction* = proc (L: PState): cint{.cdecl.} # #** functions that read/write blocks when loading/dumping Lua chunks # type - Reader* = proc (L: PState, ud: Pointer, sz: ptr int): cstring{.cdecl.} - Writer* = proc (L: PState, p: Pointer, sz: int, ud: Pointer): int{.cdecl.} - Alloc* = proc (ud, theptr: Pointer, osize, nsize: int){.cdecl.} + Reader* = proc (L: PState, ud: Pointer, sz: ptr cint): cstring{.cdecl.} + Writer* = proc (L: PState, p: Pointer, sz: cint, ud: Pointer): cint{.cdecl.} + Alloc* = proc (ud, theptr: Pointer, osize, nsize: cint){.cdecl.} const TNONE* = - 1 @@ -112,130 +112,86 @@ const type # Type of Numbers in Lua Number* = float - Integer* = int + Integer* = cint + +{.pragma: ilua, importc: "lua_$1".} + +{.push callConv: cdecl, dynlib: LibName.} +#{.push importc: "lua_$1".} + +proc newstate*(f: Alloc, ud: Pointer): PState {.ilua.} + +proc close*(L: PState){.ilua.} +proc newthread*(L: PState): PState{.ilua.} +proc atpanic*(L: PState, panicf: CFunction): CFunction{.ilua.} + +proc gettop*(L: PState): cint{.ilua.} +proc settop*(L: PState, idx: cint){.ilua.} +proc pushvalue*(L: PState, Idx: cint){.ilua.} +proc remove*(L: PState, idx: cint){.ilua.} +proc insert*(L: PState, idx: cint){.ilua.} +proc replace*(L: PState, idx: cint){.ilua.} +proc checkstack*(L: PState, sz: cint): cint{.ilua.} +proc xmove*(`from`, `to`: PState, n: cint){.ilua.} +proc isnumber*(L: PState, idx: cint): cint{.ilua.} +proc isstring*(L: PState, idx: cint): cint{.ilua.} +proc iscfunction*(L: PState, idx: cint): cint{.ilua.} +proc isuserdata*(L: PState, idx: cint): cint{.ilua.} +proc luatype*(L: PState, idx: cint): cint{.importc: "lua_type".} +proc typename*(L: PState, tp: cint): cstring{.ilua.} +proc equal*(L: PState, idx1, idx2: cint): cint{.ilua.} +proc rawequal*(L: PState, idx1, idx2: cint): cint{.ilua.} +proc lessthan*(L: PState, idx1, idx2: cint): cint{.ilua.} +proc tonumber*(L: PState, idx: cint): Number{.ilua.} +proc tointeger*(L: PState, idx: cint): Integer{.ilua.} +proc toboolean*(L: PState, idx: cint): cint{.ilua.} +proc tolstring*(L: PState, idx: cint, length: ptr cint): cstring{.ilua.} +proc objlen*(L: PState, idx: cint): cint{.ilua.} +proc tocfunction*(L: PState, idx: cint): CFunction{.ilua.} +proc touserdata*(L: PState, idx: cint): Pointer{.ilua.} +proc tothread*(L: PState, idx: cint): PState{.ilua.} +proc topointer*(L: PState, idx: cint): Pointer{.ilua.} +proc pushnil*(L: PState){.ilua.} +proc pushnumber*(L: PState, n: Number){.ilua.} +proc pushinteger*(L: PState, n: Integer){.ilua.} +proc pushlstring*(L: PState, s: cstring, len: cint){.ilua.} +proc pushstring*(L: PState, s: cstring){.ilua.} +proc pushvfstring*(L: PState, fmt: cstring, argp: Pointer): cstring{.ilua.} +proc pushfstring*(L: PState, fmt: cstring): cstring{.varargs,ilua.} +proc pushcclosure*(L: PState, fn: CFunction, n: cint){.ilua.} +proc pushboolean*(L: PState, b: cint){.ilua.} +proc pushlightuserdata*(L: PState, p: Pointer){.ilua.} +proc pushthread*(L: PState){.ilua.} +proc gettable*(L: PState, idx: cint){.ilua.} +proc getfield*(L: Pstate, idx: cint, k: cstring){.ilua.} +proc rawget*(L: PState, idx: cint){.ilua.} +proc rawgeti*(L: PState, idx, n: cint){.ilua.} +proc createtable*(L: PState, narr, nrec: cint){.ilua.} +proc newuserdata*(L: PState, sz: cint): Pointer{.ilua.} +proc getmetatable*(L: PState, objindex: cint): cint{.ilua.} +proc getfenv*(L: PState, idx: cint){.ilua.} +proc settable*(L: PState, idx: cint){.ilua.} +proc setfield*(L: PState, idx: cint, k: cstring){.ilua.} +proc rawset*(L: PState, idx: cint){.ilua.} +proc rawseti*(L: PState, idx, n: cint){.ilua.} +proc setmetatable*(L: PState, objindex: cint): cint{.ilua.} +proc setfenv*(L: PState, idx: cint): cint{.ilua.} +proc call*(L: PState, nargs, nresults: cint){.ilua.} +proc pcall*(L: PState, nargs, nresults, errf: cint): cint{.ilua.} +proc cpcall*(L: PState, func: CFunction, ud: Pointer): cint{.ilua.} +proc load*(L: PState, reader: Reader, dt: Pointer, chunkname: cstring): cint{.ilua.} +proc dump*(L: PState, writer: Writer, data: Pointer): cint{.ilua.} +proc luayield*(L: PState, nresults: cint): cint{.importc: "lua_yield".} +proc resume*(L: PState, narg: cint): cint{.ilua.} +proc status*(L: PState): cint{.ilua.} +proc gc*(L: PState, what, data: cint): cint{.ilua.} +proc error*(L: PState): cint{.ilua.} +proc next*(L: PState, idx: cint): cint{.ilua.} +proc concat*(L: PState, n: cint){.ilua.} +proc getallocf*(L: PState, ud: ptr Pointer): Alloc{.ilua.} +proc setallocf*(L: PState, f: Alloc, ud: Pointer){.ilua.} +{.pop.} -proc newstate*(f: Alloc, ud: Pointer): PState{.cdecl, dynlib: NAME, - importc: "lua_newstate".} -proc close*(L: PState){.cdecl, dynlib: NAME, importc: "lua_close".} -proc newthread*(L: PState): PState{.cdecl, dynlib: NAME, - importc: "lua_newthread".} -proc atpanic*(L: PState, panicf: CFunction): CFunction{.cdecl, dynlib: NAME, - importc: "lua_atpanic".} -proc gettop*(L: PState): int{.cdecl, dynlib: NAME, importc: "lua_gettop".} -proc settop*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_settop".} -proc pushvalue*(L: PState, Idx: int){.cdecl, dynlib: NAME, - importc: "lua_pushvalue".} -proc remove*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_remove".} -proc insert*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_insert".} -proc replace*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_replace".} -proc checkstack*(L: PState, sz: int): cint{.cdecl, dynlib: NAME, - importc: "lua_checkstack".} -proc xmove*(`from`, `to`: PState, n: int){.cdecl, dynlib: NAME, - importc: "lua_xmove".} -proc isnumber*(L: PState, idx: int): cint{.cdecl, dynlib: NAME, - importc: "lua_isnumber".} -proc isstring*(L: PState, idx: int): cint{.cdecl, dynlib: NAME, - importc: "lua_isstring".} -proc iscfunction*(L: PState, idx: int): cint{.cdecl, dynlib: NAME, - importc: "lua_iscfunction".} -proc isuserdata*(L: PState, idx: int): cint{.cdecl, dynlib: NAME, - importc: "lua_isuserdata".} -proc luatype*(L: PState, idx: int): int{.cdecl, dynlib: NAME, importc: "lua_type".} -proc typename*(L: PState, tp: int): cstring{.cdecl, dynlib: NAME, - importc: "lua_typename".} -proc equal*(L: PState, idx1, idx2: int): cint{.cdecl, dynlib: NAME, - importc: "lua_equal".} -proc rawequal*(L: PState, idx1, idx2: int): cint{.cdecl, dynlib: NAME, - importc: "lua_rawequal".} -proc lessthan*(L: PState, idx1, idx2: int): cint{.cdecl, dynlib: NAME, - importc: "lua_lessthan".} -proc tonumber*(L: PState, idx: int): Number{.cdecl, dynlib: NAME, - importc: "lua_tonumber".} -proc tointeger*(L: PState, idx: int): Integer{.cdecl, dynlib: NAME, - importc: "lua_tointeger".} -proc toboolean*(L: PState, idx: int): cint{.cdecl, dynlib: NAME, - importc: "lua_toboolean".} -proc tolstring*(L: PState, idx: int, length: ptr int): cstring{.cdecl, - dynlib: NAME, importc: "lua_tolstring".} -proc objlen*(L: PState, idx: int): int{.cdecl, dynlib: NAME, - importc: "lua_objlen".} -proc tocfunction*(L: PState, idx: int): CFunction{.cdecl, dynlib: NAME, - importc: "lua_tocfunction".} -proc touserdata*(L: PState, idx: int): Pointer{.cdecl, dynlib: NAME, - importc: "lua_touserdata".} -proc tothread*(L: PState, idx: int): PState{.cdecl, dynlib: NAME, - importc: "lua_tothread".} -proc topointer*(L: PState, idx: int): Pointer{.cdecl, dynlib: NAME, - importc: "lua_topointer".} -proc pushnil*(L: PState){.cdecl, dynlib: NAME, importc: "lua_pushnil".} -proc pushnumber*(L: PState, n: Number){.cdecl, dynlib: NAME, - importc: "lua_pushnumber".} -proc pushinteger*(L: PState, n: Integer){.cdecl, dynlib: NAME, - importc: "lua_pushinteger".} -proc pushlstring*(L: PState, s: cstring, len: int){.cdecl, dynlib: NAME, - importc: "lua_pushlstring".} -proc pushstring*(L: PState, s: cstring){.cdecl, dynlib: NAME, - importc: "lua_pushstring".} -proc pushvfstring*(L: PState, fmt: cstring, argp: Pointer): cstring{.cdecl, - dynlib: NAME, importc: "lua_pushvfstring".} -proc pushfstring*(L: PState, fmt: cstring): cstring{.cdecl, varargs, - dynlib: NAME, importc: "lua_pushfstring".} -proc pushcclosure*(L: PState, fn: CFunction, n: int){.cdecl, dynlib: NAME, - importc: "lua_pushcclosure".} -proc pushboolean*(L: PState, b: cint){.cdecl, dynlib: NAME, - importc: "lua_pushboolean".} -proc pushlightuserdata*(L: PState, p: Pointer){.cdecl, dynlib: NAME, - importc: "lua_pushlightuserdata".} -proc pushthread*(L: PState){.cdecl, dynlib: NAME, importc: "lua_pushthread".} -proc gettable*(L: PState, idx: int){.cdecl, dynlib: NAME, - importc: "lua_gettable".} -proc getfield*(L: Pstate, idx: int, k: cstring){.cdecl, dynlib: NAME, - importc: "lua_getfield".} -proc rawget*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_rawget".} -proc rawgeti*(L: PState, idx, n: int){.cdecl, dynlib: NAME, - importc: "lua_rawgeti".} -proc createtable*(L: PState, narr, nrec: int){.cdecl, dynlib: NAME, - importc: "lua_createtable".} -proc newuserdata*(L: PState, sz: int): Pointer{.cdecl, dynlib: NAME, - importc: "lua_newuserdata".} -proc getmetatable*(L: PState, objindex: int): int{.cdecl, dynlib: NAME, - importc: "lua_getmetatable".} -proc getfenv*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_getfenv".} -proc settable*(L: PState, idx: int){.cdecl, dynlib: NAME, - importc: "lua_settable".} -proc setfield*(L: PState, idx: int, k: cstring){.cdecl, dynlib: NAME, - importc: "lua_setfield".} -proc rawset*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_rawset".} -proc rawseti*(L: PState, idx, n: int){.cdecl, dynlib: NAME, - importc: "lua_rawseti".} -proc setmetatable*(L: PState, objindex: int): int{.cdecl, dynlib: NAME, - importc: "lua_setmetatable".} -proc setfenv*(L: PState, idx: int): int{.cdecl, dynlib: NAME, - importc: "lua_setfenv".} -proc call*(L: PState, nargs, nresults: int){.cdecl, dynlib: NAME, - importc: "lua_call".} -proc pcall*(L: PState, nargs, nresults, errf: int): int{.cdecl, dynlib: NAME, - importc: "lua_pcall".} -proc cpcall*(L: PState, func: CFunction, ud: Pointer): int{.cdecl, dynlib: NAME, - importc: "lua_cpcall".} -proc load*(L: PState, reader: Reader, dt: Pointer, chunkname: cstring): int{. - cdecl, dynlib: NAME, importc: "lua_load".} -proc dump*(L: PState, writer: Writer, data: Pointer): int{.cdecl, dynlib: NAME, - importc: "lua_dump".} -proc luayield*(L: PState, nresults: int): int{.cdecl, dynlib: NAME, - importc: "lua_yield".} -proc resume*(L: PState, narg: int): int{.cdecl, dynlib: NAME, - importc: "lua_resume".} -proc status*(L: PState): int{.cdecl, dynlib: NAME, importc: "lua_status".} -proc gc*(L: PState, what, data: int): int{.cdecl, dynlib: NAME, - importc: "lua_gc".} -proc error*(L: PState): int{.cdecl, dynlib: NAME, importc: "lua_error".} -proc next*(L: PState, idx: int): int{.cdecl, dynlib: NAME, importc: "lua_next".} -proc concat*(L: PState, n: int){.cdecl, dynlib: NAME, importc: "lua_concat".} -proc getallocf*(L: PState, ud: ptr Pointer): Alloc{.cdecl, dynlib: NAME, - importc: "lua_getallocf".} -proc setallocf*(L: PState, f: Alloc, ud: Pointer){.cdecl, dynlib: NAME, - importc: "lua_setallocf".} # #** Garbage-collection functions and options # @@ -256,29 +212,29 @@ const #** =============================================================== # -proc pop*(L: PState, n: int) +proc pop*(L: PState, n: cint) proc newtable*(L: Pstate) proc register*(L: PState, n: cstring, f: CFunction) proc pushcfunction*(L: PState, f: CFunction) -proc strlen*(L: Pstate, i: int): int -proc isfunction*(L: PState, n: int): bool -proc istable*(L: PState, n: int): bool -proc islightuserdata*(L: PState, n: int): bool -proc isnil*(L: PState, n: int): bool -proc isboolean*(L: PState, n: int): bool -proc isthread*(L: PState, n: int): bool -proc isnone*(L: PState, n: int): bool -proc isnoneornil*(L: PState, n: int): bool +proc strlen*(L: Pstate, i: cint): cint +proc isfunction*(L: PState, n: cint): bool +proc istable*(L: PState, n: cint): bool +proc islightuserdata*(L: PState, n: cint): bool +proc isnil*(L: PState, n: cint): bool +proc isboolean*(L: PState, n: cint): bool +proc isthread*(L: PState, n: cint): bool +proc isnone*(L: PState, n: cint): bool +proc isnoneornil*(L: PState, n: cint): bool proc pushliteral*(L: PState, s: cstring) proc setglobal*(L: PState, s: cstring) proc getglobal*(L: PState, s: cstring) -proc tostring*(L: PState, i: int): cstring +proc tostring*(L: PState, i: cint): cstring # #** compatibility macros and functions # proc getregistry*(L: PState) -proc getgccount*(L: PState): int +proc getgccount*(L: PState): cint type Chunkreader* = Reader Chunkwriter* = Writer @@ -307,18 +263,18 @@ const type TDebug*{.final.} = object # activation record - event*: int + event*: cint name*: cstring # (n) namewhat*: cstring # (n) `global', `local', `field', `method' what*: cstring # (S) `Lua', `C', `main', `tail' source*: cstring # (S) - currentline*: int # (l) - nups*: int # (u) number of upvalues - linedefined*: int # (S) - lastlinedefined*: int # (S) - short_src*: array[0..IDSIZE - 1, Char] # (S) - # private part - i_ci*: int # active function + currentline*: cint # (l) + nups*: cint # (u) number of upvalues + linedefined*: cint # (S) + lastlinedefined*: cint # (S) + short_src*: array[0.. Date: Thu, 6 Jun 2013 03:51:38 -0600 Subject: [PATCH 13/83] cleaned up lualib.nim --- lib/wrappers/lua/lualib.nim | 38 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/wrappers/lua/lualib.nim b/lib/wrappers/lua/lualib.nim index 94f08a1f1..8c1237dde 100644 --- a/lib/wrappers/lua/lualib.nim +++ b/lib/wrappers/lua/lualib.nim @@ -32,35 +32,33 @@ const DBLIBNAME* = "debug" LOADLIBNAME* = "package" -proc open_base*(L: PState): cint{.cdecl, dynlib: LIB_NAME, - importc: "luaopen_base".} -proc open_table*(L: PState): cint{.cdecl, dynlib: LIB_NAME, - importc: "luaopen_table".} -proc open_io*(L: PState): cint{.cdecl, dynlib: LIB_NAME, importc: "luaopen_io".} -proc open_string*(L: PState): cint{.cdecl, dynlib: LIB_NAME, - importc: "luaopen_string".} -proc open_math*(L: PState): cint{.cdecl, dynlib: LIB_NAME, - importc: "luaopen_math".} -proc open_debug*(L: PState): cint{.cdecl, dynlib: LIB_NAME, - importc: "luaopen_debug".} -proc open_package*(L: PState): cint{.cdecl, dynlib: LIB_NAME, - importc: "luaopen_package".} -proc openlibs*(L: PState){.cdecl, dynlib: LIB_NAME, importc: "luaL_openlibs".} +{.pragma: ilua, importc: "lua$1".} + +{.push callConv: cdecl, dynlib: lua.LIB_NAME.} +proc open_base*(L: PState): cint{.ilua.} +proc open_table*(L: PState): cint{.ilua.} +proc open_io*(L: PState): cint{.ilua.} +proc open_string*(L: PState): cint{.ilua.} +proc open_math*(L: PState): cint{.ilua.} +proc open_debug*(L: PState): cint{.ilua.} +proc open_package*(L: PState): cint{.ilua.} +proc openlibs*(L: PState){.importc: "luaL_openlibs".} +{.pop.} proc baselibopen*(L: PState): Bool = - Result = open_base(L) != 0'i32 + open_base(L) != 0'i32 proc tablibopen*(L: PState): Bool = - Result = open_table(L) != 0'i32 + open_table(L) != 0'i32 proc iolibopen*(L: PState): Bool = - Result = open_io(L) != 0'i32 + open_io(L) != 0'i32 proc strlibopen*(L: PState): Bool = - Result = open_string(L) != 0'i32 + open_string(L) != 0'i32 proc mathlibopen*(L: PState): Bool = - Result = open_math(L) != 0'i32 + open_math(L) != 0'i32 proc dblibopen*(L: PState): Bool = - Result = open_debug(L) != 0'i32 + open_debug(L) != 0'i32 From 9604df84316af83fe80a77ad1aeb2eacdd0466cb Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 6 Jun 2013 23:15:05 +0200 Subject: [PATCH 14/83] fixes #466 --- compiler/ccgtypes.nim | 18 +++++++++--------- compiler/cgen.nim | 2 +- config/nimrod.cfg | 4 ++-- lib/nimbase.h | 4 ++-- todo.txt | 1 - 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 427b0ec86..88e7e264d 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -674,7 +674,7 @@ proc genProcHeader(m: BModule, prc: PSym): PRope = # ------------------ type info generation ------------------------------------- -proc genTypeInfo(m: BModule, typ: PType): PRope +proc genTypeInfo(m: BModule, t: PType): PRope proc getNimNode(m: BModule): PRope = result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)]) inc(m.typeNodes) @@ -692,7 +692,7 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and (typ.sons[0] == nil) or isPureObject(typ)) -proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = +proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = var nimtypeKind: int #allocMemTI(m, typ, name) if isObjLackingTypeField(typ): @@ -901,21 +901,21 @@ type include ccgtrav -proc genTypeInfo(m: BModule, typ: PType): PRope = - var t = getUniqueType(typ) +proc genTypeInfo(m: BModule, t: PType): PRope = + var t = getUniqueType(t) result = ropef("NTI$1", [toRope(t.id)]) - let owner = typ.skipTypes(typedescPtrs).owner.getModule + if ContainsOrIncl(m.typeInfoMarker, t.id): + return con("(&".toRope, result, ")".toRope) + let owner = t.skipTypes(typedescPtrs).owner.getModule if owner != m.module: # make sure the type info is created in the owner module - discard genTypeInfo(owner.bmod, typ) - # refenrece the type info as extern here + discard genTypeInfo(owner.bmod, t) + # reference the type info as extern here discard cgsym(m, "TNimType") discard cgsym(m, "TNimNode") appf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n", [result, toRope(typeToString(t))]) return con("(&".toRope, result, ")".toRope) - if ContainsOrIncl(m.typeInfoMarker, t.id): - return con("(&".toRope, result, ")".toRope) case t.kind of tyEmpty: result = toRope"0" of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 7e782fc12..45fb9f878 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1031,7 +1031,7 @@ proc genInitCode(m: BModule) = var procname = CStringLit(m.initProc, prc, m.module.name.s) app(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) else: - app(prc, ~"\tvolatile TFrame F; F.len = 0;$N") + app(prc, ~"\tTFrame F; F.len = 0;$N") app(prc, genSectionStart(cpsInit)) app(prc, m.preInitProc.s(cpsInit)) diff --git a/config/nimrod.cfg b/config/nimrod.cfg index d9075a20f..18c746213 100644 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -93,10 +93,10 @@ hint[LineTooLong]=off cc = clang tlsEmulation:on gcc.options.always = "-w -fasm-blocks" - gpp.options.always = "-w -fasm-blocks" + gpp.options.always = "-w -fasm-blocks -fpermissive" @else: gcc.options.always = "-w" - gpp.options.always = "-w" + gpp.options.always = "-w -fpermissive" @end gcc.options.speed = "-O3 -fno-strict-aliasing" diff --git a/lib/nimbase.h b/lib/nimbase.h index f47ab4d29..af06e447d 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -437,11 +437,11 @@ struct TFrame { }; #define nimfr(proc, file) \ - volatile TFrame F; \ + TFrame F; \ F.procname = proc; F.filename = file; F.line = 0; F.len = 0; nimFrame(&F); #define nimfrs(proc, file, slots, length) \ - volatile struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \ + struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \ F.procname = proc; F.filename = file; F.line = 0; F.len = length; nimFrame((TFrame*)&F); #define nimln(n, file) \ diff --git a/todo.txt b/todo.txt index 2aea8660f..ad0a4afa2 100644 --- a/todo.txt +++ b/todo.txt @@ -24,7 +24,6 @@ Bugs - blocks can "export" an identifier but the CCG generates {} for them ... - osproc execProcesses can deadlock if all processes fail (as experienced in c++ mode) -- bootstrapping does not work in C++ mode - result = result shr 8 for the "system()" wrapper From 2793170feddab3e2124216c695de45ae7c541477 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 18:25:57 +0200 Subject: [PATCH 15/83] Makes caasdriver exit with number of errors. Also displays result and separator after each test. --- .gitignore | 3 +++ tests/caasdriver.nim | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 7bca71531..93dae69ba 100644 --- a/.gitignore +++ b/.gitignore @@ -167,4 +167,7 @@ examples/cross_calculator/android/tags /run.json /testresults.html /testresults.json +/tests/caas/imported +/tests/caas/main +/tests/caasdriver /tools/nimgrep diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index 3f3d3671e..7472cfdeb 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -18,9 +18,9 @@ proc startNimrodSession*(project: string): TNimrodSession = proc doCommand*(session: var TNimrodSession, command: string): string = session.nim.inputStream.write(command & "\n") session.nim.inputStream.flush - + result = "" - + while true: var line = TaintedString("") if session.nim.outputStream.readLine(line): @@ -38,7 +38,7 @@ proc doScenario(script: string, output: PStream): bool = var f = open(script) var project = TaintedString("") - + if f.readLine(project): var s = startNimrodSession(script.dirname / project.string) @@ -78,9 +78,14 @@ iterator caasTestsRunner*(filter = ""): tuple[test, output: string, yield (scenario, outStream.data, r) when isMainModule: - var filter = "" + var + filter = "" + failures = 0 if paramCount() > 0: filter = paramStr(1) - - for t, o, r in caasTestsRunner(filter): - echo t, "\n", o - + + for test, output, result in caasTestsRunner(filter): + echo test, "\n", output, "-> ", $result, "\n-----" + if not result: + failures += 1 + + quit(failures) From 18725aab28b7a6a58926b34c263252f77cf94b7d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 18:32:43 +0200 Subject: [PATCH 16/83] Adds verbose switch, by default reports only failures. --- tests/caasdriver.nim | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index 7472cfdeb..b36b6f5b7 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -81,10 +81,20 @@ when isMainModule: var filter = "" failures = 0 - if paramCount() > 0: filter = paramStr(1) + verbose = false + + for i in 0..ParamCount() - 1: + let param = paramStr(i + 1) + case param + of "verbose": verbose = true + else: filter = param + + if verbose and len(filter) > 0: + echo "Running only test cases matching filter '$1'" % [filter] for test, output, result in caasTestsRunner(filter): - echo test, "\n", output, "-> ", $result, "\n-----" + if not result or verbose: + echo test, "\n", output, "-> ", $result, "\n-----" if not result: failures += 1 From 298f546be0ec4fa5ff1d8877780d002d4edf242c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 18:47:31 +0200 Subject: [PATCH 17/83] Adds embedded comment describing program usage. --- tests/caasdriver.nim | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index b36b6f5b7..f319512b9 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -1,5 +1,27 @@ import osproc, streams, os, strutils, re +## Compiler as a service tester. +## +## This test cases uses the txt files in the caas/ subdirectory. +## Each of the text files inside encodes a session with the compiler. +## The first line indicates the main project file. Lines starting with '>' +## indicate a command to be sent to the compiler and the lines following a +## command include checks for expected or forbidden output (! for forbidden). +## +## You can optionally pass parameters at the command line to modify the +## behaviour of the test suite. By default only tests which fail will be echoed +## to stdout. If you want to see all the output pass the word "verbose" as a +## parameter. +## +## If you don't want to run all the test case files, you can pass any substring +## as a parameter. Only files matching the passed substring will be run. The +## filtering doesn't use any globbing metacharacters, it's a plain match. +## +## Example to run only "*-compile*.txt" tests in verbose mode: +## +## ./caasdriver verbose -compile + + type TNimrodSession* = object nim: PProcess From 9b0e03cb2e87d325cca232ccd36a3e2735c3a5c9 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 18:51:52 +0200 Subject: [PATCH 18/83] Replaces dirname with osproc.parentDir. --- tests/caasdriver.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index f319512b9..9d3e91b22 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -26,15 +26,13 @@ type TNimrodSession* = object nim: PProcess -proc dirname(path: string): string = path.splitPath()[0] - var TesterDir = getAppDir() NimrodBin = TesterDir / "../bin/nimrod" proc startNimrodSession*(project: string): TNimrodSession = result.nim = startProcess(NimrodBin, - workingDir = project.dirname, + workingDir = project.parentDir, args = ["serve", "--server.type:stdin", project]) proc doCommand*(session: var TNimrodSession, command: string): string = @@ -63,7 +61,7 @@ proc doScenario(script: string, output: PStream): bool = if f.readLine(project): var - s = startNimrodSession(script.dirname / project.string) + s = startNimrodSession(script.parentDir / project.string) tline = TaintedString("") lastOutput = "" ln = 1 From 8f76092b89212ed9f691d288d11dc660dacc9d52 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 19:47:33 +0200 Subject: [PATCH 19/83] Runs all caas tests also in process mode. This requires changing a little bit the test cases to avoid specifying the filename as the last parameter. The trackDirty switch still doesn't work for process mode. Also revers some SuccessX which I think were hidden in the amount of output verbosity. --- tests/caas/compile-suggest.txt | 2 +- tests/caas/compile-then-def.txt | 8 ++-- tests/caas/def-def-compile.txt | 8 ++-- tests/caas/def-then-compile.txt | 4 +- tests/caas/suggest-compile.txt | 2 +- tests/caasdriver.nim | 75 ++++++++++++++++++++++++--------- 6 files changed, 67 insertions(+), 32 deletions(-) diff --git a/tests/caas/compile-suggest.txt b/tests/caas/compile-suggest.txt index 4e2ab9729..3cc0ee0cb 100644 --- a/tests/caas/compile-suggest.txt +++ b/tests/caas/compile-suggest.txt @@ -1,7 +1,7 @@ main.nim > c SuccessX -> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest main.nim +> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest skField\tx skField\ty diff --git a/tests/caas/compile-then-def.txt b/tests/caas/compile-then-def.txt index a5080c50d..839ca1532 100644 --- a/tests/caas/compile-then-def.txt +++ b/tests/caas/compile-then-def.txt @@ -1,9 +1,9 @@ main.nim > c SuccessX -> idetools --track:main.nim,5,18 --def main.nim +> idetools --track:main.nim,5,18 --def strutils.toUpper -SuccessX -> idetools --track:main.nim,5,18 --def main.nim +! SuccessX +> idetools --track:main.nim,5,18 --def strutils.toUpper -SuccessX +! SuccessX diff --git a/tests/caas/def-def-compile.txt b/tests/caas/def-def-compile.txt index 64002aff1..05ea5a1aa 100644 --- a/tests/caas/def-def-compile.txt +++ b/tests/caas/def-def-compile.txt @@ -1,10 +1,10 @@ main.nim -> idetools --track:main.nim,5,18 --def main.nim +> idetools --track:main.nim,5,18 --def strutils.toUpper -SuccessX -> idetools --track:main.nim,5,18 --def main.nim +! SuccessX +> idetools --track:main.nim,5,18 --def strutils.toUpper -SuccessX +! SuccessX > c SuccessX diff --git a/tests/caas/def-then-compile.txt b/tests/caas/def-then-compile.txt index efa4dcbb3..7e755cff2 100644 --- a/tests/caas/def-then-compile.txt +++ b/tests/caas/def-then-compile.txt @@ -1,7 +1,7 @@ main.nim -> idetools --track:main.nim,5,18 --def main.nim +> idetools --track:main.nim,5,18 --def strutils.toUpper -SuccessX +! SuccessX > c SuccessX diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt index 49d0dc431..76756c86e 100644 --- a/tests/caas/suggest-compile.txt +++ b/tests/caas/suggest-compile.txt @@ -1,5 +1,5 @@ main.nim -> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest main.nim +> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest skField\tx skField\ty > c diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index 9d3e91b22..2f482a810 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -23,19 +23,30 @@ import osproc, streams, os, strutils, re type + TRunMode = enum + ProcRun, CaasRun + TNimrodSession* = object - nim: PProcess + nim: PProcess # Holds the open process for CaasRun sessions, nil otherwise. + mode: TRunMode # Stores the type of run mode the session was started with. + lastOutput: string # Preserves the last output, needed for ProcRun mode. + filename: string # Appended to each command starting with '>'. var TesterDir = getAppDir() NimrodBin = TesterDir / "../bin/nimrod" -proc startNimrodSession*(project: string): TNimrodSession = - result.nim = startProcess(NimrodBin, - workingDir = project.parentDir, - args = ["serve", "--server.type:stdin", project]) +proc startNimrodSession(project: string, mode: TRunMode): TNimrodSession = + let (dir, name) = project.SplitPath + result.mode = mode + result.lastOutput = "" + result.filename = name + if mode == CaasRun: + result.nim = startProcess(NimrodBin, workingDir = dir, + args = ["serve", "--server.type:stdin", name]) -proc doCommand*(session: var TNimrodSession, command: string): string = +proc doCaasCommand(session: var TNimrodSession, command: string): string = + assert session.mode == CaasRun session.nim.inputStream.write(command & "\n") session.nim.inputStream.flush @@ -50,10 +61,34 @@ proc doCommand*(session: var TNimrodSession, command: string): string = result = "FAILED TO EXECUTE: " & command & "\n" & result break -proc close(session: var TNimrodSession) {.destructor.} = - session.nim.close +proc doProcCommand(session: var TNimrodSession, command: string): string = + assert session.mode == ProcRun + except: result = "FAILED TO EXECUTE: " & command & "\n" & result + var + process = startProcess(NimrodBin, args = command.split) + stream = outputStream(process) + line = TaintedString("") -proc doScenario(script: string, output: PStream): bool = + result = "" + while stream.readLine(line): + if result.len > 0: result &= "\n" + result &= line.string + + process.close() + +proc doCommand(session: var TNimrodSession, command: string) = + if session.mode == CaasRun: + session.lastOutput = doCaasCommand(session, + command & " " & session.filename) + else: + session.lastOutput = doProcCommand(session, + command & " " & session.filename) + +proc close(session: var TNimrodSession) {.destructor.} = + if session.mode == CaasRun: + session.nim.close + +proc doScenario(script: string, output: PStream, mode: TRunMode): bool = result = true var f = open(script) @@ -61,9 +96,8 @@ proc doScenario(script: string, output: PStream): bool = if f.readLine(project): var - s = startNimrodSession(script.parentDir / project.string) + s = startNimrodSession(script.parentDir / project.string, mode) tline = TaintedString("") - lastOutput = "" ln = 1 while f.readLine(tline): @@ -72,8 +106,8 @@ proc doScenario(script: string, output: PStream): bool = if line.strip.len == 0: continue if line.startsWith(">"): - lastOutput = s.doCommand(line.substr(1).strip) - output.writeln line, "\n", lastOutput + s.doCommand(line.substr(1).strip) + output.writeln line, "\n", s.lastOutput else: var expectMatch = true var pattern = line @@ -81,7 +115,7 @@ proc doScenario(script: string, output: PStream): bool = pattern = line.substr(1).strip expectMatch = false - var actualMatch = lastOutput.find(re(pattern)) != -1 + var actualMatch = s.lastOutput.find(re(pattern)) != -1 if expectMatch == actualMatch: output.writeln "SUCCESS ", line @@ -90,12 +124,13 @@ proc doScenario(script: string, output: PStream): bool = result = false iterator caasTestsRunner*(filter = ""): tuple[test, output: string, - status: bool] = + status: bool, mode: TRunMode] = for scenario in os.walkFiles(TesterDir / "caas/*.txt"): if filter.len > 0 and find(scenario, filter) == -1: continue - var outStream = newStringStream() - let r = doScenario(scenario, outStream) - yield (scenario, outStream.data, r) + for mode in [CaasRun, ProcRun]: + var outStream = newStringStream() + let r = doScenario(scenario, outStream, mode) + yield (scenario, outStream.data, r, mode) when isMainModule: var @@ -112,9 +147,9 @@ when isMainModule: if verbose and len(filter) > 0: echo "Running only test cases matching filter '$1'" % [filter] - for test, output, result in caasTestsRunner(filter): + for test, output, result, mode in caasTestsRunner(filter): if not result or verbose: - echo test, "\n", output, "-> ", $result, "\n-----" + echo test, "\n", output, "-> ", $mode, ":", $result, "\n-----" if not result: failures += 1 From 1d813167c4464e3d955764ca54a1df51817ace4d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 20:26:09 +0200 Subject: [PATCH 20/83] Implements prefixes for comments or specific mode runs. --- tests/caas/basic-recompile.txt | 5 ++++- tests/caas/compile-suggest.txt | 5 +++-- tests/caas/suggest-compile.txt | 5 +++-- tests/caasdriver.nim | 39 ++++++++++++++++++++++++++++------ 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/tests/caas/basic-recompile.txt b/tests/caas/basic-recompile.txt index 9c943fe88..d869944b9 100644 --- a/tests/caas/basic-recompile.txt +++ b/tests/caas/basic-recompile.txt @@ -2,6 +2,9 @@ main.nim > c SuccessX > c -! Processing +# The "Processing" string will be found always in proc mode since each +# compilation command will generate it. We need to test it only in Caas mode to +# verify the server is not recompiling again the file. +CaasRun ! Processing SuccessX diff --git a/tests/caas/compile-suggest.txt b/tests/caas/compile-suggest.txt index 3cc0ee0cb..f90fc5aa1 100644 --- a/tests/caas/compile-suggest.txt +++ b/tests/caas/compile-suggest.txt @@ -1,7 +1,8 @@ main.nim > c SuccessX +# Ugh, undocumented trackDirty, how is it supposed to work? Fails in proc mode. > idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest -skField\tx -skField\ty +CaasRun skField\tx +CaasRun skField\ty diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt index 76756c86e..897d14f52 100644 --- a/tests/caas/suggest-compile.txt +++ b/tests/caas/suggest-compile.txt @@ -1,7 +1,8 @@ main.nim +# Ugh, undocumented trackDirty, how is it supposed to work? Fails in proc mode. > idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest -skField\tx -skField\ty +CaasRun skField\tx +CaasRun skField\ty > c SuccessX diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index 2f482a810..d6f4a8bd6 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -3,10 +3,23 @@ import osproc, streams, os, strutils, re ## Compiler as a service tester. ## ## This test cases uses the txt files in the caas/ subdirectory. -## Each of the text files inside encodes a session with the compiler. -## The first line indicates the main project file. Lines starting with '>' -## indicate a command to be sent to the compiler and the lines following a -## command include checks for expected or forbidden output (! for forbidden). +## +## Each of the text files inside encodes a session with the compiler: +## +## The first line indicates the main project file. +## +## Lines starting with '>' indicate a command to be sent to the compiler and +## the lines following a command include checks for expected or forbidden +## output (! for forbidden). +## +## If a line starts with '#' it will be ignored completely, so you can use that +## for comments. +## +## All the tests are run both in ProcRun (each command creates a separate +## process) and CaasRun (first command starts up a server and it is reused for +## the rest) modes. Since some cases are specific to either ProcRun or CaasRun +## modes, you can prefix a line with the mode and the line will be processed +## only in that mode. ## ## You can optionally pass parameters at the command line to modify the ## behaviour of the test suite. By default only tests which fail will be echoed @@ -32,6 +45,8 @@ type lastOutput: string # Preserves the last output, needed for ProcRun mode. filename: string # Appended to each command starting with '>'. +const modes = [CaasRun, ProcRun] + var TesterDir = getAppDir() NimrodBin = TesterDir / "../bin/nimrod" @@ -103,9 +118,21 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool = while f.readLine(tline): var line = tline.string inc ln + + # Filter lines by run mode, removing the prefix if the mode is current. + for testMode in modes: + if line.startsWith($testMode): + if testMode != mode: + line = "" + else: + line = line[len($testMode)..len(line) - 1].strip + break + if line.strip.len == 0: continue - if line.startsWith(">"): + if line.startsWith("#"): + continue + elif line.startsWith(">"): s.doCommand(line.substr(1).strip) output.writeln line, "\n", s.lastOutput else: @@ -127,7 +154,7 @@ iterator caasTestsRunner*(filter = ""): tuple[test, output: string, status: bool, mode: TRunMode] = for scenario in os.walkFiles(TesterDir / "caas/*.txt"): if filter.len > 0 and find(scenario, filter) == -1: continue - for mode in [CaasRun, ProcRun]: + for mode in modes: var outStream = newStringStream() let r = doScenario(scenario, outStream, mode) yield (scenario, outStream.data, r, mode) From f11a3b2324290cfebc328284c6f74287e7652e44 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 21:24:45 +0200 Subject: [PATCH 21/83] Adds generic idetools api test case. At the moment three lines of the test case fail in proc mode. --- .gitignore | 1 + tests/caas/idetools_api.nim | 23 +++++++++++++++++++++ tests/caas/idetools_api.txt | 41 +++++++++++++++++++++++++++++++++++++ tests/caasdriver.nim | 6 +++++- 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/caas/idetools_api.nim create mode 100644 tests/caas/idetools_api.txt diff --git a/.gitignore b/.gitignore index 93dae69ba..025c4ca35 100644 --- a/.gitignore +++ b/.gitignore @@ -167,6 +167,7 @@ examples/cross_calculator/android/tags /run.json /testresults.html /testresults.json +/tests/caas/idetools_api /tests/caas/imported /tests/caas/main /tests/caasdriver diff --git a/tests/caas/idetools_api.nim b/tests/caas/idetools_api.nim new file mode 100644 index 000000000..930d26429 --- /dev/null +++ b/tests/caas/idetools_api.nim @@ -0,0 +1,23 @@ +import unicode, sequtils + +proc test_enums() = + var o: Tfile + if o.open("files " & "test.txt", fmWrite): + o.write("test") + o.close() + +proc test_iterators(filename = "tests.nim") = + let + input = readFile(filename) + letters = toSeq(runes(string(input))) + for letter in letters: echo int(letter) + +const SOME_SEQUENCE = @[1, 2] +type + bad_string = distinct string + TPerson = object of TObject + name*: bad_string + age: int + +proc adder(a, b: int): int = + result = a + b diff --git a/tests/caas/idetools_api.txt b/tests/caas/idetools_api.txt new file mode 100644 index 000000000..eaa91cd5f --- /dev/null +++ b/tests/caas/idetools_api.txt @@ -0,0 +1,41 @@ +idetools_api.nim +> c +SuccessX +> idetools --track:idetools_api.nim,4,11 --def +def\tskType\tsystem.TFile\tTFile +> idetools --track:idetools_api.nim,5,7 --def +def\tskProc\tsystem.Open\tproc \(var TFile, string, TFileMode, int\): bool +> idetools --track:idetools_api.nim,5,21 --def +def\tskProc\tsystem.\&\tproc \(string, string\): string\{.noSideEffect.\} +> idetools --track:idetools_api.nim,5,38 --def +def\tskEnumField\tsystem.TFileMode.fmWrite\tTFileMode +> idetools --track:idetools_api.nim,7,6 --def +def\tskProc\tsystem.Close\tproc \(TFile\) +> idetools --track:idetools_api.nim,12,23 --def +def\tskIterator\tunicode.runes\titerator \(string\): TRune +> idetools --track:idetools_api.nim,12,15 --def +def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr +> idetools --track:idetools_api.nim,15,7 --def + +# ProcRun mode will fail the next line, because the type is returned empty. +def\tskConst\tidetools_api.SOME_SEQUENCE\tseq\[int\]\t +> idetools --track:idetools_api.nim,15,23 --def +def\tskProc\tsystem.@\tproc \(array\[IDX, T\]\): seq\[T\]\{.noSideEffect.\} +> idetools --track:idetools_api.nim,17,3 --def + +# ProcRun mode will fail the next line, because the type is returned empty. +def\tskType\tidetools_api.bad_string\tbad_string\t +> idetools --track:idetools_api.nim,11,24 --def +def\tskParam\tidetools_api.test_iterators.filename\tstring +> idetools --track:idetools_api.nim,6,5 --def +def\tskVar\tidetools_api.test_enums.o\tTFile +> idetools --track:idetools_api.nim,12,34 --def +def\tskLet\tidetools_api.test_iterators.input\tTaintedString +> idetools --track:idetools_api.nim,13,35 --def +def\tskForVar\tidetools_api.test_iterators.letter\tTRune +> idetools --track:idetools_api.nim,23,3 --def +def\tskResult\tidetools_api.adder.result\tint +> idetools --track:idetools_api.nim,19,6 --def + +# ProcRun mode will fail the next line, because the type is returned empty. +def\tskField\tidetools_api.TPerson.name\tbad_string\t diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index d6f4a8bd6..e1270f08a 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -21,6 +21,9 @@ import osproc, streams, os, strutils, re ## modes, you can prefix a line with the mode and the line will be processed ## only in that mode. ## +## The rest of the line is treated as a regular expression, so be careful +## escaping metacharacters like parenthesis. +## ## You can optionally pass parameters at the command line to modify the ## behaviour of the test suite. By default only tests which fail will be echoed ## to stdout. If you want to see all the output pass the word "verbose" as a @@ -142,7 +145,8 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool = pattern = line.substr(1).strip expectMatch = false - var actualMatch = s.lastOutput.find(re(pattern)) != -1 + let actualMatch = + s.lastOutput.find(re(pattern, flags = {reStudy})) != -1 if expectMatch == actualMatch: output.writeln "SUCCESS ", line From 0d333fdcb446a89e079d0722ef140c1d7c3bbf39 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 21:38:07 +0200 Subject: [PATCH 22/83] Adds idetools test case. Refs #416. --- .gitignore | 1 + tests/caas/issue_416.nim | 17 +++++++++++++++++ tests/caas/issue_416.txt | 14 ++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 tests/caas/issue_416.nim create mode 100644 tests/caas/issue_416.txt diff --git a/.gitignore b/.gitignore index 025c4ca35..b57f95571 100644 --- a/.gitignore +++ b/.gitignore @@ -169,6 +169,7 @@ examples/cross_calculator/android/tags /testresults.json /tests/caas/idetools_api /tests/caas/imported +/tests/caas/issue_416 /tests/caas/main /tests/caasdriver /tools/nimgrep diff --git a/tests/caas/issue_416.nim b/tests/caas/issue_416.nim new file mode 100644 index 000000000..d52f611d6 --- /dev/null +++ b/tests/caas/issue_416.nim @@ -0,0 +1,17 @@ +import unicode, sequtils + +proc test() = + let input = readFile("weird.nim") + for letter in runes(string(input)): + echo int(letter) + +when 1 > 0: + proc failtest() = + let + input = readFile("weird.nim") + letters = toSeq(runes(string(input))) + for letter in letters: + echo int(letter) + +when isMainModule: + test() diff --git a/tests/caas/issue_416.txt b/tests/caas/issue_416.txt new file mode 100644 index 000000000..7f81c2b2c --- /dev/null +++ b/tests/caas/issue_416.txt @@ -0,0 +1,14 @@ +issue_416.nim +> c +SuccessX +> idetools --track:issue_416.nim,12,28 --def +def\tskType\tsystem.string\tstring +> idetools --track:issue_416.nim,12,35 --def +def\tskLet\tissue_416.failtest.input\tTaintedString + +# The following fail because they seem shifted one colum to the right. +> idetools --track:issue_416.nim,12,16 --def +def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr +> idetools --track:issue_416.nim,12,22 --def +def\tskIterator\tunicode.runes\titerator \(string\): TRune + From 27ebd3ce2e411458d2f06b633285b1ba0a9b096d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 21:43:27 +0200 Subject: [PATCH 23/83] Adds comments to output, can help debug failure logs. --- tests/caasdriver.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index e1270f08a..2de046a11 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -134,6 +134,7 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool = if line.strip.len == 0: continue if line.startsWith("#"): + output.writeln line continue elif line.startsWith(">"): s.doCommand(line.substr(1).strip) From b0e71709657d9c2d8b00b762a710b230a2cc8149 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 7 Jun 2013 22:06:04 +0200 Subject: [PATCH 24/83] Adds idetools testcase. Refs #416. --- .gitignore | 1 + tests/caas/issue_452.nim | 8 ++++++++ tests/caas/issue_452.txt | 11 +++++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/caas/issue_452.nim create mode 100644 tests/caas/issue_452.txt diff --git a/.gitignore b/.gitignore index b57f95571..f05ca2016 100644 --- a/.gitignore +++ b/.gitignore @@ -170,6 +170,7 @@ examples/cross_calculator/android/tags /tests/caas/idetools_api /tests/caas/imported /tests/caas/issue_416 +/tests/caas/issue_452 /tests/caas/main /tests/caasdriver /tools/nimgrep diff --git a/tests/caas/issue_452.nim b/tests/caas/issue_452.nim new file mode 100644 index 000000000..46cff6241 --- /dev/null +++ b/tests/caas/issue_452.nim @@ -0,0 +1,8 @@ +const + VERSION_STR1* = "0.5.0" ## Idetools shifts this one column. + VERSION_STR2 = "0.5.0" ## This one is ok. + VERSION_STR3* = "0.5.0" ## Bad. + VERSION_STR4 = "0.5.0" ## Ok. + +proc forward1*(): string = result = "" +proc forward2(): string = result = "" diff --git a/tests/caas/issue_452.txt b/tests/caas/issue_452.txt new file mode 100644 index 000000000..5db60516c --- /dev/null +++ b/tests/caas/issue_452.txt @@ -0,0 +1,11 @@ +issue_452.nim +> c +SuccessX +> idetools --track:issue_452.nim,2,2 --def +def\tskConst\tissue_452.VERSION_STR1\tstring +> idetools --track:issue_452.nim,3,2 --def +def\tskConst\tissue_452.VERSION_STR2\tstring +> idetools --track:issue_452.nim,7,5 --def +def\tskProc\tissue_452.forward1\tproc \(\): string\t +> idetools --track:issue_452.nim,8,5 --def +def\tskProc\tissue_452.forward2\tproc \(\): string\t From 97eb4bd0ff5792ebd1cb958a726127af1129ce7b Mon Sep 17 00:00:00 2001 From: zah Date: Sat, 8 Jun 2013 11:19:45 +0300 Subject: [PATCH 25/83] documented trackDirty --- doc/advopt.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/advopt.txt b/doc/advopt.txt index 38461244d..3b6fafd0f 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -12,6 +12,8 @@ Advanced commands: //check checks the project for syntax and semantic //idetools compiler support for IDEs: possible options: --track:FILE,LINE,COL track a file/cursor position + --trackDirty:DIRTY_FILE,ORIG_FILE,LINE,COL + track a file, currently not saved to disk --suggest suggest all possible symbols at position --def list all possible definitions at position --context list possible invokation context From 1bbabd1e81ab8dfcb15287594c4479141b8c21a0 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 8 Jun 2013 20:47:26 +0200 Subject: [PATCH 26/83] Removes CaasRun prefix from suggest-compile.txt test. The --trackDirty switch should work the same for ProcRun, so it's a bug that it doesn't dump the same suggestion information as in server mode. --- tests/caas/suggest-compile.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt index 897d14f52..a74d4dc21 100644 --- a/tests/caas/suggest-compile.txt +++ b/tests/caas/suggest-compile.txt @@ -1,8 +1,8 @@ main.nim # Ugh, undocumented trackDirty, how is it supposed to work? Fails in proc mode. > idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest -CaasRun skField\tx -CaasRun skField\ty +skField\tx +skField\ty > c SuccessX From a7e70e6229168b303fcf2a945923da776ddf41c4 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 9 Jun 2013 15:23:53 +0200 Subject: [PATCH 27/83] Adds distinct and borrow to the one and only true index. --- doc/manual.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index f163e0d5f..dca4d9243 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1388,7 +1388,7 @@ accesses its environment. If it does so, it has the calling convention Distinct type ------------- -A distinct type is new type derived from a `base type`:idx: that is +A `distinct type`:idx: is new type derived from a `base type`:idx: that is incompatible with its base type. In particular, it is an essential property of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its @@ -1435,7 +1435,7 @@ number without unit; and the same holds for division: This quickly gets tedious. The implementations are trivial and the compiler should not generate all this code only to optimize it away later - after all ``+`` for dollars should produce the same binary code as ``+`` for ints. -The pragma ``borrow`` has been designed to solve this problem; in principle +The pragma `borrow`:idx: has been designed to solve this problem; in principle it generates the above trivial implementations: .. code-block:: nimrod From 19a36c9acb9383c8be8390abb4f05c01abd1956d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 9 Jun 2013 15:34:25 +0200 Subject: [PATCH 28/83] Documents shortcut array syntax. Refs #397. --- doc/tut1.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/tut1.txt b/doc/tut1.txt index 28e23b0f0..0cc9b05c1 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1182,6 +1182,21 @@ type and instead write it embedded directly as the type of the first dimension: type TLightTower = array[1..10, array[north..west, TBlinkLights]] +It is quite frequent to have arrays start at zero, so there's a shortcut syntax +to specify a range from zero to the specified index minus one: + +.. code-block:: nimrod + type + TIntArray = array[0..5, int] # an array that is indexed with 0..5 + TQuickArray = array[6, int] # an array that is indexed with 0..5 + var + x: TIntArray + y: TQuickArray + x = [1, 2, 3, 4, 5, 6] + y = x + for i in low(x)..high(x): + echo(x[i], y[i]) + Sequences --------- From 05c2d9bd735a39408bf77ef2efca1771aa518a97 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 9 Jun 2013 16:10:03 +0200 Subject: [PATCH 29/83] Adds docutils modules to lib index, fixes :idx: markers. --- doc/lib.txt | 16 ++++++++++++++++ lib/packages/docutils/rst.nim | 2 +- lib/packages/docutils/rstast.nim | 2 +- lib/packages/docutils/rstgen.nim | 4 ++-- web/nimrod.ini | 4 +++- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/doc/lib.txt b/doc/lib.txt index 2f781f375..1b004aa9d 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -255,6 +255,22 @@ Parsers This is a low level module that implements an extremely efficient buffering scheme for lexers and parsers. This is used by the diverse parsing modules. +* `highlite `_ + Source highlighter for programming or markup languages. Currently + only few languages are supported, other languages may be added. + The interface supports one language nested in another. + +* `rst `_ + This module implements a reStructuredText parser. A large subset + is implemented. Some features of the markdown wiki syntax are + also supported. + +* `rstast `_ + This module implements an AST for the reStructuredText parser. + +* `rstgen `_ + This module implements a generator of HTML/Latex from reStructuredText. + XML Processing -------------- diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 54c17367f..91664cd50 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module implements a `reStructuredText`:idx parser. A large +## This module implements a `reStructuredText`:idx: parser. A large ## subset is implemented. Some features of the `markdown`:idx: wiki syntax are ## also supported. diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 23233fd39..3d191dacb 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module implements an AST for the `reStructuredText`:idx parser. +## This module implements an AST for the `reStructuredText`:idx: parser. import strutils diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 53bd8188e..a393943fb 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module implements a generator of HTML/Latex from `reStructuredText`:idx. +## This module implements a generator of HTML/Latex from `reStructuredText`:idx:. import strutils, os, hashes, strtabs, rstast, rst, highlite @@ -692,4 +692,4 @@ proc rstToHtml*(s: string, options: TRstParseOptions, var rst = rstParse(s, filen, 0, 1, dummyHasToc, options) result = "" renderRstToOut(d, rst, result) - \ No newline at end of file + diff --git a/web/nimrod.ini b/web/nimrod.ini index 36252deb6..d9c6cb786 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -60,7 +60,9 @@ srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings" srcdoc2: "pure/events;pure/collections/sequtils;pure/irc;pure/cookies" srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits" srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri" -srcdoc2: "pure/nimprof;pure/unittest" +srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" +srcdoc2: "packages/docutils/rst;packages/docutils/rstast" +srcdoc2: "packages/docutils/rstgen" webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup" webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" From 07d19df80f36f3c0639172385b89b1854c8ebcee Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 9 Jun 2013 22:51:27 +0200 Subject: [PATCH 30/83] Wraps variables inside `when` for macosx. Refs #461. --- lib/wrappers/readline/readline.nim | 47 ++++++++++++++++-------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/wrappers/readline/readline.nim b/lib/wrappers/readline/readline.nim index d14171c46..1f0dd564f 100644 --- a/lib/wrappers/readline/readline.nim +++ b/lib/wrappers/readline/readline.nim @@ -156,33 +156,34 @@ const type Thook_func* = proc (a2: cstring): cstring{.cdecl.} -# If non-null, this contains the address of a function that the application -# wants called before trying the standard tilde expansions. The function -# is called with the text sans tilde, and returns a malloc()'ed string -# which is the expansion, or a NULL pointer if the expansion fails. +when not defined(macosx): + # If non-null, this contains the address of a function that the application + # wants called before trying the standard tilde expansions. The function + # is called with the text sans tilde, and returns a malloc()'ed string + # which is the expansion, or a NULL pointer if the expansion fails. -var expansion_preexpansion_hook*{.importc: "tilde_expansion_preexpansion_hook", - dynlib: tildeDll.}: Thook_func + var expansion_preexpansion_hook*{.importc: "tilde_expansion_preexpansion_hook", + dynlib: tildeDll.}: Thook_func -# If non-null, this contains the address of a function to call if the -# standard meaning for expanding a tilde fails. The function is called -# with the text (sans tilde, as in "foo"), and returns a malloc()'ed string -# which is the expansion, or a NULL pointer if there is no expansion. + # If non-null, this contains the address of a function to call if the + # standard meaning for expanding a tilde fails. The function is called + # with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + # which is the expansion, or a NULL pointer if there is no expansion. -var expansion_failure_hook*{.importc: "tilde_expansion_failure_hook", - dynlib: tildeDll.}: Thook_func + var expansion_failure_hook*{.importc: "tilde_expansion_failure_hook", + dynlib: tildeDll.}: Thook_func -# When non-null, this is a NULL terminated array of strings which -# are duplicates for a tilde prefix. Bash uses this to expand -# `=~' and `:~'. + # When non-null, this is a NULL terminated array of strings which + # are duplicates for a tilde prefix. Bash uses this to expand + # `=~' and `:~'. -var additional_prefixes*{.importc: "tilde_additional_prefixes", dynlib: tildeDll.}: cstringArray + var additional_prefixes*{.importc: "tilde_additional_prefixes", dynlib: tildeDll.}: cstringArray -# When non-null, this is a NULL terminated array of strings which match -# the end of a username, instead of just "/". Bash sets this to -# `:' and `=~'. + # When non-null, this is a NULL terminated array of strings which match + # the end of a username, instead of just "/". Bash sets this to + # `:' and `=~'. -var additional_suffixes*{.importc: "tilde_additional_suffixes", dynlib: tildeDll.}: cstringArray + var additional_suffixes*{.importc: "tilde_additional_suffixes", dynlib: tildeDll.}: cstringArray # Return a new string which is the result of tilde expanding STRING. @@ -229,7 +230,8 @@ type # The current undo list for RL_LINE_BUFFER. -var undo_list*{.importc: "rl_undo_list", dynlib: readlineDll.}: ptr TUNDO_LIST +when not defined(macosx): + var undo_list*{.importc: "rl_undo_list", dynlib: readlineDll.}: ptr TUNDO_LIST # The data structure for mapping textual names to code addresses. @@ -239,7 +241,8 @@ type function*: TCommandFunc -var funmap*{.importc: "funmap", dynlib: readlineDll.}: ptr ptr TFUNMAP +when not defined(macosx): + var funmap*{.importc: "funmap", dynlib: readlineDll.}: ptr ptr TFUNMAP # **************************************************************** # From 23ef565a3c2c1f83816fefbeadb0fc59e754997d Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Jun 2013 23:29:43 +0200 Subject: [PATCH 31/83] implemented large parts of the 'not nil' checking --- compiler/ast.nim | 29 ++++++++++++--- compiler/magicsys.nim | 16 +++++++- compiler/msgs.nim | 9 +++-- compiler/parampatterns.nim | 2 + compiler/pragmas.nim | 10 +++-- compiler/semexprs.nim | 25 +++++++++++++ compiler/sempass2.nim | 68 +++++++++++++++++++++++++++++----- compiler/semstmts.nim | 2 +- compiler/semtypes.nim | 67 +++++++++++++++++++-------------- compiler/semtypinst.nim | 2 + compiler/transf.nim | 7 ++-- compiler/trees.nim | 3 +- compiler/wordrecg.nim | 4 +- doc/manual.txt | 70 ++++++++++++++++++++++------------- doc/nimrodc.txt | 2 +- lib/pure/times.nim | 36 +++++++++--------- tests/reject/tuninit1.nim | 36 ++++++++++++++++++ todo.txt | 3 +- tools/niminst/debcreation.nim | 4 +- web/index.txt | 2 +- web/news.txt | 2 +- 21 files changed, 290 insertions(+), 109 deletions(-) create mode 100644 tests/reject/tuninit1.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 6e5a17d99..f4b1b84f5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -353,7 +353,7 @@ type nfSem # node has been checked for semantics TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 19) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) tfVarargs, # procedure has C styled varargs tfNoSideEffect, # procedure type does not allow side effects tfFinal, # is the object final? @@ -380,7 +380,13 @@ type tfByRef, # pass object/tuple by reference (C backend) tfIterator, # type is really an iterator, not a tyProc tfShared, # type is 'shared' - tfNotNil # type cannot be 'nil' + tfNotNil, # type cannot be 'nil' + + tfNeedsInit, # type constains a "not nil" constraint somewhere or some + # other type so that it requires inititalization + tfHasShared, # type constains a "shared" constraint modifier somewhere + tfHasMeta, # type has "typedesc" or "expr" somewhere + tfHasGCedMem, # type contains GC'ed memory TTypeFlags* = set[TTypeFlag] @@ -1168,14 +1174,25 @@ proc newSons(father: PNode, length: int) = else: setlen(father.sons, length) -proc addSon*(father, son: PType) {.deprecated.} = - if isNil(father.sons): father.sons = @[] - add(father.sons, son) - #assert((father.kind != tyGenericInvokation) or (son.kind != tyGenericInst)) +proc propagateToOwner*(owner, elem: PType) = + owner.flags = owner.flags + (elem.flags * {tfNeedsInit, tfHasShared, + tfHasMeta, tfHasGCedMem}) + if tfNotNil in elem.flags: + owner.flags.incl tfNeedsInit + + if tfShared in elem.flags: + owner.flags.incl tfHasShared + + if elem.kind in {tyExpr, tyTypeDesc}: + owner.flags.incl tfHasMeta + elif elem.kind in {tyString, tyRef, tySequence} or + elem.kind == tyProc and elem.callConv == ccClosure: + owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType) = if isNil(father.sons): father.sons = @[] add(father.sons, son) + if not son.isNil: propagateToOwner(father, son) proc addSon(father, son: PNode) = assert son != nil diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 352b6ca04..1972dec98 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -43,6 +43,18 @@ proc getSysSym(name: string): PSym = result.typ = newType(tyError, systemModule) if result.kind == skStub: loadStub(result) +proc getSysMagic*(name: string, m: TMagic): PSym = + var ti: TIdentIter + let id = getIdent(name) + result = InitIdentIter(ti, systemModule.tab, id) + while result != nil: + if result.kind == skStub: loadStub(result) + if result.magic == m: return result + result = NextIdentIter(ti, systemModule.tab) + rawMessage(errSystemNeeds, name) + result = newSym(skError, id, systemModule, systemModule.info) + result.typ = newType(tyError, systemModule) + proc sysTypeFromName*(name: string): PType = result = getSysSym(name).typ @@ -111,7 +123,9 @@ proc skipIntLit*(t: PType): PType {.inline.} = proc AddSonSkipIntLit*(father, son: PType) = if isNil(father.sons): father.sons = @[] - add(father.sons, son.skipIntLit) + let s = son.skipIntLit + add(father.sons, s) + propagateToOwner(father, s) proc setIntLitType*(result: PNode) = let i = result.intVal diff --git a/compiler/msgs.nim b/compiler/msgs.nim index f75aec0d5..302a7cfd8 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -106,7 +106,7 @@ type warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure, - warnEachIdentIsTuple, warnShadowIdent, warnUninit, warnUser, + warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnUninit, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, @@ -355,7 +355,8 @@ const warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]", warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]", warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]", - warnUninit: "read from potentially uninitialized variable: '$1' [Uninit]", + warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]", + warnUninit: "'$1' might not have been initialized [Uninit]", warnUser: "$1 [User]", hintSuccess: "operation successful [Success]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", @@ -375,14 +376,14 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..20, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..21, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", - "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "Uninit", + "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "Uninit", "User"] HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index de7bcaeee..283f83906 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -50,6 +50,8 @@ proc add(code: var TPatternCode, op: TOpcode) {.inline.} = proc whichAlias*(p: PSym): TAliasRequest = if p.constraint != nil: result = TAliasRequest(p.constraint.strVal[0].ord) + else: + result = aqNone proc compileConstraints(p: PNode, result: var TPatternCode) = case p.kind diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index cc432aea8..cecec8e5e 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -50,7 +50,7 @@ const typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow, wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef, - wInheritable, wGenSym, wInject} + wInheritable, wGenSym, wInject, wRequiresInit} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportcpp, wImportobjc, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -253,11 +253,11 @@ proc processNote(c: PContext, n: PNode) = of wHint: var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s) if x >= 0: nk = TNoteKind(x + ord(hintMin)) - else: invalidPragma(n) + else: invalidPragma(n); return of wWarning: var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s) if x >= 0: nk = TNoteKind(x + ord(warnMin)) - else: InvalidPragma(n) + else: InvalidPragma(n); return else: invalidPragma(n) return @@ -695,6 +695,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, noVal(it) if sym.typ == nil: invalidPragma(it) else: incl(sym.typ.flags, tfIncompleteStruct) + of wRequiresInit: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfNeedsInit) of wByRef: noVal(it) if sym == nil or sym.typ == nil: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fba028a46..cdab9b535 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1601,6 +1601,25 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = addSonSkipIntLit(typ, n.sons[i].typ) result.typ = typ +proc checkInitialized(n: PNode, ids: TIntSet, info: TLineInfo) = + case n.kind + of nkRecList: + for i in countup(0, sonsLen(n) - 1): + checkInitialized(n.sons[i], ids, info) + of nkRecCase: + if (n.sons[0].kind != nkSym): InternalError(info, "checkInitialized") + checkInitialized(n.sons[0], ids, info) + when false: + # XXX we cannot check here, as we don't know the branch! + for i in countup(1, sonsLen(n) - 1): + case n.sons[i].kind + of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info) + else: internalError(info, "checkInitialized") + of nkSym: + if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids: + Message(info, errGenerated, "field not initialized: " & n.sym.name.s) + else: internalError(info, "checkInitialized") + proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n.sons[0], nil) result = n @@ -1611,6 +1630,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if t.kind != tyObject: localError(n.info, errGenerated, "object constructor needs an object type") return + var objType = t var ids = initIntSet() for i in 1.. = toCover: tracked.init.add id # else we can't merge proc trackIf(tracked: PEffects, n: PNode) = track(tracked, n.sons[0].sons[0]) + let oldFacts = tracked.guards.len + addFact(tracked.guards, n.sons[0].sons[0]) let oldState = tracked.init.len var inter: TIntersection = @[] + var toCover = 0 track(tracked, n.sons[0].sons[1]) + if not breaksBlock(n.sons[0].sons[1]): inc toCover for i in oldState.. 1: + addFact(tracked.guards, branch.sons[0]) setLen(tracked.init, oldState) for i in 0 .. = toCover: tracked.init.add id # else we can't merge as it is not exhaustive + setLen(tracked.guards, oldFacts) proc trackBlock(tracked: PEffects, n: PNode) = if n.kind in {nkStmtList, nkStmtListExpr}: @@ -377,6 +415,9 @@ proc isTrue(n: PNode): bool = n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or n.kind == nkIntLit and n.intVal != 0 +proc paramType(op: PType, i: int): PType = + if op != nil and i < op.len: result = op.sons[i] + proc track(tracked: PEffects, n: PNode) = case n.kind of nkSym: @@ -404,11 +445,12 @@ proc track(tracked: PEffects, n: PNode) = else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) - for i in 1 .. 0 or n.sons[1].intVal < 0: + incl(result.flags, tfNeedsInit) + elif n.sons[0].floatVal > 0.0 or n.sons[1].floatVal < 0.0: + incl(result.flags, tfNeedsInit) else: LocalError(n.sons[0].info, errRangeExpected) result = newOrPrevType(tyError, prev, c) @@ -386,34 +396,34 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, swap(branch.sons[L-2], branch.sons[L-1]) checkForOverlap(c, t, i, branchIndex) -proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, - father: PNode, rectype: PSym) -proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, - father: PNode, rectype: PSym) = +proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, + father: PNode, rectype: PType) +proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, + father: PNode, rectype: PType) = var a = copyNode(n) checkMinSonsLen(n, 2) semRecordNodeAux(c, n.sons[0], check, pos, a, rectype) - if a.sons[0].kind != nkSym: + if a.sons[0].kind != nkSym: internalError("semRecordCase: discriminant is no symbol") return incl(a.sons[0].sym.flags, sfDiscriminant) var covered: biggestInt = 0 var typ = skipTypes(a.sons[0].Typ, abstractVar-{tyTypeDesc}) - if not isOrdinalType(typ): + if not isOrdinalType(typ): LocalError(n.info, errSelectorMustBeOrdinal) - elif firstOrd(typ) < 0: + elif firstOrd(typ) < 0: LocalError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s) - elif lengthOrd(typ) > 0x00007FFF: + elif lengthOrd(typ) > 0x00007FFF: LocalError(n.info, errLenXinvalid, a.sons[0].sym.name.s) var chckCovered = true - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var b = copyTree(n.sons[i]) addSon(a, b) case n.sons[i].kind - of nkOfBranch: + of nkOfBranch: checkMinSonsLen(b, 2) semCaseBranch(c, a, b, i, covered) - of nkElse: + of nkElse: chckCovered = false checkSonsLen(b, 1) else: illFormedAst(n) @@ -424,7 +434,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, addSon(father, a) proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, - father: PNode, rectype: PSym) = + father: PNode, rectype: PType) = if n == nil: return case n.kind of nkRecWhen: @@ -463,7 +473,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, semRecordCase(c, n, check, pos, father, rectype) of nkNilLit: if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info)) - of nkRecList: + of nkRecList: # attempt to keep the nesting at a sane level: var a = if father.kind == nkRecList: father else: copyNode(n) for i in countup(0, sonsLen(n) - 1): @@ -473,7 +483,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, checkMinSonsLen(n, 3) var length = sonsLen(n) var a: PNode - if father.kind != nkRecList and length >= 4: a = newNodeI(nkRecList, n.info) + if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info) else: a = ast.emptyNode if n.sons[length-1].kind != nkEmpty: localError(n.sons[length-1].info, errInitHereNotAllowed) @@ -483,17 +493,19 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, typ = errorType(c) else: typ = semTypeNode(c, n.sons[length-2], nil) + propagateToOwner(rectype, typ) + let rec = rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) suggestSym(n.sons[i], f) f.typ = typ f.position = pos - if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and + if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and (f.loc.r == nil): f.loc.r = toRope(f.name.s) - f.flags = f.flags + ({sfImportc, sfExportc} * rectype.flags) + f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) inc(pos) - if ContainsOrIncl(check, f.name.id): + if ContainsOrIncl(check, f.name.id): localError(n.sons[i].info, errAttemptToRedefine, f.name.s) if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) @@ -502,20 +514,20 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, else: illFormedAst(n) proc addInheritedFieldsAux(c: PContext, check: var TIntSet, pos: var int, - n: PNode) = + n: PNode) = case n.kind - of nkRecCase: + of nkRecCase: if (n.sons[0].kind != nkSym): InternalError(n.info, "addInheritedFieldsAux") addInheritedFieldsAux(c, check, pos, n.sons[0]) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind - of nkOfBranch, nkElse: + of nkOfBranch, nkElse: addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i])) else: internalError(n.info, "addInheritedFieldsAux(record case branch)") - of nkRecList: - for i in countup(0, sonsLen(n) - 1): + of nkRecList: + for i in countup(0, sonsLen(n) - 1): addInheritedFieldsAux(c, check, pos, n.sons[i]) - of nkSym: + of nkSym: Incl(check, n.sym.name.id) inc(pos) else: InternalError(n.info, "addInheritedFieldsAux()") @@ -553,7 +565,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyObject, prev, c) rawAddSon(result, base) result.n = newNodeI(nkRecList, n.info) - semRecordNodeAux(c, n.sons[2], check, pos, result.n, result.sym) + semRecordNodeAux(c, n.sons[2], check, pos, result.n, result) if n.sons[0].kind != nkEmpty: # dummy symbol for `pragma`: var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c) @@ -853,6 +865,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: result = freshType(result, prev) result.flags.incl(tfNotNil) + result.flags.incl(tfNeedsInit) else: LocalError(n.info, errGenerated, "invalid type") else: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 26341525c..31fbc33e1 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -152,6 +152,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = x = lookupTypeVar(cl, x) if header == nil: header = copyType(t, t.owner, false) header.sons[i] = x + propagateToOwner(header, x) #idTablePut(cl.typeMap, body.sons[i-1], x) if header != nil: # search again after first pass: @@ -170,6 +171,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = var x = replaceTypeVarsT(cl, t.sons[i]) assert x.kind != tyGenericInvokation header.sons[i] = x + propagateToOwner(header, x) idTablePut(cl.typeMap, body.sons[i-1], x) for i in countup(1, sonsLen(t) - 1): diff --git a/compiler/transf.nim b/compiler/transf.nim index 058143cdd..a35669e1d 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -504,8 +504,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode = result.add(elseBranch) elif result.Pnode.lastSon.kind != nkElse and not ( skipTypes(n.sons[0].Typ, abstractVarRange).Kind in - {tyInt..tyInt64, tyChar, tyEnum}): - # fix a stupid code gen bug by normalizing: + {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}): + # fix a stupid code gen bug by normalizing: var elseBranch = newTransNode(nkElse, n.info, 1) elseBranch[0] = newTransNode(nkNilLit, n.info, 0) add(result, elseBranch) @@ -704,7 +704,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}: result = n else: - when useEffectSystem: trackProc(prc, n) + #when useEffectSystem: trackProc(prc, n) var c = openTransf(module, "") result = processTransf(c, n) if prc.kind != skMacro: @@ -713,6 +713,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = if prc.kind == skIterator and prc.typ.callConv == ccClosure: result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) + when useEffectSystem: trackProc(prc, result) proc transformStmt*(module: PSym, n: PNode): PNode = if nfTransf in n.flags: diff --git a/compiler/trees.nim b/compiler/trees.nim index f371cb021..ab5c97a19 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -93,8 +93,7 @@ proc getOpSym*(op: PNode): PSym = proc getMagic*(op: PNode): TMagic = case op.kind - of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix, - nkInfix: + of nkCallKinds: case op.sons[0].Kind of nkSym: result = op.sons[0].sym.magic else: result = mNone diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 36d08c718..2a8a02bd6 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -41,7 +41,7 @@ type wImmediate, wDestructor, wImportCpp, wImportObjC, wImportCompilerProc, - wImportc, wExportc, wIncompleteStruct, + wImportc, wExportc, wIncompleteStruct, wRequiresInit, wAlign, wNodecl, wPure, wSideeffect, wHeader, wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, @@ -122,7 +122,7 @@ const "immediate", "destructor", "importcpp", "importobjc", "importcompilerproc", "importc", "exportc", "incompletestruct", - "align", "nodecl", "pure", "sideeffect", + "requiresinit", "align", "nodecl", "pure", "sideeffect", "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line", "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", diff --git a/doc/manual.txt b/doc/manual.txt index f163e0d5f..7147eb631 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -327,36 +327,36 @@ Numerical constants `Numerical constants`:idx: are of a single type and have the form:: - hexdigit ::= digit | 'A'..'F' | 'a'..'f' - octdigit ::= '0'..'7' - bindigit ::= '0'..'1' - HEX_LIT ::= '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* - DEC_LIT ::= digit ( ['_'] digit )* - OCT_LIT ::= '0o' octdigit ( ['_'] octdigit )* - BIN_LIT ::= '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* + hexdigit = digit | 'A'..'F' | 'a'..'f' + octdigit = '0'..'7' + bindigit = '0'..'1' + HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* + DEC_LIT = digit ( ['_'] digit )* + OCT_LIT = '0o' octdigit ( ['_'] octdigit )* + BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* - INT_LIT ::= HEX_LIT - | DEC_LIT - | OCT_LIT - | BIN_LIT + INT_LIT = HEX_LIT + | DEC_LIT + | OCT_LIT + | BIN_LIT - INT8_LIT ::= INT_LIT ['\''] ('i' | 'I') '8' - INT16_LIT ::= INT_LIT ['\''] ('i' | 'I') '16' - INT32_LIT ::= INT_LIT ['\''] ('i' | 'I') '32' - INT64_LIT ::= INT_LIT ['\''] ('i' | 'I') '64' + INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8' + INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16' + INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' + INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64' - UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U') - UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U') '8' - UINT16_LIT ::= INT_LIT ['\''] ('u' | 'U') '16' - UINT32_LIT ::= INT_LIT ['\''] ('u' | 'U') '32' - UINT64_LIT ::= INT_LIT ['\''] ('u' | 'U') '64' + UINT8_LIT = INT_LIT ['\''] ('u' | 'U') + UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8' + UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' + UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' + UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' - exponent ::= ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* - FLOAT_LIT ::= digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent) - FLOAT32_LIT ::= HEX_LIT '\'' ('f'|'F') '32' - | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32' - FLOAT64_LIT ::= HEX_LIT '\'' ('f'|'F') '64' - | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64' + exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* + FLOAT_LIT = digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent) + FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32' + | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32' + FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64' + | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64' As can be seen in the productions, numerical constants can contain underscores @@ -1818,6 +1818,24 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit proc returnUndefinedValue: int {.noinit.} = nil +The implicit initialization can be also prevented by the `requiresInit`:idx: +type pragma. The compiler requires an explicit initialization then. However +it does a `control flow analysis`:idx: to prove the variable has been +initialized and does not rely on syntactic properties: + +.. code-block:: nimrod + type + TMyObject = object {.requiresInit.} + + proc p() = + # the following is valid: + var x: TMyObject + if someCondition(): + x = a() + else: + x = a() + use x + let statement ------------- diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 7f77a50d2..339cee382 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -20,7 +20,7 @@ on the different supported platforms. It is not a definition of the Nimrod programming language (therefore is the `manual `_). Nimrod is free software; it is licensed under the -`GNU General Public License `_. +`MIT License `_. Compiler Usage diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 2eb8d692b..86609c8e3 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -338,24 +338,24 @@ when not defined(JS): const weekDays: array [0..6, TWeekDay] = [ dSun, dMon, dTue, dWed, dThu, dFri, dSat] - result.second = int(tm.second) - result.minute = int(tm.minute) - result.hour = int(tm.hour) - result.monthday = int(tm.monthday) - result.month = TMonth(tm.month) - result.year = tm.year + 1900'i32 - result.weekday = weekDays[int(tm.weekDay)] - result.yearday = int(tm.yearday) - result.isDST = tm.isDST > 0 - if local: - if result.isDST: - result.tzname = getTzname().DST - else: - result.tzname = getTzname().nonDST - else: - result.tzname = "UTC" - - result.timezone = if local: getTimezone() else: 0 + TTimeInfo(second: int(tm.second), + minute: int(tm.minute), + hour: int(tm.hour), + monthday: int(tm.monthday), + month: TMonth(tm.month), + year: tm.year + 1900'i32, + weekday: weekDays[int(tm.weekDay)], + yearday: int(tm.yearday), + isDST: tm.isDST > 0, + tzname: if local: + if tm.isDST > 0: + getTzname().DST + else: + getTzname().nonDST + else: + "UTC", + timezone: if local: getTimezone() else: 0 + ) proc timeInfoToTM(t: TTimeInfo): structTM = const diff --git a/tests/reject/tuninit1.nim b/tests/reject/tuninit1.nim new file mode 100644 index 000000000..e51f9ce7c --- /dev/null +++ b/tests/reject/tuninit1.nim @@ -0,0 +1,36 @@ +discard """ + errormsg: "'y' might not have been initialized" + line:28 +""" + +import strutils + +{.warning[Uninit]:on.} + +proc p = + var x, y, z: int + if stdin.readLine == "true": + x = 34 + + while false: + y = 999 + break + + while true: + if x == 12: break + y = 9999 + + try: + z = parseInt("1233") + except E_Base: + case x + of 34: z = 123 + of 13: z = 34 + else: z = 8 + else: + y = 3444 + x = 3111 + z = 0 + echo x, y, z + +p() diff --git a/todo.txt b/todo.txt index 2aea8660f..9a3377d1c 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,8 @@ version 0.9.4 ============= - make 'bind' default for templates and introduce 'mixin'; -- implement full 'not nil' checking; range[1..3] needs the same mechanism +- test 'not nil' checking more +- prove field accesses; prove array accesses - special rule for ``[]=`` - ``=`` should be overloadable; requires specialization for ``=``; general lift mechanism in the compiler is already implemented for 'fields' diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim index 1e08e5653..982bfaf3d 100644 --- a/tools/niminst/debcreation.nim +++ b/tools/niminst/debcreation.nim @@ -227,9 +227,9 @@ when isMainModule: #echo(createRules()) - prepDeb("nimrod", "0.8.14", "Dominik Picheta", "morfeusz8@gmail.com", + prepDeb("nimrod", "0.9.2", "Dominik Picheta", "morfeusz8@gmail.com", "The Nimrod compiler", "Compiler for the Nimrod programming language", - @[("bin/nimrod", "gpl2"), ("lib/*", "lgpl")], + @[("bin/nimrod", "MIT"), ("lib/*", "MIT")], @["bin/nimrod"], @["config/*"], @["doc/*"], @["lib/*"], "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)") diff --git a/web/index.txt b/web/index.txt index 260d303c8..e504b65c4 100644 --- a/web/index.txt +++ b/web/index.txt @@ -89,7 +89,7 @@ Nimrod plays nice with others * **The Nimrod Compiler can also generate C++ or Objective C for easier interfacing.** * There are lots of bindings: for example, bindings to GTK2, the Windows API, - the POSIX API, OpenGL, SDL, Cario, Python, Lua, TCL, X11, libzip, PCRE, + the POSIX API, OpenGL, SDL, Cairo, Python, Lua, TCL, X11, libzip, PCRE, libcurl, mySQL and SQLite are included in the standard distribution. * A C to Nimrod conversion utility: New bindings to C libraries are easily generated by ``c2nim``. diff --git a/web/news.txt b/web/news.txt index 4f932bc05..f5737ffa3 100644 --- a/web/news.txt +++ b/web/news.txt @@ -36,7 +36,7 @@ Language Additions - Arrays can now be declared with a single integer literal ``N`` instead of a range; the range is then ``0..N-1``. - +- Added ``requiresInit`` pragma to enforce explicit initialization. 2013-05-20 New website design! From be4fd48ab8456bb9f814dbae1e919e535b87b54f Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Jun 2013 23:51:29 +0200 Subject: [PATCH 32/83] fixes 465 --- compiler/nimrod.ini | 1 - lib/copying.txt | 24 ------------------------ 2 files changed, 25 deletions(-) delete mode 100644 lib/copying.txt diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini index bc2f775b6..49dcd25ba 100644 --- a/compiler/nimrod.ini +++ b/compiler/nimrod.ini @@ -64,7 +64,6 @@ Files: "bin/empty.txt" [Lib] Files: "lib/nimbase.h" -Files: "lib/copying.txt" Files: "lib/*.nim" Files: "lib/*.cfg" diff --git a/lib/copying.txt b/lib/copying.txt deleted file mode 100644 index 0ff6b7d87..000000000 --- a/lib/copying.txt +++ /dev/null @@ -1,24 +0,0 @@ -=============================================================================== -Nimrod -- a Compiler for Nimrod. http://nimrod-code.org/ - -Copyright (C) 2004-2013 Andreas Rumpf. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -[ MIT license: http://www.opensource.org/licenses/mit-license.php ] \ No newline at end of file From 8f97f3180abf23d500027accffe6a1895d1a96ac Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Jun 2013 00:17:10 +0200 Subject: [PATCH 33/83] made tests green --- lib/pure/pegs.nim | 2 +- tests/reject/tdisallowif.nim | 2 +- tests/reject/tnotnil2.nim | 2 +- tests/reject/tuninit1.nim | 2 +- tests/run/tpegs.nim | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index b6877696c..d8fed845a 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -82,7 +82,7 @@ type of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: PNonTerminal - of pkBackRef..pkBackRefIgnoreStyle: index: range[1..MaxSubpatterns] + of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] else: sons: seq[TNode] PNonTerminal* = ref TNonTerminal diff --git a/tests/reject/tdisallowif.nim b/tests/reject/tdisallowif.nim index f7bb7098b..10f54288a 100644 --- a/tests/reject/tdisallowif.nim +++ b/tests/reject/tdisallowif.nim @@ -21,7 +21,7 @@ s[0] = substr(s[0], 0, 2) echo s[0] -if true: +if s[0] != "hi": echo "do it" echo "more branches" else: diff --git a/tests/reject/tnotnil2.nim b/tests/reject/tnotnil2.nim index 755314e30..bd6b8b675 100644 --- a/tests/reject/tnotnil2.nim +++ b/tests/reject/tnotnil2.nim @@ -1,6 +1,6 @@ discard """ errormsg: "cannot prove 'y' is not nil" - line:22 + line:20 """ import strutils diff --git a/tests/reject/tuninit1.nim b/tests/reject/tuninit1.nim index e51f9ce7c..2a994b187 100644 --- a/tests/reject/tuninit1.nim +++ b/tests/reject/tuninit1.nim @@ -1,6 +1,6 @@ discard """ errormsg: "'y' might not have been initialized" - line:28 + line:34 """ import strutils diff --git a/tests/run/tpegs.nim b/tests/run/tpegs.nim index e64cd8fef..efc6a8fa4 100644 --- a/tests/run/tpegs.nim +++ b/tests/run/tpegs.nim @@ -77,7 +77,7 @@ type of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: PNonTerminal - of pkBackRef..pkBackRefIgnoreStyle: index: range[1..MaxSubpatterns] + of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] else: sons: seq[TNode] PNonTerminal* = ref TNonTerminal From d8f349f2019a33fd1b284d863736b5301301ad6d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 10 Jun 2013 22:01:00 +0200 Subject: [PATCH 34/83] Fixes caasdriver for --taintMode:on compilation. --- tests/caasdriver.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index 2de046a11..87360ed34 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -171,7 +171,7 @@ when isMainModule: verbose = false for i in 0..ParamCount() - 1: - let param = paramStr(i + 1) + let param = string(paramStr(i + 1)) case param of "verbose": verbose = true else: filter = param From b7a4a9a6ce0d0a0a844875af946124b45fadb01a Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 10 Jun 2013 22:06:38 +0200 Subject: [PATCH 35/83] Fixes usage of public API caasTestsRunner in tester. --- tests/tester.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/tester.nim b/tests/tester.nim index 8c8e31fe8..e10b89761 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -365,8 +365,9 @@ proc outputJSON(reject, compile, run: TResults) = writeFile(jsonFile, s) proc runCaasTests(r: var TResults) = - for test, output, status in caasTestsRunner(): - r.addResult(test, "", output, if status: reSuccess else: reOutputsDiffer) + for test, output, status, mode in caasTestsRunner(): + r.addResult(test, "", output & "-> " & $mode, + if status: reSuccess else: reOutputsDiffer) proc main() = os.putenv "NIMTEST_NO_COLOR", "1" From c7a4412f8ae0edef821fa5d7e64cb200eb4a9830 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 10 Jun 2013 19:14:36 +0200 Subject: [PATCH 36/83] Adds skMethod example to idetools api test. --- tests/caas/idetools_api.nim | 20 ++++++++++++++++++++ tests/caas/idetools_api.txt | 3 +++ 2 files changed, 23 insertions(+) diff --git a/tests/caas/idetools_api.nim b/tests/caas/idetools_api.nim index 930d26429..6327f4c22 100644 --- a/tests/caas/idetools_api.nim +++ b/tests/caas/idetools_api.nim @@ -21,3 +21,23 @@ type proc adder(a, b: int): int = result = a + b + +type + PExpr = ref object of TObject ## abstract base class for an expression + PLiteral = ref object of PExpr + x: int + PPlusExpr = ref object of PExpr + a, b: PExpr + +# watch out: 'eval' relies on dynamic binding +method eval(e: PExpr): int = + # override this base method + quit "to override!" + +method eval(e: PLiteral): int = e.x +method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b) + +proc newLit(x: int): PLiteral = PLiteral(x: x) +proc newPlus(a, b: PExpr): PPlusExpr = PPlusExpr(a: a, b: b) + +echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) diff --git a/tests/caas/idetools_api.txt b/tests/caas/idetools_api.txt index eaa91cd5f..f2b513481 100644 --- a/tests/caas/idetools_api.txt +++ b/tests/caas/idetools_api.txt @@ -39,3 +39,6 @@ def\tskResult\tidetools_api.adder.result\tint # ProcRun mode will fail the next line, because the type is returned empty. def\tskField\tidetools_api.TPerson.name\tbad_string\t + +> idetools --track:idetools_api.nim,43,7 --def +def\tskMethod\tidetools_api.eval\tproc \(PPlusExpr\): int\t From b0ba4caf64a51af3b345fc22fe1595f34b93e8ed Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 10 Jun 2013 23:06:49 +0200 Subject: [PATCH 37/83] Renames test cases to be more human friendly. --- .gitignore | 4 ++-- tests/caas/issue_416.txt | 14 -------------- ...{issue_416.nim => issue_416_template_shift.nim} | 0 tests/caas/issue_416_template_shift.txt | 14 ++++++++++++++ tests/caas/issue_452.txt | 11 ----------- .../{issue_452.nim => issue_452_export_shift.nim} | 0 tests/caas/issue_452_export_shift.txt | 11 +++++++++++ 7 files changed, 27 insertions(+), 27 deletions(-) delete mode 100644 tests/caas/issue_416.txt rename tests/caas/{issue_416.nim => issue_416_template_shift.nim} (100%) create mode 100644 tests/caas/issue_416_template_shift.txt delete mode 100644 tests/caas/issue_452.txt rename tests/caas/{issue_452.nim => issue_452_export_shift.nim} (100%) create mode 100644 tests/caas/issue_452_export_shift.txt diff --git a/.gitignore b/.gitignore index f05ca2016..dc63ebb4c 100644 --- a/.gitignore +++ b/.gitignore @@ -169,8 +169,8 @@ examples/cross_calculator/android/tags /testresults.json /tests/caas/idetools_api /tests/caas/imported -/tests/caas/issue_416 -/tests/caas/issue_452 +/tests/caas/issue_416_template_shift +/tests/caas/issue_452_export_shift /tests/caas/main /tests/caasdriver /tools/nimgrep diff --git a/tests/caas/issue_416.txt b/tests/caas/issue_416.txt deleted file mode 100644 index 7f81c2b2c..000000000 --- a/tests/caas/issue_416.txt +++ /dev/null @@ -1,14 +0,0 @@ -issue_416.nim -> c -SuccessX -> idetools --track:issue_416.nim,12,28 --def -def\tskType\tsystem.string\tstring -> idetools --track:issue_416.nim,12,35 --def -def\tskLet\tissue_416.failtest.input\tTaintedString - -# The following fail because they seem shifted one colum to the right. -> idetools --track:issue_416.nim,12,16 --def -def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr -> idetools --track:issue_416.nim,12,22 --def -def\tskIterator\tunicode.runes\titerator \(string\): TRune - diff --git a/tests/caas/issue_416.nim b/tests/caas/issue_416_template_shift.nim similarity index 100% rename from tests/caas/issue_416.nim rename to tests/caas/issue_416_template_shift.nim diff --git a/tests/caas/issue_416_template_shift.txt b/tests/caas/issue_416_template_shift.txt new file mode 100644 index 000000000..3a494bd57 --- /dev/null +++ b/tests/caas/issue_416_template_shift.txt @@ -0,0 +1,14 @@ +issue_416_template_shift.nim +> c +SuccessX +> idetools --track:issue_416_template_shift.nim,12,28 --def +def\tskType\tsystem.string\tstring +> idetools --track:issue_416_template_shift.nim,12,35 --def +def\tskLet\tissue_416_template_shift.failtest.input\tTaintedString + +# The following fail because they seem shifted one colum to the right. +> idetools --track:issue_416_template_shift.nim,12,16 --def +def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr +> idetools --track:issue_416_template_shift.nim,12,22 --def +def\tskIterator\tunicode.runes\titerator \(string\): TRune + diff --git a/tests/caas/issue_452.txt b/tests/caas/issue_452.txt deleted file mode 100644 index 5db60516c..000000000 --- a/tests/caas/issue_452.txt +++ /dev/null @@ -1,11 +0,0 @@ -issue_452.nim -> c -SuccessX -> idetools --track:issue_452.nim,2,2 --def -def\tskConst\tissue_452.VERSION_STR1\tstring -> idetools --track:issue_452.nim,3,2 --def -def\tskConst\tissue_452.VERSION_STR2\tstring -> idetools --track:issue_452.nim,7,5 --def -def\tskProc\tissue_452.forward1\tproc \(\): string\t -> idetools --track:issue_452.nim,8,5 --def -def\tskProc\tissue_452.forward2\tproc \(\): string\t diff --git a/tests/caas/issue_452.nim b/tests/caas/issue_452_export_shift.nim similarity index 100% rename from tests/caas/issue_452.nim rename to tests/caas/issue_452_export_shift.nim diff --git a/tests/caas/issue_452_export_shift.txt b/tests/caas/issue_452_export_shift.txt new file mode 100644 index 000000000..02ba053f1 --- /dev/null +++ b/tests/caas/issue_452_export_shift.txt @@ -0,0 +1,11 @@ +issue_452_export_shift.nim +> c +SuccessX +> idetools --track:issue_452_export_shift.nim,2,2 --def +def\tskConst\tissue_452_export_shift.VERSION_STR1\tstring +> idetools --track:issue_452_export_shift.nim,3,2 --def +def\tskConst\tissue_452_export_shift.VERSION_STR2\tstring +> idetools --track:issue_452_export_shift.nim,7,5 --def +def\tskProc\tissue_452_export_shift.forward1\tproc \(\): string\t +> idetools --track:issue_452_export_shift.nim,8,5 --def +def\tskProc\tissue_452_export_shift.forward2\tproc \(\): string\t From fe18117fb28df5ffee28e2e4d720452ddfc89fff Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 10 Jun 2013 23:07:36 +0200 Subject: [PATCH 38/83] Adds idetools testcase for dynamic dispatch. Refs #477. --- .gitignore | 1 + tests/caas/issue_477_dynamic_dispatch.nim | 19 +++++++++++++++++++ tests/caas/issue_477_dynamic_dispatch.txt | 5 +++++ 3 files changed, 25 insertions(+) create mode 100644 tests/caas/issue_477_dynamic_dispatch.nim create mode 100644 tests/caas/issue_477_dynamic_dispatch.txt diff --git a/.gitignore b/.gitignore index dc63ebb4c..eb2e27e9e 100644 --- a/.gitignore +++ b/.gitignore @@ -171,6 +171,7 @@ examples/cross_calculator/android/tags /tests/caas/imported /tests/caas/issue_416_template_shift /tests/caas/issue_452_export_shift +/tests/caas/issue_477_dynamic_dispatch /tests/caas/main /tests/caasdriver /tools/nimgrep diff --git a/tests/caas/issue_477_dynamic_dispatch.nim b/tests/caas/issue_477_dynamic_dispatch.nim new file mode 100644 index 000000000..6e6b21ef0 --- /dev/null +++ b/tests/caas/issue_477_dynamic_dispatch.nim @@ -0,0 +1,19 @@ +type + TThing = object of TObject + TUnit = object of TThing + x: int + +method collide(a, b: TThing) {.inline.} = + quit "to override!" + +method collide(a: TThing, b: TUnit) {.inline.} = + echo "collide1" + +method collide(a: TUnit, b: TThing) {.inline.} = + echo "collide2" + +var + a, b: TUnit + +when isMainModule: + collide(a, b) # output: 2 diff --git a/tests/caas/issue_477_dynamic_dispatch.txt b/tests/caas/issue_477_dynamic_dispatch.txt new file mode 100644 index 000000000..068a9d022 --- /dev/null +++ b/tests/caas/issue_477_dynamic_dispatch.txt @@ -0,0 +1,5 @@ +issue_477_dynamic_dispatch.nim +> c --run +SuccessX +> idetools --track:issue_477_dynamic_dispatch.nim,19,5 --def +def\tskMethod\tissue_477_dynamic_dispatch.collide\tproc \(TUnit, TThing\)\{.inline.\} From c156f2d4938d6e79844a17ecad8c5c50b9c32354 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 11 Jun 2013 00:31:40 +0200 Subject: [PATCH 39/83] next steps for guarded data flow analysis --- compiler/guards.nim | 255 +++++++++++++++++++++++++++----- compiler/msgs.nim | 23 +-- compiler/semexprs.nim | 4 +- compiler/sempass2.nim | 34 +++-- tests/reject/tcheckedfield1.nim | 60 ++++++++ tests/reject/tnotnil1.nim | 5 +- todo.txt | 7 +- 7 files changed, 330 insertions(+), 58 deletions(-) create mode 100644 tests/reject/tcheckedfield1.nim diff --git a/compiler/guards.nim b/compiler/guards.nim index 6d0bf6bc6..e9c9f9ac1 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -27,23 +27,53 @@ const proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} proc isLocation(n: PNode): bool = not n.isValue -#n.kind in {nkSym, nkBracketExpr, nkDerefExpr, nkHiddenDeref, nkDotExpr} proc isLet(n: PNode): bool = if n.kind == nkSym: - # XXX allow skResult, skVar here if not re-bound if n.sym.kind in {skLet, skTemp, skForVar}: result = true elif n.sym.kind == skParam and skipTypes(n.sym.typ, abstractInst).kind != tyVar: result = true -proc isLetLocation(m: PNode): bool = +proc isVar(n: PNode): bool = + n.kind == nkSym and n.sym.kind in {skResult, skVar} and + sfGlobal notin n.sym.flags + +proc isLetLocation(m: PNode, isApprox: bool): bool = + # consider: 'n[].kind' --> we really need to support 1 deref op even if this + # is technically wrong due to aliasing :-( We could introduce "soft" facts + # for this; this would still be very useful for warnings and also nicely + # solves the 'var' problems. For now we fix this by requiring much more + # restrictive expressions for the 'not nil' checking. var n = m + var derefs = 0 while true: case n.kind of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv: n = n.sons[0] + of nkDerefExpr, nkHiddenDeref: + n = n.sons[0] + inc derefs + of nkBracketExpr: + if isConstExpr(n.sons[1]) or isLet(n.sons[1]): + n = n.sons[0] + else: return + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + n = n.sons[1] + else: + break + result = n.isLet and derefs <= ord(isApprox) + if not result and isApprox: + result = isVar(n) + +proc interestingCaseExpr*(m: PNode): bool = + var n = m + while true: + case n.kind + of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv, + nkDerefExpr, nkHiddenDeref: + n = n.sons[0] of nkBracketExpr: if isConstExpr(n.sons[1]) or isLet(n.sons[1]): n = n.sons[0] @@ -62,14 +92,30 @@ proc neg(n: PNode): PNode = result.sons[0] = newSymNode(getSysMagic("not", mNot)) result.sons[1] = n +proc buildIsNil(arg: PNode): PNode = + result = newNodeI(nkCall, arg.info, 2) + result.sons[0] = newSymNode(getSysMagic("isNil", mIsNil)) + result.sons[1] = arg + proc usefulFact(n: PNode): PNode = case n.getMagic - of someEq+someLe+someLt: - if isLetLocation(n.sons[1]) or n.len == 3 and isLetLocation(n.sons[2]): + of someEq: + if skipConv(n.sons[2]).kind == nkNilLit and ( + isLetLocation(n.sons[1], false) or isVar(n.sons[1])): + result = buildIsNil(n.sons[1]) + else: + if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true): + # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' + result = n + of someLe+someLt: + if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' result = n - of someIn, mIsNil: - if isLetLocation(n.sons[1]): + of mIsNil: + if isLetLocation(n.sons[1], false) or isVar(n.sons[1]): + result = n + of someIn: + if isLetLocation(n.sons[1], true): result = n of mAnd: let @@ -102,7 +148,9 @@ proc usefulFact(n: PNode): PNode = result.sons[2] = b.neg else: let a = usefulFact(n.sons[1]) - if a != nil: result = n + if a != nil: + result = copyTree(n) + result.sons[1] = a of mOr: # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything # with that knowledge... @@ -157,10 +205,44 @@ proc sameTree(a, b: PNode): bool = if not sameTree(a.sons[i], b.sons[i]): return result = true +proc hasSubTree(n, x: PNode): bool = + if n.sameTree(x): result = true + else: + for i in 0..safeLen(n)-1: + if hasSubTree(n.sons[i], x): return true + +proc invalidateFacts*(m: var TModel, n: PNode) = + # We are able to guard local vars (as opposed to 'let' variables)! + # 'while p != nil: f(p); p = p.next' + # This is actually quite easy to do: + # Re-assignments (incl. pass to a 'var' param) trigger an invalidation + # of every fact that contains 'v'. + # + # if x < 4: + # if y < 5 + # x = unknown() + # # we invalidate 'x' here but it's known that x >= 4 + # # for the else anyway + # else: + # echo x + # + # The same mechanism could be used for more complex data stored on the heap; + # procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we + # could CSE these expressions then and help C's optimizer. + for i in 0..high(m): + if m[i] != nil and m[i].hasSubTree(n): m[i] = nil + proc valuesUnequal(a, b: PNode): bool = if a.isValue and b.isValue: result = not SameValue(a, b) +proc pred(n: PNode): PNode = + if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(biggestInt): + result = copyNode(n) + dec result.intVal + else: + result = n + type TImplication* = enum impUnknown, impNo, impYes @@ -178,35 +260,85 @@ proc impliesEq(fact, eq: PNode): TImplication = if sameTree(fact.sons[1], eq.sons[val]): result = impYes elif valuesUnequal(fact.sons[1], eq.sons[val]): result = impNo of mInSet: - if sameTree(fact.sons[1], eq.sons[loc]) and isValue(eq.sons[val]): - if inSet(fact.sons[2], eq.sons[val]): result = impYes + # remember: mInSet is 'contains' so the set comes first! + if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]): + if inSet(fact.sons[1], eq.sons[val]): result = impYes else: result = impNo - of mIsNil: - if sameTree(fact.sons[1], eq.sons[loc]): - if eq.sons[val].kind == nkNilLit: - result = impYes of mNot, mOr, mAnd: internalError(eq.info, "impliesEq") - else: nil + else: discard + +proc leImpliesIn(x, c, aSet: PNode): TImplication = + if c.kind in {nkCharLit..nkUInt64Lit}: + # fact: x <= 4; question x in {56}? + # --> true iff every value <= 4 is in the set {56} + # + var value = newIntNode(c.kind, firstOrd(x.typ)) + # don't iterate too often: + if c.intVal - value.intVal < 1000: + var i, pos, neg: int + while value.intVal <= c.intVal: + if inSet(aSet, value): inc pos + else: inc neg + inc i; inc value.intVal + if pos == i: result = impYes + elif neg == i: result = impNo + #echo "result ", result, " ", i, " ", neg, " ", pos + # XXX wrong for the impNo case + +proc geImpliesIn(x, c, aSet: PNode): TImplication = + if c.kind in {nkCharLit..nkUInt64Lit}: + # fact: x >= 4; question x in {56}? + # --> true iff every value >= 4 is in the set {56} + # + var value = newIntNode(c.kind, c.intVal) + let max = lastOrd(x.typ) + # don't iterate too often: + if max - value.intVal < 1000: + var i, pos, neg: int + while value.intVal <= max: + if inSet(aSet, value): inc pos + else: inc neg + inc i; inc value.intVal + if pos == i: result = impYes + elif neg == i: result = impNo + +proc compareSets(a, b: PNode): TImplication = + if equalSets(a, b): result = impYes + elif intersectSets(a, b).len == 0: result = impNo + +proc impliesIn(fact, loc, aSet: PNode): TImplication = + case fact.sons[0].sym.magic + of someEq: + if sameTree(fact.sons[1], loc): + if inSet(aSet, fact.sons[2]): result = impYes + else: result = impNo + elif sameTree(fact.sons[2], loc): + if inSet(aSet, fact.sons[1]): result = impYes + else: result = impNo + of mInSet: + if sameTree(fact.sons[2], loc): + result = compareSets(fact.sons[1], aSet) + of someLe: + if sameTree(fact.sons[1], loc): + result = leImpliesIn(fact.sons[1], fact.sons[2], aSet) + elif sameTree(fact.sons[2], loc): + result = geImpliesIn(fact.sons[2], fact.sons[1], aSet) + of someLt: + if sameTree(fact.sons[1], loc): + result = leImpliesIn(fact.sons[1], fact.sons[2].pred, aSet) + elif sameTree(fact.sons[2], loc): + # 4 < x --> 3 <= x + result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet) + of mNot, mOr, mAnd: internalError(loc.info, "impliesIn") + else: discard proc impliesIsNil(fact, eq: PNode): TImplication = case fact.sons[0].sym.magic - of someEq: - if sameTree(fact.sons[1], eq.sons[1]): - if fact.sons[2].kind == nkNilLit: result = impYes - elif sameTree(fact.sons[2], eq.sons[1]): - if fact.sons[1].kind == nkNilLit: result = impYes of mIsNil: if sameTree(fact.sons[1], eq.sons[1]): result = impYes of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil") - else: nil - -proc pred(n: PNode): PNode = - if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(biggestInt): - result = copyNode(n) - dec result.intVal - else: - result = n + else: discard proc impliesGe(fact, x, c: PNode): TImplication = InternalAssert isLocation(x) @@ -242,7 +374,7 @@ proc impliesGe(fact, x, c: PNode): TImplication = if isValue(fact.sons[1]) and isValue(c): if leValue(c, fact.sons[1]): result = impYes of mNot, mOr, mAnd: internalError(x.info, "impliesGe") - else: nil + else: discard proc impliesLe(fact, x, c: PNode): TImplication = if not isLocation(x): @@ -330,17 +462,70 @@ proc factImplies(fact, prop: PNode, isNegation: bool): TImplication = result = impliesLe(fact, prop.sons[1], prop.sons[2]) of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2]) + of mInSet: + result = impliesIn(fact, prop.sons[2], prop.sons[1]) else: internalError(prop.info, "invalid proposition") proc doesImply*(facts: TModel, prop: PNode): TImplication = assert prop.kind in nkCallKinds for f in facts: - result = f.factImplies(prop, false) - if result != impUnknown: return + # facts can be invalidated, in which case they are 'nil': + if not f.isNil: + result = f.factImplies(prop, false) + if result != impUnknown: return proc impliesNotNil*(facts: TModel, arg: PNode): TImplication = - var x = newNodeI(nkCall, arg.info, 2) - x.sons[0] = newSymNode(getSysMagic("isNil", mIsNil)) - x.sons[1] = arg - result = doesImply(facts, x.neg) + result = doesImply(facts, buildIsNil(arg).neg) + +proc settype(n: PNode): PType = + result = newType(tySet, n.typ.owner) + addSonSkipIntLit(result, n.typ) + +proc buildOf(it, loc: PNode): PNode = + var s = newNodeI(nkCurly, it.info, it.len-1) + s.typ = settype(loc) + for i in 0..it.len-2: s.sons[i] = it.sons[i] + result = newNodeI(nkCall, it.info, 3) + result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) + result.sons[1] = s + result.sons[2] = loc + +proc buildElse(n: PNode): PNode = + var s = newNodeIT(nkCurly, n.info, settype(n.sons[0])) + for i in 1..n.len-2: + let branch = n.sons[i] + assert branch.kind == nkOfBranch + for j in 0..branch.len-2: + s.add(branch.sons[j]) + result = newNodeI(nkCall, n.info, 3) + result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) + result.sons[1] = s + result.sons[2] = n.sons[0] + +proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = + let branch = n.sons[i] + if branch.kind == nkOfBranch: + m.add buildOf(branch, n.sons[0]) + else: + m.add n.buildElse.neg + +proc buildProperFieldCheck(access, check: PNode): PNode = + if check.sons[1].kind == nkCurly: + result = copyTree(check) + if access.kind == nkDotExpr: + var a = copyTree(access) + a.sons[1] = check.sons[2] + result.sons[2] = a + # 'access.kind != nkDotExpr' can happen for object constructors + # which we don't check yet + else: + # it is some 'not' + assert check.getMagic == mNot + result = buildProperFieldCheck(access, check.sons[1]).neg + +proc checkFieldAccess*(m: TModel, n: PNode) = + for i in 1..n.len-1: + let check = buildProperFieldCheck(n.sons[0], n.sons[i]) + if m.doesImply(check) != impYes: + Message(n.info, warnProveField, renderTree(n.sons[0])); break diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 302a7cfd8..e57c41f71 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -106,11 +106,13 @@ type warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure, - warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnUninit, warnUser, - hintSuccess, hintSuccessX, - hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, - hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, - hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, + warnEachIdentIsTuple, warnShadowIdent, + warnProveInit, warnProveField, warnProveIndex, + warnUninit, warnUser, + hintSuccess, hintSuccessX, + hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, + hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, + hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, hintConditionAlwaysTrue, hintPattern, hintUser @@ -356,6 +358,8 @@ const warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]", warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]", warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]", + warnProveField: "cannot prove that field '$1' is accessible [ProveField]", + warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnUninit: "'$1' might not have been initialized [Uninit]", warnUser: "$1 [User]", hintSuccess: "operation successful [Success]", @@ -376,15 +380,15 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..21, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..23, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", - "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "Uninit", - "User"] + "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", + "ProveInit", "ProveField", "ProveIndex", "Uninit", "User"] HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", @@ -516,7 +520,8 @@ proc sourceLine*(i: TLineInfo): PRope var gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - - {warnShadowIdent, warnUninit} + {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex} gErrorCounter*: int = 0 # counts the number of errors gHintCounter*: int = 0 gWarnCounter*: int = 0 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cdab9b535..d418cd1e7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1657,7 +1657,9 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = e = fitNode(c, f.typ, e) # small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have # 3 childen the last being the field check - if check != nil: it.add(check) + if check != nil: + check.sons[0] = it.sons[0] + it.add(check) else: localError(it.info, errUndeclaredFieldX, id.s) it.sons[1] = e diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 151cb9cbc..f5b70ff18 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -315,15 +315,17 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) - if paramType != nil and tfNotNil in paramType.flags and - op != nil and tfNotNil notin op.flags: - case impliesNotNil(tracked.guards, n) - of impUnknown: - Message(n.info, errGenerated, - "cannot prove '$1' is not nil" % n.renderTree) - of impNo: - Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) - of impYes: discard + if paramType != nil: + if tfNotNil in paramType.flags and op != nil and tfNotNil notin op.flags: + case impliesNotNil(tracked.guards, n) + of impUnknown: + Message(n.info, errGenerated, + "cannot prove '$1' is not nil" % n.renderTree) + of impNo: + Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) + of impYes: discard + if skipTypes(paramType, abstractInst).kind == tyVar: + invalidateFacts(tracked.guards, n) proc breaksBlock(n: PNode): bool = case n.kind @@ -341,16 +343,22 @@ proc breaksBlock(n: PNode): bool = proc trackCase(tracked: PEffects, n: PNode) = track(tracked, n.sons[0]) let oldState = tracked.init.len + let oldFacts = tracked.guards.len + let interesting = interestingCaseExpr(n.sons[0]) and warnProveField in gNotes var inter: TIntersection = @[] var toCover = 0 for i in 1.. = toCover: tracked.init.add id # else we can't merge + setLen(tracked.guards, oldFacts) proc trackIf(tracked: PEffects, n: PNode) = track(tracked, n.sons[0].sons[0]) @@ -453,12 +462,16 @@ proc track(tracked: PEffects, n: PNode) = # XXX new(objWithNotNil) is not initialized properly! for i in 0 .. fact: not (x.k <= nkBinary) + +proc p() = + var x: PNode = PNode(k: nkStr, s: "abc") + + let y = x + if not y.isNil: + echo toString(y), " ", toString2(y) + +p() diff --git a/tests/reject/tnotnil1.nim b/tests/reject/tnotnil1.nim index 3535bbd63..222c77376 100644 --- a/tests/reject/tnotnil1.nim +++ b/tests/reject/tnotnil1.nim @@ -1,6 +1,6 @@ discard """ errormsg: "'y' is provably nil" - line:22 + line:25 """ import strutils @@ -15,6 +15,9 @@ proc q(x: pointer not nil) = proc p() = var x: pointer + if not x.isNil: + q(x) + let y = x if not y.isNil: q(y) diff --git a/todo.txt b/todo.txt index 5b9f36dcd..f0e01c6c6 100644 --- a/todo.txt +++ b/todo.txt @@ -1,9 +1,10 @@ version 0.9.4 ============= -- make 'bind' default for templates and introduce 'mixin'; -- test 'not nil' checking more -- prove field accesses; prove array accesses +- make 'bind' default for templates and introduce 'mixin' +- fix tcheckedfield1 +- 'not nil' checking for globals +- prove array accesses - special rule for ``[]=`` - ``=`` should be overloadable; requires specialization for ``=``; general lift mechanism in the compiler is already implemented for 'fields' From 41c51a11d3a9edcc4e5fbc2d3bc3e0e0b357f6ff Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 11 Jun 2013 16:49:04 +0200 Subject: [PATCH 40/83] Adds $TESTNIM and $MODULE as variables to test cases. --- tests/caas/compile-suggest.txt | 2 +- tests/caas/compile-then-def.txt | 4 +- tests/caas/def-def-compile.txt | 4 +- tests/caas/def-then-compile.txt | 2 +- tests/caas/idetools_api.txt | 52 ++++++++++++------------- tests/caas/issue_416_template_shift.txt | 10 ++--- tests/caas/issue_452_export_shift.txt | 16 ++++---- tests/caas/suggest-compile.txt | 2 +- tests/caasdriver.nim | 32 ++++++++++----- 9 files changed, 69 insertions(+), 55 deletions(-) diff --git a/tests/caas/compile-suggest.txt b/tests/caas/compile-suggest.txt index f90fc5aa1..55d3f8733 100644 --- a/tests/caas/compile-suggest.txt +++ b/tests/caas/compile-suggest.txt @@ -2,7 +2,7 @@ main.nim > c SuccessX # Ugh, undocumented trackDirty, how is it supposed to work? Fails in proc mode. -> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest +> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest CaasRun skField\tx CaasRun skField\ty diff --git a/tests/caas/compile-then-def.txt b/tests/caas/compile-then-def.txt index 839ca1532..66ff29346 100644 --- a/tests/caas/compile-then-def.txt +++ b/tests/caas/compile-then-def.txt @@ -1,9 +1,9 @@ main.nim > c SuccessX -> idetools --track:main.nim,5,18 --def +> idetools --track:$TESTNIM,5,18 --def strutils.toUpper ! SuccessX -> idetools --track:main.nim,5,18 --def +> idetools --track:$TESTNIM,5,18 --def strutils.toUpper ! SuccessX diff --git a/tests/caas/def-def-compile.txt b/tests/caas/def-def-compile.txt index 05ea5a1aa..12ba7e993 100644 --- a/tests/caas/def-def-compile.txt +++ b/tests/caas/def-def-compile.txt @@ -1,8 +1,8 @@ main.nim -> idetools --track:main.nim,5,18 --def +> idetools --track:$TESTNIM,5,18 --def strutils.toUpper ! SuccessX -> idetools --track:main.nim,5,18 --def +> idetools --track:$TESTNIM,5,18 --def strutils.toUpper ! SuccessX > c diff --git a/tests/caas/def-then-compile.txt b/tests/caas/def-then-compile.txt index 7e755cff2..e44af7f63 100644 --- a/tests/caas/def-then-compile.txt +++ b/tests/caas/def-then-compile.txt @@ -1,5 +1,5 @@ main.nim -> idetools --track:main.nim,5,18 --def +> idetools --track:$TESTNIM,5,18 --def strutils.toUpper ! SuccessX > c diff --git a/tests/caas/idetools_api.txt b/tests/caas/idetools_api.txt index f2b513481..bc1dc6a5f 100644 --- a/tests/caas/idetools_api.txt +++ b/tests/caas/idetools_api.txt @@ -1,44 +1,44 @@ idetools_api.nim > c SuccessX -> idetools --track:idetools_api.nim,4,11 --def +> idetools --track:$TESTNIM,4,11 --def def\tskType\tsystem.TFile\tTFile -> idetools --track:idetools_api.nim,5,7 --def +> idetools --track:$TESTNIM,5,7 --def def\tskProc\tsystem.Open\tproc \(var TFile, string, TFileMode, int\): bool -> idetools --track:idetools_api.nim,5,21 --def +> idetools --track:$TESTNIM,5,21 --def def\tskProc\tsystem.\&\tproc \(string, string\): string\{.noSideEffect.\} -> idetools --track:idetools_api.nim,5,38 --def +> idetools --track:$TESTNIM,5,38 --def def\tskEnumField\tsystem.TFileMode.fmWrite\tTFileMode -> idetools --track:idetools_api.nim,7,6 --def +> idetools --track:$TESTNIM,7,6 --def def\tskProc\tsystem.Close\tproc \(TFile\) -> idetools --track:idetools_api.nim,12,23 --def +> idetools --track:$TESTNIM,12,23 --def def\tskIterator\tunicode.runes\titerator \(string\): TRune -> idetools --track:idetools_api.nim,12,15 --def +> idetools --track:$TESTNIM,12,15 --def def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr -> idetools --track:idetools_api.nim,15,7 --def +> idetools --track:$TESTNIM,15,7 --def # ProcRun mode will fail the next line, because the type is returned empty. -def\tskConst\tidetools_api.SOME_SEQUENCE\tseq\[int\]\t -> idetools --track:idetools_api.nim,15,23 --def +def\tskConst\t$MODULE.SOME_SEQUENCE\tseq\[int\]\t +> idetools --track:$TESTNIM,15,23 --def def\tskProc\tsystem.@\tproc \(array\[IDX, T\]\): seq\[T\]\{.noSideEffect.\} -> idetools --track:idetools_api.nim,17,3 --def +> idetools --track:$TESTNIM,17,3 --def # ProcRun mode will fail the next line, because the type is returned empty. -def\tskType\tidetools_api.bad_string\tbad_string\t -> idetools --track:idetools_api.nim,11,24 --def -def\tskParam\tidetools_api.test_iterators.filename\tstring -> idetools --track:idetools_api.nim,6,5 --def -def\tskVar\tidetools_api.test_enums.o\tTFile -> idetools --track:idetools_api.nim,12,34 --def -def\tskLet\tidetools_api.test_iterators.input\tTaintedString -> idetools --track:idetools_api.nim,13,35 --def -def\tskForVar\tidetools_api.test_iterators.letter\tTRune -> idetools --track:idetools_api.nim,23,3 --def -def\tskResult\tidetools_api.adder.result\tint -> idetools --track:idetools_api.nim,19,6 --def +def\tskType\t$MODULE.bad_string\tbad_string\t +> idetools --track:$TESTNIM,11,24 --def +def\tskParam\t$MODULE.test_iterators.filename\tstring +> idetools --track:$TESTNIM,6,5 --def +def\tskVar\t$MODULE.test_enums.o\tTFile +> idetools --track:$TESTNIM,12,34 --def +def\tskLet\t$MODULE.test_iterators.input\tTaintedString +> idetools --track:$TESTNIM,13,35 --def +def\tskForVar\t$MODULE.test_iterators.letter\tTRune +> idetools --track:$TESTNIM,23,3 --def +def\tskResult\t$MODULE.adder.result\tint +> idetools --track:$TESTNIM,19,6 --def # ProcRun mode will fail the next line, because the type is returned empty. -def\tskField\tidetools_api.TPerson.name\tbad_string\t +def\tskField\t$MODULE.TPerson.name\tbad_string\t -> idetools --track:idetools_api.nim,43,7 --def -def\tskMethod\tidetools_api.eval\tproc \(PPlusExpr\): int\t +> idetools --track:$TESTNIM,43,7 --def +def\tskMethod\t$MODULE.eval\tproc \(PPlusExpr\): int\t diff --git a/tests/caas/issue_416_template_shift.txt b/tests/caas/issue_416_template_shift.txt index 3a494bd57..951a21612 100644 --- a/tests/caas/issue_416_template_shift.txt +++ b/tests/caas/issue_416_template_shift.txt @@ -1,14 +1,14 @@ issue_416_template_shift.nim > c SuccessX -> idetools --track:issue_416_template_shift.nim,12,28 --def +> idetools --track:$TESTNIM,12,28 --def def\tskType\tsystem.string\tstring -> idetools --track:issue_416_template_shift.nim,12,35 --def -def\tskLet\tissue_416_template_shift.failtest.input\tTaintedString +> idetools --track:$TESTNIM,12,35 --def +def\tskLet\t$MODULE.failtest.input\tTaintedString # The following fail because they seem shifted one colum to the right. -> idetools --track:issue_416_template_shift.nim,12,16 --def +> idetools --track:$TESTNIM,12,16 --def def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr -> idetools --track:issue_416_template_shift.nim,12,22 --def +> idetools --track:$TESTNIM,12,22 --def def\tskIterator\tunicode.runes\titerator \(string\): TRune diff --git a/tests/caas/issue_452_export_shift.txt b/tests/caas/issue_452_export_shift.txt index 02ba053f1..56ba0b74f 100644 --- a/tests/caas/issue_452_export_shift.txt +++ b/tests/caas/issue_452_export_shift.txt @@ -1,11 +1,11 @@ issue_452_export_shift.nim > c SuccessX -> idetools --track:issue_452_export_shift.nim,2,2 --def -def\tskConst\tissue_452_export_shift.VERSION_STR1\tstring -> idetools --track:issue_452_export_shift.nim,3,2 --def -def\tskConst\tissue_452_export_shift.VERSION_STR2\tstring -> idetools --track:issue_452_export_shift.nim,7,5 --def -def\tskProc\tissue_452_export_shift.forward1\tproc \(\): string\t -> idetools --track:issue_452_export_shift.nim,8,5 --def -def\tskProc\tissue_452_export_shift.forward2\tproc \(\): string\t +> idetools --track:$TESTNIM,2,2 --def +def\tskConst\t$MODULE.VERSION_STR1\tstring +> idetools --track:$TESTNIM,3,2 --def +def\tskConst\t$MODULE.VERSION_STR2\tstring +> idetools --track:$TESTNIM,7,5 --def +def\tskProc\t$MODULE.forward1\tproc \(\): string\t +> idetools --track:$TESTNIM,8,5 --def +def\tskProc\t$MODULE.forward2\tproc \(\): string\t diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt index a74d4dc21..45e391548 100644 --- a/tests/caas/suggest-compile.txt +++ b/tests/caas/suggest-compile.txt @@ -1,6 +1,6 @@ main.nim # Ugh, undocumented trackDirty, how is it supposed to work? Fails in proc mode. -> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest +> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest skField\tx skField\ty > c diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index 87360ed34..d4838dc39 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -22,7 +22,12 @@ import osproc, streams, os, strutils, re ## only in that mode. ## ## The rest of the line is treated as a regular expression, so be careful -## escaping metacharacters like parenthesis. +## escaping metacharacters like parenthesis. Before the line is processed as a +## regular expression, some basic variables are searched for and replaced in +## the tests. The variables which will be replaced are: +## +## - $TESTNIM: filename specified in the first line of the script. +## - $MODULE: like $TESTNIM but without extension, useful for expected output. ## ## You can optionally pass parameters at the command line to modify the ## behaviour of the test suite. By default only tests which fail will be echoed @@ -46,26 +51,35 @@ type nim: PProcess # Holds the open process for CaasRun sessions, nil otherwise. mode: TRunMode # Stores the type of run mode the session was started with. lastOutput: string # Preserves the last output, needed for ProcRun mode. - filename: string # Appended to each command starting with '>'. + filename: string # Appended to each command starting with '>'. Also a var. + modname: string # Like filename but without extension. -const modes = [CaasRun, ProcRun] +const + modes = [CaasRun, ProcRun] + filenameReplaceVar = "$TESTNIM" + moduleReplaceVar = "$MODULE" var TesterDir = getAppDir() NimrodBin = TesterDir / "../bin/nimrod" +proc replaceVars(session: var TNimrodSession, text: string): string = + result = text.replace(filenameReplaceVar, session.filename) + result = result.replace(moduleReplaceVar, session.modname) + proc startNimrodSession(project: string, mode: TRunMode): TNimrodSession = - let (dir, name) = project.SplitPath + let (dir, name, ext) = project.splitFile result.mode = mode result.lastOutput = "" - result.filename = name + result.filename = name & ext + result.modname = name if mode == CaasRun: result.nim = startProcess(NimrodBin, workingDir = dir, args = ["serve", "--server.type:stdin", name]) proc doCaasCommand(session: var TNimrodSession, command: string): string = assert session.mode == CaasRun - session.nim.inputStream.write(command & "\n") + session.nim.inputStream.write(session.replaceVars(command) & "\n") session.nim.inputStream.flush result = "" @@ -83,7 +97,7 @@ proc doProcCommand(session: var TNimrodSession, command: string): string = assert session.mode == ProcRun except: result = "FAILED TO EXECUTE: " & command & "\n" & result var - process = startProcess(NimrodBin, args = command.split) + process = startProcess(NimrodBin, args = session.replaceVars(command).split) stream = outputStream(process) line = TaintedString("") @@ -141,9 +155,9 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool = output.writeln line, "\n", s.lastOutput else: var expectMatch = true - var pattern = line + var pattern = s.replaceVars(line) if line.startsWith("!"): - pattern = line.substr(1).strip + pattern = pattern.substr(1).strip expectMatch = false let actualMatch = From 10c441deed6b9ee176f59ef3ee015f99a54c4f46 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 11 Jun 2013 16:15:14 +0200 Subject: [PATCH 41/83] Enables trackDirty query for idetools proc mode. --- tests/caas/compile-suggest.txt | 5 ++--- tests/caas/suggest-compile.txt | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/caas/compile-suggest.txt b/tests/caas/compile-suggest.txt index 55d3f8733..617060347 100644 --- a/tests/caas/compile-suggest.txt +++ b/tests/caas/compile-suggest.txt @@ -1,8 +1,7 @@ main.nim > c SuccessX -# Ugh, undocumented trackDirty, how is it supposed to work? Fails in proc mode. > idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest -CaasRun skField\tx -CaasRun skField\ty +skField\tx +skField\ty diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt index 45e391548..ac6187236 100644 --- a/tests/caas/suggest-compile.txt +++ b/tests/caas/suggest-compile.txt @@ -1,5 +1,4 @@ main.nim -# Ugh, undocumented trackDirty, how is it supposed to work? Fails in proc mode. > idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest skField\tx skField\ty From 28041461b7366433c22acc8257c9a5e8a0bd3b40 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 11 Jun 2013 17:23:48 +0200 Subject: [PATCH 42/83] Adds symbol proc run mode as suggested by Zahary. To avoid collision each test case uses a different nimcache directory based on the input test filename. --- .gitignore | 2 ++ tests/caasdriver.nim | 20 +++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index eb2e27e9e..a52319eff 100644 --- a/.gitignore +++ b/.gitignore @@ -167,6 +167,7 @@ examples/cross_calculator/android/tags /run.json /testresults.html /testresults.json +/tests/caas/SymbolProcRun.*/ /tests/caas/idetools_api /tests/caas/imported /tests/caas/issue_416_template_shift @@ -175,3 +176,4 @@ examples/cross_calculator/android/tags /tests/caas/main /tests/caasdriver /tools/nimgrep +no changes added to commit (use "git add" and/or "git commit -a") diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index d4838dc39..619ce9b6a 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -45,7 +45,7 @@ import osproc, streams, os, strutils, re type TRunMode = enum - ProcRun, CaasRun + ProcRun, CaasRun, SymbolProcRun TNimrodSession* = object nim: PProcess # Holds the open process for CaasRun sessions, nil otherwise. @@ -53,9 +53,10 @@ type lastOutput: string # Preserves the last output, needed for ProcRun mode. filename: string # Appended to each command starting with '>'. Also a var. modname: string # Like filename but without extension. + nimcache: string # Input script based name for the nimcache dir. const - modes = [CaasRun, ProcRun] + modes = [CaasRun, ProcRun, SymbolProcRun] filenameReplaceVar = "$TESTNIM" moduleReplaceVar = "$MODULE" @@ -67,12 +68,14 @@ proc replaceVars(session: var TNimrodSession, text: string): string = result = text.replace(filenameReplaceVar, session.filename) result = result.replace(moduleReplaceVar, session.modname) -proc startNimrodSession(project: string, mode: TRunMode): TNimrodSession = +proc startNimrodSession(project, script: string, mode: TRunMode): + TNimrodSession = let (dir, name, ext) = project.splitFile result.mode = mode result.lastOutput = "" result.filename = name & ext result.modname = name + result.nimcache = "SymbolProcRun." & script.splitFile.name if mode == CaasRun: result.nim = startProcess(NimrodBin, workingDir = dir, args = ["serve", "--server.type:stdin", name]) @@ -94,7 +97,7 @@ proc doCaasCommand(session: var TNimrodSession, command: string): string = break proc doProcCommand(session: var TNimrodSession, command: string): string = - assert session.mode == ProcRun + assert session.mode == ProcRun or session.mode == SymbolProcRun except: result = "FAILED TO EXECUTE: " & command & "\n" & result var process = startProcess(NimrodBin, args = session.replaceVars(command).split) @@ -113,6 +116,13 @@ proc doCommand(session: var TNimrodSession, command: string) = session.lastOutput = doCaasCommand(session, command & " " & session.filename) else: + var command = command + # For symbol runs we prepend the necessary parameters to avoid clobbering + # the normal nimcache. + if session.mode == SymbolProcRun: + command = "--symbolFiles:on --nimcache:" & session.nimcache & + " " & command + echo "Running ", command session.lastOutput = doProcCommand(session, command & " " & session.filename) @@ -128,7 +138,7 @@ proc doScenario(script: string, output: PStream, mode: TRunMode): bool = if f.readLine(project): var - s = startNimrodSession(script.parentDir / project.string, mode) + s = startNimrodSession(script.parentDir / project.string, script, mode) tline = TaintedString("") ln = 1 From b4ea608b65ea7637f29cd65827ab46e44bc08017 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 11 Jun 2013 22:34:09 +0200 Subject: [PATCH 43/83] Removes the nimcache symbol directory for each run. --- tests/caasdriver.nim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index 619ce9b6a..cd27839c7 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -75,7 +75,13 @@ proc startNimrodSession(project, script: string, mode: TRunMode): result.lastOutput = "" result.filename = name & ext result.modname = name - result.nimcache = "SymbolProcRun." & script.splitFile.name + + let (nimcacheDir, nimcacheName, nimcacheExt) = script.splitFile + result.nimcache = "SymbolProcRun." & nimcacheName + + if mode == SymbolProcRun: + removeDir(nimcacheDir / result.nimcache) + if mode == CaasRun: result.nim = startProcess(NimrodBin, workingDir = dir, args = ["serve", "--server.type:stdin", name]) @@ -122,7 +128,6 @@ proc doCommand(session: var TNimrodSession, command: string) = if session.mode == SymbolProcRun: command = "--symbolFiles:on --nimcache:" & session.nimcache & " " & command - echo "Running ", command session.lastOutput = doProcCommand(session, command & " " & session.filename) From 009730595303d6da2911fb16004b51388628f0cc Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 12 Jun 2013 00:41:02 +0200 Subject: [PATCH 44/83] bugfixes for the guarded data flow analysis --- compiler/guards.nim | 161 +++++++++++++++++++------------- compiler/nimsets.nim | 6 ++ compiler/sempass2.nim | 2 - tests/reject/tcheckedfield1.nim | 4 +- todo.txt | 1 - 5 files changed, 103 insertions(+), 71 deletions(-) diff --git a/compiler/guards.nim b/compiler/guards.nim index e9c9f9ac1..d4cb34f36 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -38,7 +38,7 @@ proc isLet(n: PNode): bool = proc isVar(n: PNode): bool = n.kind == nkSym and n.sym.kind in {skResult, skVar} and - sfGlobal notin n.sym.flags + {sfGlobal, sfAddrTaken} * n.sym.flags == {} proc isLetLocation(m: PNode, isApprox: bool): bool = # consider: 'n[].kind' --> we really need to support 1 deref op even if this @@ -67,27 +67,58 @@ proc isLetLocation(m: PNode, isApprox: bool): bool = if not result and isApprox: result = isVar(n) -proc interestingCaseExpr*(m: PNode): bool = - var n = m - while true: - case n.kind - of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv, - nkDerefExpr, nkHiddenDeref: - n = n.sons[0] - of nkBracketExpr: - if isConstExpr(n.sons[1]) or isLet(n.sons[1]): - n = n.sons[0] - else: return - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - n = n.sons[1] - else: - break - result = n.isLet +proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) + +proc swapArgs(fact: PNode, newOp: string, m: TMagic): PNode = + result = newNodeI(nkCall, fact.info, 3) + result.sons[0] = newSymNode(getSysMagic(newOp, m)) + result.sons[1] = fact.sons[2] + result.sons[2] = fact.sons[1] proc neg(n: PNode): PNode = - if n.getMagic == mNot: + if n == nil: return nil + case n.getMagic + of mNot: result = n.sons[1] + of someLt: + # not (a < b) == a >= b == b <= a + result = swapArgs(n, "<=", mLeI) + of someLe: + result = swapArgs(n, "<", mLtI) + of mInSet: + if n.sons[1].kind != nkCurly: return nil + let t = n.sons[2].typ.skipTypes(abstractInst) + result = newNodeI(nkCall, n.info, 3) + result.sons[0] = n.sons[0] + result.sons[2] = n.sons[2] + if t.kind == tyEnum: + var s = newNodeIT(nkCurly, n.info, n.sons[1].typ) + for e in t.n: + let eAsNode = newIntNode(nkIntLit, e.sym.position) + if not inSet(n.sons[1], eAsNode): s.add eAsNode + result.sons[1] = s + elif lengthOrd(t) < 1000: + result.sons[1] = complement(n.sons[1]) + else: + # not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4 + # XXX todo + result = nil + of mOr: + # not (a or b) --> not a and not b + let + a = n.sons[1].neg + b = n.sons[2].neg + if a != nil and b != nil: + result = newNodeI(nkCall, n.info, 3) + result.sons[0] = newSymNode(getSysMagic("and", mAnd)) + result.sons[1] = a + result.sons[2] = b + elif a != nil: + result = a + elif b != nil: + result = b else: + # leave not (a == 4) as it is result = newNodeI(nkCall, n.info, 2) result.sons[0] = newSymNode(getSysMagic("not", mNot)) result.sons[1] = n @@ -131,26 +162,9 @@ proc usefulFact(n: PNode): PNode = elif b != nil: result = b of mNot: - case n.sons[1].getMagic - of mNot: - # normalize 'not (not a)' into 'a': - result = usefulFact(n.sons[1].sons[1]) - of mOr: - # not (a or b) --> not a and not b - let n = n.sons[1] - let - a = usefulFact(n.sons[1]) - b = usefulFact(n.sons[2]) - if a != nil and b != nil: - result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(getSysMagic("and", mAnd)) - result.sons[1] = a.neg - result.sons[2] = b.neg - else: - let a = usefulFact(n.sons[1]) - if a != nil: - result = copyTree(n) - result.sons[1] = a + let a = usefulFact(n.sons[1]) + if a != nil: + result = a.neg of mOr: # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything # with that knowledge... @@ -159,13 +173,13 @@ proc usefulFact(n: PNode): PNode = # (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2)) # not (x != 3 and y != 2) let - a = usefulFact(n.sons[1]) - b = usefulFact(n.sons[2]) + a = usefulFact(n.sons[1]).neg + b = usefulFact(n.sons[2]).neg if a != nil and b != nil: result = newNodeI(nkCall, n.info, 3) result.sons[0] = newSymNode(getSysMagic("and", mAnd)) - result.sons[1] = a.neg - result.sons[2] = b.neg + result.sons[1] = a + result.sons[2] = b result = result.neg elif n.kind == nkSym and n.sym.kind == skLet: # consider: @@ -184,7 +198,9 @@ proc addFact*(m: var TModel, n: PNode) = let n = usefulFact(n) if n != nil: m.add n -proc addFactNeg*(m: var TModel, n: PNode) = addFact(m, n.neg) +proc addFactNeg*(m: var TModel, n: PNode) = + let n = n.neg + if n != nil: addFact(m, n) proc sameTree(a, b: PNode): bool = result = false @@ -270,7 +286,7 @@ proc impliesEq(fact, eq: PNode): TImplication = proc leImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: # fact: x <= 4; question x in {56}? - # --> true iff every value <= 4 is in the set {56} + # --> true if every value <= 4 is in the set {56} # var value = newIntNode(c.kind, firstOrd(x.typ)) # don't iterate too often: @@ -282,8 +298,6 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication = inc i; inc value.intVal if pos == i: result = impYes elif neg == i: result = impNo - #echo "result ", result, " ", i, " ", neg, " ", pos - # XXX wrong for the impNo case proc geImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: @@ -427,33 +441,48 @@ proc impliesLt(fact, x, c: PNode): TImplication = if q != x: result = impliesLe(fact, q, c) -proc factImplies(fact, prop: PNode, isNegation: bool): TImplication = +proc `~`(x: TImplication): TImplication = + case x + of impUnknown: impUnknown + of impNo: impYes + of impYes: impNo + +proc factImplies(fact, prop: PNode): TImplication = case fact.getMagic of mNot: - case factImplies(fact.sons[1], prop, not isNegation) - of impUnknown: return impUnknown - of impNo: return impYes - of impYes: return impNo - of mAnd: - if not isNegation: - result = factImplies(fact.sons[1], prop, isNegation) - if result != impUnknown: return result - return factImplies(fact.sons[2], prop, isNegation) - else: - # careful! not (a and b) means not a or not b: + # Consider: + # enum nkBinary, nkTernary, nkStr + # fact: not (k <= nkBinary) + # question: k in {nkStr} + # --> 'not' for facts is entirely different than 'not' for questions! + # it's provably wrong if every value > 4 is in the set {56} + # That's because we compute the implication and 'a -> not b' cannot + # be treated the same as 'not a -> b' + + # (not a) -> b compute as not (a -> b) ??? + # == not a or not b == not (a and b) + let arg = fact.sons[1] + case arg.getMagic + of mIsNil: + return ~factImplies(arg, prop) + of mAnd: + # not (a and b) means not a or not b: # a or b --> both need to imply 'prop' - let a = factImplies(fact.sons[1], prop, isNegation) - let b = factImplies(fact.sons[2], prop, isNegation) - if a == b: return a + let a = factImplies(arg.sons[1], prop) + let b = factImplies(arg.sons[2], prop) + if a == b: return ~a return impUnknown + else: + InternalError(fact.info, "invalid fact") + of mAnd: + result = factImplies(fact.sons[1], prop) + if result != impUnknown: return result + return factImplies(fact.sons[2], prop) else: discard case prop.sons[0].sym.magic of mNot: - case fact.factImplies(prop.sons[1], isNegation) - of impUnknown: result = impUnknown - of impNo: result = impYes - of impYes: result = impNo + result = ~fact.factImplies(prop.sons[1]) of mIsNil: result = impliesIsNil(fact, prop) of someEq: @@ -472,7 +501,7 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication = for f in facts: # facts can be invalidated, in which case they are 'nil': if not f.isNil: - result = f.factImplies(prop, false) + result = f.factImplies(prop) if result != impUnknown: return proc impliesNotNil*(facts: TModel, arg: PNode): TImplication = diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 93bccae24..f874ccdca 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -156,6 +156,12 @@ proc equalSets(a, b: PNode): bool = toBitSet(b, y) result = bitSetEquals(x, y) +proc complement*(a: PNode): PNode = + var x: TBitSet + toBitSet(a, x) + for i in countup(0, high(x)): x[i] = not x[i] + result = toTreeSet(x, a.typ, a.info) + proc cardSet(s: PNode): BiggestInt = # here we can do better than converting it into a compact set # we just count the elements directly diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f5b70ff18..50286d399 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -324,8 +324,6 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = of impNo: Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) of impYes: discard - if skipTypes(paramType, abstractInst).kind == tyVar: - invalidateFacts(tracked.guards, n) proc breaksBlock(n: PNode): bool = case n.kind diff --git a/tests/reject/tcheckedfield1.nim b/tests/reject/tcheckedfield1.nim index f74a8b76d..5ade3a13a 100644 --- a/tests/reject/tcheckedfield1.nim +++ b/tests/reject/tcheckedfield1.nim @@ -1,6 +1,6 @@ discard """ - errormsg: "cannot prove that field 's' is accessible" - line:54 + errormsg: "cannot prove that field 'x.s' is accessible" + line:51 """ import strutils diff --git a/todo.txt b/todo.txt index f0e01c6c6..83af59b87 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,6 @@ version 0.9.4 ============= - make 'bind' default for templates and introduce 'mixin' -- fix tcheckedfield1 - 'not nil' checking for globals - prove array accesses - special rule for ``[]=`` From d4c91bbd8546b4e4a2a986f0047e9c7935ef2302 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 13 Jun 2013 01:40:11 +0200 Subject: [PATCH 45/83] improvements for 'not nil' checking --- compiler/ast.nim | 14 ++++- compiler/guards.nim | 7 +++ compiler/sempass2.nim | 57 +++++++++++++------ compiler/semstmts.nim | 8 +++ compiler/semtypes.nim | 1 - compiler/sigmatch.nim | 31 ++++++++-- .../manyloc/keineschweine/lib/sg_packets.nim | 6 +- todo.txt | 1 - web/news.txt | 1 + 9 files changed, 99 insertions(+), 27 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index f4b1b84f5..e4f7f6d98 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1175,10 +1175,18 @@ proc newSons(father: PNode, length: int) = setlen(father.sons, length) proc propagateToOwner*(owner, elem: PType) = - owner.flags = owner.flags + (elem.flags * {tfNeedsInit, tfHasShared, - tfHasMeta, tfHasGCedMem}) + const HaveTheirOwnEmpty = {tySequence, tySet} + owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta, + tfHasGCedMem}) if tfNotNil in elem.flags: - owner.flags.incl tfNeedsInit + if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}: + owner.flags.incl tfNotNil + elif owner.kind notin HaveTheirOwnEmpty: + owner.flags.incl tfNeedsInit + + if tfNeedsInit in elem.flags: + if owner.kind in HaveTheirOwnEmpty: nil + else: owner.flags.incl tfNeedsInit if tfShared in elem.flags: owner.flags.incl tfHasShared diff --git a/compiler/guards.nim b/compiler/guards.nim index d4cb34f36..aece63b19 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -532,6 +532,13 @@ proc buildElse(n: PNode): PNode = result.sons[1] = s result.sons[2] = n.sons[0] +proc addDiscriminantFact*(m: var TModel, n: PNode) = + var fact = newNodeI(nkCall, n.info, 3) + fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) + fact.sons[1] = n.sons[0] + fact.sons[2] = n.sons[1] + m.add fact + proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = let branch = n.sons[i] if branch.kind == nkOfBranch: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 50286d399..2c87c3b2c 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -74,7 +74,6 @@ type bottom: int owner: PSym init: seq[int] # list of initialized variables - # coming soon: "guard" tracking for 'let' variables guards: TModel # nested guards PEffects = var TEffects @@ -89,11 +88,19 @@ proc initVar(a: PEffects, n: PNode) = if x == s.id: return a.init.add s.id +proc initVarViaNew(a: PEffects, n: PNode) = + if n.kind != nkSym: return + let s = n.sym + if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}: + # 'x' is not nil, but that doesn't mean it's not nil children + # are initialized: + initVarViaNew(a, n) + proc useVar(a: PEffects, n: PNode) = let s = n.sym if isLocalVar(a, s): if s.id notin a.init: - if tfNeedsInit in s.typ.flags: + if {tfNeedsInit, tfNotNil} * s.typ.flags != {}: when true: Message(n.info, warnProveInit, s.name.s) else: @@ -298,6 +305,18 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let tagSpec = effectSpec(pragma, wTags) mergeTags(tracked, tagSpec, n) +proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = + let n = n.skipConv + if paramType != nil and tfNotNil in paramType.flags and + n.typ != nil and tfNotNil notin n.typ.flags: + case impliesNotNil(tracked.guards, n) + of impUnknown: + Message(n.info, errGenerated, + "cannot prove '$1' is not nil" % n.renderTree) + of impNo: + Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) + of impYes: discard + proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = let op = n.typ if op != nil and op.kind == tyProc and n.kind != nkNilLit: @@ -315,15 +334,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) - if paramType != nil: - if tfNotNil in paramType.flags and op != nil and tfNotNil notin op.flags: - case impliesNotNil(tracked.guards, n) - of impUnknown: - Message(n.info, errGenerated, - "cannot prove '$1' is not nil" % n.renderTree) - of impNo: - Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) - of impYes: discard + notNilCheck(tracked, n, paramType) proc breaksBlock(n: PNode): bool = case n.kind @@ -456,8 +467,7 @@ proc track(tracked: PEffects, n: PNode) = if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq, mShallowCopy}: # may not look like an assignment, but it is: - initVar(tracked, n.sons[1]) - # XXX new(objWithNotNil) is not initialized properly! + initVarViaNew(tracked, n.sons[1]) for i in 0 .. Date: Thu, 13 Jun 2013 15:19:55 +0200 Subject: [PATCH 46/83] fixes #481 --- compiler/guards.nim | 19 ++++++++++++++++++- compiler/sempass2.nim | 9 +++++++++ tests/reject/tnotnil1.nim | 15 ++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/compiler/guards.nim b/compiler/guards.nim index aece63b19..f02a53684 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -345,12 +345,22 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication = result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet) of mNot, mOr, mAnd: internalError(loc.info, "impliesIn") else: discard - + +proc valueIsNil(n: PNode): TImplication = + if n.kind == nkNilLit: impYes + elif n.kind in {nkStrLit..nkTripleStrLit, nkBracket, nkObjConstr}: impNo + else: impUnknown + proc impliesIsNil(fact, eq: PNode): TImplication = case fact.sons[0].sym.magic of mIsNil: if sameTree(fact.sons[1], eq.sons[1]): result = impYes + of someEq: + if sameTree(fact.sons[1], eq.sons[1]): + result = valueIsNil(fact.sons[2].skipConv) + elif sameTree(fact.sons[2], eq.sons[1]): + result = valueIsNil(fact.sons[1].skipConv) of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil") else: discard @@ -539,6 +549,13 @@ proc addDiscriminantFact*(m: var TModel, n: PNode) = fact.sons[2] = n.sons[1] m.add fact +proc addAsgnFact*(m: var TModel, key, value: PNode) = + var fact = newNodeI(nkCall, key.info, 3) + fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) + fact.sons[1] = key + fact.sons[2] = value + m.add fact + proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = let branch = n.sons[i] if branch.kind == nkOfBranch: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 2c87c3b2c..1fc74d31c 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -309,6 +309,12 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv if paramType != nil and tfNotNil in paramType.flags and n.typ != nil and tfNotNil notin n.typ.flags: + if n.kind == nkAddr: + # addr(x[]) can't be proven, but addr(x) can: + if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return + elif n.kind == nkSym and n.sym.kind in RoutineKinds: + # 'p' is not nil obviously: + return case impliesNotNil(tracked.guards, n) of impUnknown: Message(n.info, errGenerated, @@ -481,6 +487,7 @@ proc track(tracked: PEffects, n: PNode) = initVar(tracked, n.sons[0]) invalidateFacts(tracked.guards, n.sons[0]) track(tracked, n.sons[0]) + addAsgnFact(tracked.guards, n.sons[0], n.sons[1]) notNilCheck(tracked, n.sons[1], n.sons[0].typ) of nkVarSection: for child in n: @@ -489,6 +496,7 @@ proc track(tracked: PEffects, n: PNode) = track(tracked, last) for i in 0 .. child.len-3: initVar(tracked, child.sons[i]) + addAsgnFact(tracked.guards, child.sons[i], last) notNilCheck(tracked, last, child.sons[i].typ) # since 'var (a, b): T = ()' is not even allowed, there is always type # inference for (a, b) and thus no nil checking is necessary. @@ -523,6 +531,7 @@ proc track(tracked: PEffects, n: PNode) = if sfDiscriminant in x.sons[0].sym.flags: addDiscriminantFact(tracked.guards, x) setLen(tracked.guards, oldFacts) + of nkTypeSection: discard else: for i in 0 .. Date: Wed, 12 Jun 2013 23:34:29 +0200 Subject: [PATCH 47/83] Lists available idetools caas switches. --- doc/advopt.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/advopt.txt b/doc/advopt.txt index 3b6fafd0f..fc3e1e4e6 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -19,6 +19,10 @@ Advanced commands: --context list possible invokation context --usages list all usages of the symbol at position --eval evaluates an expression + //serve start the compiler as a service mode (CAAS) + --server.type:TYPE either stdin or tcp + --server.port:PORT port for tcp mode, by default 6000 + --server.address:HOST binds to that address, by default "" Advanced options: -m, --mainmodule:FILE set the project main module From d18d6ba019a512d579592b476aa8fec844879267 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 11 Jun 2013 22:14:31 +0200 Subject: [PATCH 48/83] Starts idetools specific documentation. --- doc/idetools.txt | 467 +++++++++++++++++++++++++++++++++++++++++++++++ doc/nimrodc.txt | 8 + web/nimrod.ini | 2 +- 3 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 doc/idetools.txt diff --git a/doc/idetools.txt b/doc/idetools.txt new file mode 100644 index 000000000..0ae29e824 --- /dev/null +++ b/doc/idetools.txt @@ -0,0 +1,467 @@ +================================ + Nimrod IDE Integration Guide +================================ + +:Author: Britney Spears +:Version: |nimrodversion| + +.. contents:: + + +Nimrod differs from many other compilers in that it is really fast, +and being so fast makes it suited to provide external queries for +text editors about the source code being written. Through the +``idetools`` command of `the compiler `_, any IDE can +query a ``.nim`` source file and obtain useful information like +definition of symbols or suggestions for completion. + +This document will guide you through the available options. If you +want to look at practical examples of idetools support you can look +at the test files found in ``tests/caas/*.txt`` or `various editor +integrations `_ +already available. + + +Idetools invokation +=================== + +Specifying the location of the query +------------------------------------ + +All of the available idetools commands require you to specify a +query location through the ``--track`` or ``--trackDirty`` switches. +The general idetools invokations are:: + + nimrod idetools --track:FILE,LINE,COL proj.nim + +Or:: + + nimrod idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL proj.nim + +``proj.nim`` + This is the main *project* filename. Most of the time you will + pass in the same as **FILE**, but for bigger projects this is + the file which is used as main entry point for the program, the + one which users compile to generate a final binary. + +```` + This would be any of the other idetools available options, like + ``--def`` or ``--suggest`` explained in the following sections. + +``COL`` + An integer with the column you are going to query. For the + compiler columns start at zero, so the first column will be + **0** and the last in an 80 column terminal will be **79**. + +``LINE`` + An integer with the line you are going to query. For the compiler + lines start at **1**. + +``FILE`` + The file you want to perform the query on. Usually you will + pass in the same value as **proj.nim**. + +``DIRTY_FILE`` + The **FILE** paramater is enough for static analysis, but IDEs + tend to have *unsaved buffers* where the user may still be in + the middle of typing a line. In such situations the IDE can + save the current contents to a temporary file and then use the + ``--trackDirty`` switch. + + Dirty files are likely to contain errors and they are usually + compiled partially only to the point needed to service the + idetool request. The compiler discriminates them to ensure that + **a)** they won't be cached and **b)** they won't invalidate + the cached contents of the original module. + + The other reason is that the dirty file can appear anywhere on + disk (e.g. in tmpfs), but it must be treated as having a path + matching the original module when it comes to usage of relative + paths, etc. Queries, however, will refer to the dirty module + name in their answers instead of the normal filename. + + +Definitions +----------- + +The ``--def`` idetools switch performs a query about the definition +of a specific symbol. If available, idetools will answer with the +type, source file, line/column information and other accessory data +if available like a docstring. With this information an IDE can +provide the typical *Jump to definition* where a user puts the +cursor on a symbol or uses the mouse to select it and is redirected +to the place where the symbol is located. + +Since Nimrod is implemented in Nimrod, one of the nice things of +this feature is that any user with an IDE supporting it can quickly +jump around the standard library implementation and see exactly +what a proc does, learning about the language and seeing real life +examples of how to write/implement specific features. + +Idetools will always answer with a single definition or none if it +can't find any valid symbol matching the position of the query. + + +Suggestions +----------- + +The ``--suggest`` idetools switch performs a query about possible +completion symbols at some point in the file. IDEs can easily provide +an autocompletion feature where the IDE scans the current file (and +related ones, if it knows about the language being edited and follows +includes/imports) and when the user starts typing something a +completion box with different options appears. + +However such features are not context sensitive and work simply on +string matching, which can be problematic in Nimrod especially due +to the case insensitiveness of the language (plus underscores as +separators!). + +The typical usage scenario for this option is to call it after the +user has typed the dot character for `the object oriented call +syntax `_. + + +Invokation context +------------------ + +The ``--context`` idetools switch is very similar to the suggestions +switch, but instead of being used after the user has typed a dot +character, this one is meant to be used after the user has typed +an opening brace to start typing parameters. + + +Symbol usages +------------- + +The ``--usages`` idetools switch lists all usages of the symbol at +a position. IDEs can use this to find all the places in the file +where the symbol is used and offer the user to rename it in all +places at the same time. Again, a pure string based search and +replace may catch symbols out of the scope of a funcion/loop. + +For this kind of query the IDE will most likely ignore all the +type/signature info provided by idetools and concentrate on the +filename, line and column position of the multiple returned answers. + + +Expression evaluation +--------------------- + +This feature is still under development. In the future it will allow +an IDE to evaluate an expression in the context of the currently +running/debugged user project. + + +Compiler as a service (CAAS) +============================ + +The ocasional use of idetools is acceptable for things like +definitions, where the user puts the cursor on a symbol or double +clicks it and after a second or two the IDE displays where that +symbol is defined. Such latencies would be terrible for features +like symbol suggestion, plus why wait at all if we can avoid it? + +The idetools command can be run as a compiler service, where you +first launch the compiler and it will stay online as a server, +accepting queries in a telnet like fashion. The advantage of staying +on is that for many queries the compiler can cache the results of +the compilation, and subsequent queries should be fast in the +millisecond range, thus being responsive enough for IDEs. + +If you want to start the server using stdin/stdout as communication +you need to type:: + + nimrod serve --server.type:stdin proj.nim + +If you want to start the server using tcp and a port, you need to type:: + + nimrod serve --server.type:tcp --server.port:6000 \ + --server.address:hostname proj.nim + +In both cases the server will start up and await further commands. +The syntax of the commands you can now send to the server is +practically the same as running the nimrod compiler on the commandline, +you only need to remove the name of the compiler since you are +already talking to it. The server will answer with as many lines +of text it thinks necessary plus an empty line to indicate the end +of the answer. + +You can find examples of client/server communication in the idetools +tests found in ``tests/caas/*.txt``. + + +Parsing idetools output +======================= + +Idetools outputs is always returned on single lines separated by +tab characters (``\t``). The values of each column are: + +1. Three characters indicating the type of returned answer (e.g. + def for definition, ``sug`` for suggestion, etc). +2. Type of the symbol. This can be ``skProc``, ``skLet``, and just + about any of the enums defined in the module ``compiler/ast.nim``. +3. Full qualitifed path of the symbol. If you are querying a symbol + defined in the ``proj.nim`` file, this would have the form + ``proj.symbolName``. +4. Type/signature. For variables and enums this will contain the + type of the symbol, for procs, methods and templates this will + contain the full unique signature (e.g. ``proc (TFile)``). +5. Full path to the file containing the symbol. +6. Line where the symbol is located in the file. Lines start to + count at **1**. +7. Column where the symbol is located in the file. Columns start + to count at **0**. +8. Docstring for the symbol if available or the empty string. To + differentiate the docstring from end of answer in server mode, + the docstring is always provided enclosed in double quotes, and + if the docstring spans multiple lines, all following lines of the + docstring will start with a blank space to align visually with + the starting quote. + + Also, you won't find raw ``\n`` characters breaking the one + answer per line format. Instead you will need to parse sequences + in the form ``\xHH``, where *HH* is a hexadecimal value (e.g. + newlines generate the sequence ``\x0A``). + +The following sections define the expected output for each kind of +symbol for which idetools returns valid output. + + +skConst +------- + +| **Third column**: module + [n scope nesting] + const name. +| **Fourth column**: the type of the const value. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + const SOME_SEQUENCE = @[1, 2] + --> col 2: $MODULE.SOME_SEQUENCE + col 3: seq[int] + col 7: "" + + +skEnumField +----------- + +| **Third column**: module + [n scope nesting] + enum type + enum field name. +| **Fourth column**: enum type grouping other enum fields. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + Open(filename, fmWrite) + --> col 2: system.TFileMode.fmWrite + col 3: TFileMode + col 7: "" + + +skForVar +-------- + +| **Third column**: module + [n scope nesting] + var name. +| **Fourth column**: type of the var. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + proc looper(filename = "tests.nim") = + for letter in filename: + echo letter + --> col 2: $MODULE.looper.letter + col 3: char + col 7: "" + + +skIterator +---------- + +The fourth column will be the empty string if the iterator is being +defined, since at that point in the file the parser hasn't processed +the full line yet. The signature will be returned complete in +posterior instances of the iterator. + +| **Third column**: module + [n scope nesting] + iterator name. +| **Fourth column**: signature of the iterator including return type. +| **Docstring**: docstring if available. + +.. code-block:: nimrod + let + text = "some text" + letters = toSeq(runes(text)) + --> col 2: unicode.runes + col 3: iterator (string): TRune + col 7: "iterates over any unicode character of the string `s`." + + +skLet +----- + +| **Third column**: module + [n scope nesting] + let name. +| **Fourth column**: the type of the let variable. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + let + text = "some text" + --> col 2: $MODULE.text + col 3: TaintedString + col 7: "" + + +skMethod +-------- + +The fourth column will be the empty string if the method is being +defined, since at that point in the file the parser hasn't processed +the full line yet. The signature will be returned complete in +posterior instances of the method. + +Methods imply `dynamic dispatch `_ and +idetools performs a static analysis on the code. For this reason +idetools may not return the definition of the correct method you +are querying because it may be impossible to know until the code +is executed. It will try to return the method which covers the most +possible cases (i.e. for variations of different classes in a +hierarchy it will prefer methods using the base class). + +While at the language level a method is differentiated from others +by the parameters and return value, the signature of the method +returned by idetools returns also the pragmas for the method. + +Note that at the moment the word ``proc`` is returned for the +signature of the found method instead of the expected ``method``. +This may change in the future. + +| **Third column**: module + [n scope nesting] + method name. +| **Fourth column**: signature of the method including return type. +| **Docstring**: docstring if available. + +.. code-block:: nimrod + method eval(e: PExpr): int = quit "to override!" + method eval(e: PLiteral): int = e.x + method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b) + echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) + --> col 2: $MODULE.eval + col 3: proc (PPlusExpr): int + col 7: "" + + +skParam +------- + +| **Third column**: module + [n scope nesting] + param name. +| **Fourth column**: the type of the parameter. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + proc reader(filename = "tests.nim") = + let text = readFile(filename) + --> col 2: $MODULE.reader.filename + col 3: string + col 7: "" + + +skProc +------ + +The fourth column will be the empty string if the proc is being +defined, since at that point in the file the parser hasn't processed +the full line yet. The signature will be returned complete in +posterior instances of the proc. + +While at the language level a proc is differentiated from others +by the parameters and return value, the signature of the proc +returned by idetools returns also the pragmas for the proc. + +| **Third column**: module + [n scope nesting] + proc name. +| **Fourth column**: signature of the proc including return type. +| **Docstring**: docstring if available. + +.. code-block:: nimrod + Open(filename, fmWrite) + --> col 2: system.Open + col 3: proc (var TFile, string, TFileMode, int): bool + col 7: + "Opens a file named `filename` with given `mode`. + + Default mode is readonly. Returns true iff the file could be opened. + This throws no exception if the file could not be opened." + + +skResult +-------- + +| **Third column**: module + [n scope nesting] + result. +| **Fourth column**: the type of the result. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + proc getRandomValue() : int = + return 4 + --> col 2: $MODULE.getRandomValue.result + col 3: int + col 7: "" + + +skTemplate +---------- + +The fourth column will be the empty string if the template is being +defined, since at that point in the file the parser hasn't processed +the full line yet. The signature will be returned complete in +posterior instances of the template. + +| **Third column**: module + [n scope nesting] + template name. +| **Fourth column**: signature of the template including return type. +| **Docstring**: docstring if available. + +.. code-block:: nimrod + let + text = "some text" + letters = toSeq(runes(text)) + --> col 2: sequtils.toSeq + col 3: proc (expr): expr + col 7: + "Transforms any iterator into a sequence. + + Example: + + .. code-block:: nimrod + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + assert odd_numbers == @[1, 3, 5, 7, 9]" + + +skType +------ + +| **Third column**: module + [n scope nesting] + type name. +| **Fourth column**: the type. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + proc writeTempFile() = + var output: TFile + --> col 2: system.TFile + col 3: TFile + col 7: "" + + +skVar +----- + +| **Third column**: module + [n scope nesting] + var name. +| **Fourth column**: the type of the var. +| **Docstring**: always the empty string. + +.. code-block:: nimrod + proc writeTempFile() = + var output: TFile + output.open("/tmp/somefile", fmWrite) + output.write("test") + --> col 2: $MODULE.writeTempFile.output + col 3: TFile + col 7: "" diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 339cee382..889fb2ced 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -443,6 +443,14 @@ in C/C++). **Note**: This pragma will not exist for the LLVM backend. +Nimrod idetools integration +======================= + +Nimrod provides language integration with external IDEs through the +idetools command. See the documentation of `idetools `_ +for further information. + + Nimrod interactive mode ======================= diff --git a/web/nimrod.ini b/web/nimrod.ini index d9c6cb786..94f196c64 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -37,7 +37,7 @@ UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson.""" [Documentation] doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters;trmacros" -doc: "tools;c2nim;niminst;nimgrep;gc;estp" +doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools" pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc" srcdoc2: "system.nim;impure/graphics;wrappers/sdl" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" From d2def332fd6d290bd74cb1e491b3c8f8f09d894a Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 16 Jun 2013 13:00:24 +0200 Subject: [PATCH 49/83] Adds $SILENT replacement to reduce test output verbosity. --- tests/caas/basic-recompile.txt | 6 ++-- tests/caas/compile-suggest.txt | 4 +-- tests/caas/compile-then-def.txt | 8 +++-- tests/caas/def-def-compile.txt | 8 +++-- tests/caas/def-then-compile.txt | 5 ++-- tests/caas/idetools_api.txt | 36 +++++++++++------------ tests/caas/issue_416_template_shift.txt | 10 +++---- tests/caas/issue_452_export_shift.txt | 10 +++---- tests/caas/issue_477_dynamic_dispatch.txt | 2 +- tests/caas/suggest-compile.txt | 4 +-- tests/caasdriver.nim | 3 ++ 11 files changed, 52 insertions(+), 44 deletions(-) diff --git a/tests/caas/basic-recompile.txt b/tests/caas/basic-recompile.txt index d869944b9..620e0c059 100644 --- a/tests/caas/basic-recompile.txt +++ b/tests/caas/basic-recompile.txt @@ -1,10 +1,10 @@ main.nim -> c +> c --verbosity:0 --hints:on SuccessX -> c # The "Processing" string will be found always in proc mode since each # compilation command will generate it. We need to test it only in Caas mode to # verify the server is not recompiling again the file. +CaasRun > c --verbosity:0 --hints:on CaasRun ! Processing -SuccessX +CaasRun SuccessX diff --git a/tests/caas/compile-suggest.txt b/tests/caas/compile-suggest.txt index 617060347..378320014 100644 --- a/tests/caas/compile-suggest.txt +++ b/tests/caas/compile-suggest.txt @@ -1,7 +1,7 @@ main.nim -> c +> c --verbosity:0 --hints:on SuccessX -> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest +> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest $SILENT skField\tx skField\ty diff --git a/tests/caas/compile-then-def.txt b/tests/caas/compile-then-def.txt index 66ff29346..72ba46b04 100644 --- a/tests/caas/compile-then-def.txt +++ b/tests/caas/compile-then-def.txt @@ -1,9 +1,11 @@ main.nim -> c +> c --verbosity:0 --hints:on SuccessX -> idetools --track:$TESTNIM,5,18 --def + +> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on strutils.toUpper ! SuccessX -> idetools --track:$TESTNIM,5,18 --def + +> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on strutils.toUpper ! SuccessX diff --git a/tests/caas/def-def-compile.txt b/tests/caas/def-def-compile.txt index 12ba7e993..21d5ea962 100644 --- a/tests/caas/def-def-compile.txt +++ b/tests/caas/def-def-compile.txt @@ -1,10 +1,12 @@ main.nim -> idetools --track:$TESTNIM,5,18 --def +> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on strutils.toUpper ! SuccessX -> idetools --track:$TESTNIM,5,18 --def + +> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on strutils.toUpper ! SuccessX -> c + +> c --verbosity:0 --hints:on SuccessX diff --git a/tests/caas/def-then-compile.txt b/tests/caas/def-then-compile.txt index e44af7f63..2214bf02c 100644 --- a/tests/caas/def-then-compile.txt +++ b/tests/caas/def-then-compile.txt @@ -1,7 +1,8 @@ main.nim -> idetools --track:$TESTNIM,5,18 --def +> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on strutils.toUpper ! SuccessX -> c + +> c --verbosity:0 --hints:on SuccessX diff --git a/tests/caas/idetools_api.txt b/tests/caas/idetools_api.txt index bc1dc6a5f..c4c22399e 100644 --- a/tests/caas/idetools_api.txt +++ b/tests/caas/idetools_api.txt @@ -1,44 +1,44 @@ idetools_api.nim -> c +> c --verbosity:0 --hints:on SuccessX -> idetools --track:$TESTNIM,4,11 --def +> idetools --track:$TESTNIM,4,11 --def $SILENT def\tskType\tsystem.TFile\tTFile -> idetools --track:$TESTNIM,5,7 --def +> idetools --track:$TESTNIM,5,7 --def $SILENT def\tskProc\tsystem.Open\tproc \(var TFile, string, TFileMode, int\): bool -> idetools --track:$TESTNIM,5,21 --def +> idetools --track:$TESTNIM,5,21 --def $SILENT def\tskProc\tsystem.\&\tproc \(string, string\): string\{.noSideEffect.\} -> idetools --track:$TESTNIM,5,38 --def +> idetools --track:$TESTNIM,5,38 --def $SILENT def\tskEnumField\tsystem.TFileMode.fmWrite\tTFileMode -> idetools --track:$TESTNIM,7,6 --def +> idetools --track:$TESTNIM,7,6 --def $SILENT def\tskProc\tsystem.Close\tproc \(TFile\) -> idetools --track:$TESTNIM,12,23 --def +> idetools --track:$TESTNIM,12,23 --def $SILENT def\tskIterator\tunicode.runes\titerator \(string\): TRune -> idetools --track:$TESTNIM,12,15 --def +> idetools --track:$TESTNIM,12,15 --def $SILENT def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr -> idetools --track:$TESTNIM,15,7 --def +> idetools --track:$TESTNIM,15,7 --def $SILENT # ProcRun mode will fail the next line, because the type is returned empty. def\tskConst\t$MODULE.SOME_SEQUENCE\tseq\[int\]\t -> idetools --track:$TESTNIM,15,23 --def +> idetools --track:$TESTNIM,15,23 --def $SILENT def\tskProc\tsystem.@\tproc \(array\[IDX, T\]\): seq\[T\]\{.noSideEffect.\} -> idetools --track:$TESTNIM,17,3 --def +> idetools --track:$TESTNIM,17,3 --def $SILENT # ProcRun mode will fail the next line, because the type is returned empty. def\tskType\t$MODULE.bad_string\tbad_string\t -> idetools --track:$TESTNIM,11,24 --def +> idetools --track:$TESTNIM,11,24 --def $SILENT def\tskParam\t$MODULE.test_iterators.filename\tstring -> idetools --track:$TESTNIM,6,5 --def +> idetools --track:$TESTNIM,6,5 --def $SILENT def\tskVar\t$MODULE.test_enums.o\tTFile -> idetools --track:$TESTNIM,12,34 --def +> idetools --track:$TESTNIM,12,34 --def $SILENT def\tskLet\t$MODULE.test_iterators.input\tTaintedString -> idetools --track:$TESTNIM,13,35 --def +> idetools --track:$TESTNIM,13,35 --def $SILENT def\tskForVar\t$MODULE.test_iterators.letter\tTRune -> idetools --track:$TESTNIM,23,3 --def +> idetools --track:$TESTNIM,23,3 --def $SILENT def\tskResult\t$MODULE.adder.result\tint -> idetools --track:$TESTNIM,19,6 --def +> idetools --track:$TESTNIM,19,6 --def $SILENT # ProcRun mode will fail the next line, because the type is returned empty. def\tskField\t$MODULE.TPerson.name\tbad_string\t -> idetools --track:$TESTNIM,43,7 --def +> idetools --track:$TESTNIM,43,7 --def $SILENT def\tskMethod\t$MODULE.eval\tproc \(PPlusExpr\): int\t diff --git a/tests/caas/issue_416_template_shift.txt b/tests/caas/issue_416_template_shift.txt index 951a21612..b1f47c1ac 100644 --- a/tests/caas/issue_416_template_shift.txt +++ b/tests/caas/issue_416_template_shift.txt @@ -1,14 +1,14 @@ issue_416_template_shift.nim -> c +> c --verbosity:0 --hints:on SuccessX -> idetools --track:$TESTNIM,12,28 --def +> idetools --track:$TESTNIM,12,28 --def $SILENT def\tskType\tsystem.string\tstring -> idetools --track:$TESTNIM,12,35 --def +> idetools --track:$TESTNIM,12,35 --def $SILENT def\tskLet\t$MODULE.failtest.input\tTaintedString # The following fail because they seem shifted one colum to the right. -> idetools --track:$TESTNIM,12,16 --def +> idetools --track:$TESTNIM,12,16 --def $SILENT def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr -> idetools --track:$TESTNIM,12,22 --def +> idetools --track:$TESTNIM,12,22 --def $SILENT def\tskIterator\tunicode.runes\titerator \(string\): TRune diff --git a/tests/caas/issue_452_export_shift.txt b/tests/caas/issue_452_export_shift.txt index 56ba0b74f..4676ed71e 100644 --- a/tests/caas/issue_452_export_shift.txt +++ b/tests/caas/issue_452_export_shift.txt @@ -1,11 +1,11 @@ issue_452_export_shift.nim -> c +> c --verbosity:0 --hints:on SuccessX -> idetools --track:$TESTNIM,2,2 --def +> idetools --track:$TESTNIM,2,2 --def $SILENT def\tskConst\t$MODULE.VERSION_STR1\tstring -> idetools --track:$TESTNIM,3,2 --def +> idetools --track:$TESTNIM,3,2 --def $SILENT def\tskConst\t$MODULE.VERSION_STR2\tstring -> idetools --track:$TESTNIM,7,5 --def +> idetools --track:$TESTNIM,7,5 --def $SILENT def\tskProc\t$MODULE.forward1\tproc \(\): string\t -> idetools --track:$TESTNIM,8,5 --def +> idetools --track:$TESTNIM,8,5 --def $SILENT def\tskProc\t$MODULE.forward2\tproc \(\): string\t diff --git a/tests/caas/issue_477_dynamic_dispatch.txt b/tests/caas/issue_477_dynamic_dispatch.txt index 068a9d022..12fd750de 100644 --- a/tests/caas/issue_477_dynamic_dispatch.txt +++ b/tests/caas/issue_477_dynamic_dispatch.txt @@ -1,5 +1,5 @@ issue_477_dynamic_dispatch.nim > c --run SuccessX -> idetools --track:issue_477_dynamic_dispatch.nim,19,5 --def +> idetools --track:issue_477_dynamic_dispatch.nim,19,5 --def $SILENT def\tskMethod\tissue_477_dynamic_dispatch.collide\tproc \(TUnit, TThing\)\{.inline.\} diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt index ac6187236..66ae795ed 100644 --- a/tests/caas/suggest-compile.txt +++ b/tests/caas/suggest-compile.txt @@ -1,7 +1,7 @@ main.nim -> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest +> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest $SILENT skField\tx skField\ty -> c +> c --verbosity:0 --hints:on SuccessX diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim index cd27839c7..f6f3d4e38 100644 --- a/tests/caasdriver.nim +++ b/tests/caasdriver.nim @@ -59,6 +59,8 @@ const modes = [CaasRun, ProcRun, SymbolProcRun] filenameReplaceVar = "$TESTNIM" moduleReplaceVar = "$MODULE" + silentReplaceVar = "$SILENT" + silentReplaceText = "--verbosity:0 --hints:off" var TesterDir = getAppDir() @@ -67,6 +69,7 @@ var proc replaceVars(session: var TNimrodSession, text: string): string = result = text.replace(filenameReplaceVar, session.filename) result = result.replace(moduleReplaceVar, session.modname) + result = result.replace(silentReplaceVar, silentReplaceText) proc startNimrodSession(project, script: string, mode: TRunMode): TNimrodSession = From b15f585273092f05145dd5b3fec5e83e27fbe45a Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 16 Jun 2013 13:18:09 +0200 Subject: [PATCH 50/83] Adds idetoos testcase to verify returned signatures. At the moment too many of them return proc. --- .gitignore | 2 +- tests/caas/its_full_of_procs.nim | 29 +++++++++++++++++++++++++++++ tests/caas/its_full_of_procs.txt | 20 ++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/caas/its_full_of_procs.nim create mode 100644 tests/caas/its_full_of_procs.txt diff --git a/.gitignore b/.gitignore index a52319eff..dcb94935f 100644 --- a/.gitignore +++ b/.gitignore @@ -173,7 +173,7 @@ examples/cross_calculator/android/tags /tests/caas/issue_416_template_shift /tests/caas/issue_452_export_shift /tests/caas/issue_477_dynamic_dispatch +/tests/caas/its_full_of_procs /tests/caas/main /tests/caasdriver /tools/nimgrep -no changes added to commit (use "git add" and/or "git commit -a") diff --git a/tests/caas/its_full_of_procs.nim b/tests/caas/its_full_of_procs.nim new file mode 100644 index 000000000..45347490c --- /dev/null +++ b/tests/caas/its_full_of_procs.nim @@ -0,0 +1,29 @@ +import unicode, sequtils + +# This example shows that idetools returns proc as signature for everything +# which can be called. While a clever person would use the second column to +# differentiate betwen procs, methods and others, why does the output contain +# incorrect information? + +type + TThing = object of TObject + TUnit = object of TThing + x: int + +method collide(a, b: TThing) {.inline.} = + quit "to override!" + +method collide(a: TThing, b: TUnit) {.inline.} = + echo "1" + +method collide(a: TUnit, b: TThing) {.inline.} = + echo "2" + +var + a, b: TUnit + +let + input = readFile("its_full_of_procs.nim") + letters = toSeq(runes(string(input))) + +collide(a, b) # output: 2 diff --git a/tests/caas/its_full_of_procs.txt b/tests/caas/its_full_of_procs.txt new file mode 100644 index 000000000..31a2d3baa --- /dev/null +++ b/tests/caas/its_full_of_procs.txt @@ -0,0 +1,20 @@ +its_full_of_procs.nim + +> idetools --track:$TESTNIM,26,15 --def $SILENT +skProc +proc \( + +> idetools --track:$TESTNIM,27,21 --def $SILENT +skIterator +iterator \( +!proc \( + +> idetools --track:$TESTNIM,29,0 --def $SILENT +skMethod +method \( +!proc \( + +> idetools --track:$TESTNIM,27,15 --def $SILENT +skTemplate +template \( +!proc \( From 25f45cace14f9b48f4b7ada3ace4c4f430d703a6 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 16 Jun 2013 13:42:36 +0200 Subject: [PATCH 51/83] Adds idetools test to check declaration proc signatures. --- .gitignore | 1 + tests/caas/forward_declarations.nim | 15 +++++++++++++++ tests/caas/forward_declarations.txt | 9 +++++++++ 3 files changed, 25 insertions(+) create mode 100644 tests/caas/forward_declarations.nim create mode 100644 tests/caas/forward_declarations.txt diff --git a/.gitignore b/.gitignore index dcb94935f..06a6e7d9f 100644 --- a/.gitignore +++ b/.gitignore @@ -168,6 +168,7 @@ examples/cross_calculator/android/tags /testresults.html /testresults.json /tests/caas/SymbolProcRun.*/ +/tests/caas/forward_declarations /tests/caas/idetools_api /tests/caas/imported /tests/caas/issue_416_template_shift diff --git a/tests/caas/forward_declarations.nim b/tests/caas/forward_declarations.nim new file mode 100644 index 000000000..177d82f20 --- /dev/null +++ b/tests/caas/forward_declarations.nim @@ -0,0 +1,15 @@ +# This example shows that idetools returns an empty signature for a forward +# declared proc in proc/symproc runs, but correctly returns the full signature +# in caas mode. + +proc echoHello(text: string) + +proc testForward() = + echo "T" + echoHello("T") + +proc echoHello(text: string) = + echo "Hello Mr." & text + +when isMainModule: + testForward() diff --git a/tests/caas/forward_declarations.txt b/tests/caas/forward_declarations.txt new file mode 100644 index 000000000..b1695b9c7 --- /dev/null +++ b/tests/caas/forward_declarations.txt @@ -0,0 +1,9 @@ +forward_declarations.nim + +> idetools --track:$TESTNIM,9,5 --def $SILENT +skProc +proc \(string\) + +> idetools --track:$TESTNIM,5,9 --def $SILENT +skProc +proc \(string\) From 1f8c5c37d38f4f4a8f9651e5a6ca699fc08ba53d Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 16 Jun 2013 21:43:34 +0200 Subject: [PATCH 52/83] Adds idetools test to verify --usages, reuses testcase. --- tests/caas/forward_usages.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/caas/forward_usages.txt diff --git a/tests/caas/forward_usages.txt b/tests/caas/forward_usages.txt new file mode 100644 index 000000000..05ef517dc --- /dev/null +++ b/tests/caas/forward_usages.txt @@ -0,0 +1,17 @@ +forward_declarations.nim + +> c --verbosity:0 --hints:on +SuccessX + +# None of the following return three instances of the echoHello proc, the first +# being the forward declaration, the second being the usage inside testForward, +# and the third being the actual implementation. + +> idetools --track:$TESTNIM,5,5 --usages $SILENT +skProc.*\n.*skProc.*\n.*skProc + +> idetools --track:$TESTNIM,9,5 --usages $SILENT +skProc.*\n.*skProc.*\n.*skProc + +> idetools --track:$TESTNIM,11,5 --usages $SILENT +skProc.*\n.*skProc.*\n.*skProc From a1da2b713bccbf9555f087caeb4e093e445952f5 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 16 Jun 2013 22:35:54 +0200 Subject: [PATCH 53/83] Adds idetools hierarchy nesting test. --- .gitignore | 1 + tests/caas/absurd_nesting.nim | 29 +++++++++++++++++++++++++++++ tests/caas/absurd_nesting.txt | 29 +++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests/caas/absurd_nesting.nim create mode 100644 tests/caas/absurd_nesting.txt diff --git a/.gitignore b/.gitignore index 06a6e7d9f..03e7c35cc 100644 --- a/.gitignore +++ b/.gitignore @@ -168,6 +168,7 @@ examples/cross_calculator/android/tags /testresults.html /testresults.json /tests/caas/SymbolProcRun.*/ +/tests/caas/absurd_nesting /tests/caas/forward_declarations /tests/caas/idetools_api /tests/caas/imported diff --git a/tests/caas/absurd_nesting.nim b/tests/caas/absurd_nesting.nim new file mode 100644 index 000000000..136d65cc7 --- /dev/null +++ b/tests/caas/absurd_nesting.nim @@ -0,0 +1,29 @@ +# Tries to test the full ownership path generated by idetools. + +proc lev1(t1: string) = + var temp = t1 + for i in 0..len(temp)-1: + temp[i] = chr(int(temp[i]) + 1) + + proc lev2(t2: string) = + var temp = t2 + for i in 0..len(temp)-1: + temp[i] = chr(int(temp[i]) + 1) + + proc lev3(t3: string) = + var temp = t3 + for i in 0..len(temp)-1: + temp[i] = chr(int(temp[i]) + 1) + + proc lev4(t4: string) = + var temp = t4 + for i in 0..len(temp)-1: + temp[i] = chr(int(temp[i]) + 1) + + echo temp & "(lev4)" + lev4(temp & "(lev3)") + lev3(temp & "(lev2)") + lev2(temp & "(lev1)") + +when isMainModule: + lev1("abcd") diff --git a/tests/caas/absurd_nesting.txt b/tests/caas/absurd_nesting.txt new file mode 100644 index 000000000..986e34836 --- /dev/null +++ b/tests/caas/absurd_nesting.txt @@ -0,0 +1,29 @@ +absurd_nesting.nim + +> c --verbosity:0 --hints:on +SuccessX + +> idetools --track:$TESTNIM,6,6 --def $SILENT +skVar\tabsurd_nesting.lev1.temp\tstring + +> idetools --track:$TESTNIM,21,13 --def $SILENT +skVar\tabsurd_nesting.lev1.lev2.lev3.lev4.temp\tstring + +> idetools --track:$TESTNIM,6,27 --def $SILENT +skForVar\tabsurd_nesting.lev1.i\tint + +> idetools --track:$TESTNIM,21,33 --def $SILENT +skForVar\tabsurd_nesting.lev1.lev1.lev3.lev4.i\tint + +> idetools --track:$TESTNIM,24,8 --def $SILENT +skProc\tabsurd_nesting.lev1.lev1.lev3.lev4\tproc \(string\) + +> idetools --track:$TESTNIM,4,13 --def $SILENT +skParam\tabsurd_nesting.lev1.t1\tstring + +> idetools --track:$TESTNIM,4,13 --def $SILENT +skParam\tabsurd_nesting.lev1.t1\tstring + +> idetools --track:$TESTNIM,19,19 --def $SILENT +skParam\tabsurd_nesting.lev1.lev2.lev3.lev4.t4\tstring + From 5b1caed806e017b39c9dd447fde00085fe5aab09 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 18 Jun 2013 21:01:12 +0200 Subject: [PATCH 54/83] Replaces relative file slurping with hardcoded array. --- lib/packages/docutils/highlite.nim | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 21dd1543a..f4b2f5867 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -44,7 +44,17 @@ const "Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink", "Label", "Reference", "Other"] - nimrodKeywords = slurp("doc/keywords.txt").split + # The following list comes from doc/keywords.txt, make sure it is + # synchronized with this array. + nimrodKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block", + "break", "case", "cast", "const", "continue", "converter", "discard", + "distinct", "div", "do", "elif", "else", "end", "enum", "except", "export", + "finally", "for", "from", "generic", "if", "import", "in", "include", + "interface", "is", "isnot", "iterator", "lambda", "let", "macro", "method", + "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", + "ptr", "raise", "ref", "return", "shared", "shl", "shr", "static", + "template", "try", "tuple", "type", "var", "when", "while", "with", + "without", "xor", "yield"] proc getSourceLanguage*(name: string): TSourceLanguage = for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)): From 4c7607e074de9272f3e7f3b5e0d40c6ee5c8259c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 18 Jun 2013 21:42:53 +0200 Subject: [PATCH 55/83] Adds previous slurping as runnable testcase. --- lib/packages/docutils/highlite.nim | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index f4b2f5867..b1b6c41e2 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -45,7 +45,7 @@ const "Label", "Reference", "Other"] # The following list comes from doc/keywords.txt, make sure it is - # synchronized with this array. + # synchronized with this array by running the module itself as a test case. nimrodKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block", "break", "case", "cast", "const", "continue", "converter", "discard", "distinct", "div", "do", "elif", "else", "end", "enum", "except", "export", @@ -545,3 +545,16 @@ proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) = of langC: cNextToken(g) of langJava: javaNextToken(g) +when isMainModule: + var keywords: seq[string] + # Try to work running in both the subdir or at the root. + for filename in ["doc/keywords.txt", "../../../doc/keywords.txt"]: + except: echo filename, " not found" + let input = string(readFile(filename)) + keywords = input.split() + break + doAssert (not keywords.isNil, "Couldn't read any keywords.txt file!") + doAssert keywords.len == nimrodKeywords.len, "No matching lengths" + for i in 0..keywords.len-1: + echo keywords[i], " == ", nimrodKeywords[i] + doAssert keywords[i] == nimrodKeywords[i], "Unexpected keyword" From 579d0f6f411170e7b96b40ce5d5b7377da1cfb4f Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Tue, 18 Jun 2013 22:18:44 +0200 Subject: [PATCH 56/83] Adds highlite to tests suite. --- lib/packages/docutils/highlite.nim | 2 +- tests/specials.nim | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index b1b6c41e2..69da2bba8 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -556,5 +556,5 @@ when isMainModule: doAssert (not keywords.isNil, "Couldn't read any keywords.txt file!") doAssert keywords.len == nimrodKeywords.len, "No matching lengths" for i in 0..keywords.len-1: - echo keywords[i], " == ", nimrodKeywords[i] + #echo keywords[i], " == ", nimrodKeywords[i] doAssert keywords[i] == nimrodKeywords[i], "Unexpected keyword" diff --git a/tests/specials.nim b/tests/specials.nim index 156a289d2..9ced66bbb 100644 --- a/tests/specials.nim +++ b/tests/specials.nim @@ -194,6 +194,8 @@ proc runSpecialTests(r: var TResults, options: string) = for t in os.walkFiles("tests/patterns/t*.nim"): runSingleTest(r, t, options) + for t in ["lib/packages/docutils/highlite"]: + runSingleTest(r, t, options) proc rejectSpecialTests(r: var TResults, options: string) = rejectThreadTests(r, options) From 705e8e929a230757fc8bc33848878f5116b20195 Mon Sep 17 00:00:00 2001 From: onionhammer Date: Thu, 20 Jun 2013 17:52:30 -0500 Subject: [PATCH 57/83] Fix: OpenSSL on Windows Updated CRYPTO_malloc_init to no longer call CRYPTO_set_mem_functions if compiling for windows Updated ERR_load_BIO_strings to import from DLLUtilName instead of DLLSSLName Updated OpenSSL_add_all_algorithms to import from DLLUtilName instead of DLLSSLName --- lib/wrappers/openssl.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index ca4ad6a99..f310c969b 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -196,7 +196,7 @@ const proc SSL_library_init*(): cInt{.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_load_error_strings*(){.cdecl, dynlib: DLLSSLName, importc.} -proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLSSLName, importc.} +proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.} proc SSLv23_client_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} proc SSLv23_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} @@ -262,14 +262,15 @@ proc ERR_error_string*(e: cInt, buf: cstring): cstring{.cdecl, proc ERR_get_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_peek_last_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.} -proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLSSLName, importc: "OPENSSL_add_all_algorithms_conf".} +proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSSL_add_all_algorithms_conf".} proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.} proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.} proc CRYPTO_malloc_init*() = - CRYPTO_set_mem_functions(alloc, realloc, dealloc) + when not defined(windows): + CRYPTO_set_mem_functions(alloc, realloc, dealloc) when True: nil From a1e823b03856c9f0d0b2a713e70f651ea9ad1fea Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 21 Jun 2013 16:07:13 +0200 Subject: [PATCH 58/83] Added an asyncio.isSendDataBuffered procedure. --- lib/pure/asyncio.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index 403401ff1..a1ce75af3 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -423,6 +423,10 @@ proc isConnecting*(s: PAsyncSocket): bool = proc isClosed*(s: PAsyncSocket): bool = ## Determines whether ``s`` has been closed. return s.info == SockClosed +proc isSendDataBuffered*(s: PAsyncSocket): bool = + ## Determines whether ``s`` has data waiting to be sent, i.e. whether this + ## socket's sendBuffer contains data. + return s.sendBuffer.len != 0 proc setHandleWrite*(s: PAsyncSocket, handleWrite: proc (s: PAsyncSocket) {.closure.}) = From 2bb4a853ecef114ce9a5fcf08ea5bc5ffff2aae0 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 21 Jun 2013 16:01:31 +0100 Subject: [PATCH 59/83] Send buffer will be respected now when closing async SCGI clients. --- lib/pure/scgi.nim | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index 57fa0b144..8e45032c8 100644 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -175,6 +175,15 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult = if ret == L: return ReadFullLine +proc checkCloseSocket(client: PAsyncClient) = + if not client.c.isClosed: + if client.c.isSendDataBuffered: + client.c.setHandleWrite do (s: PAsyncSocket): + if not s.isClosed and not s.isSendDataBuffered: + s.close() + s.delHandleWrite() + else: client.c.close() + proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = case client.mode of ClientReadChar: @@ -206,7 +215,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = client.mode = ClientReadContent else: s.handleRequest(client.c, client.input, client.headers) - if not client.c.isClosed: client.c.close() + checkCloseSocket(client) of ReadPartialLine, ReadDisconnected, ReadNone: return of ClientReadContent: let L = parseInt(client.headers["CONTENT_LENGTH"])-client.input.len @@ -215,11 +224,11 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = case ret of ReadFullLine: s.handleRequest(client.c, client.input, client.headers) - if not client.c.isClosed: client.c.close() + checkCloseSocket(client) of ReadPartialLine, ReadDisconnected, ReadNone: return else: s.handleRequest(client.c, client.input, client.headers) - if not client.c.isClosed: client.c.close() + checkCloseSocket(client) proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) = var client: PAsyncSocket From d23d14897a6249dd6b03c7fc525f42bfac2b6700 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 22 Jun 2013 00:15:07 +0100 Subject: [PATCH 60/83] Symlinks are now expanded in os.getAppFilename on Mac OS X. --- lib/pure/os.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bf00afed2..de4893f8c 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1368,6 +1368,9 @@ when defined(macosx): proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} = ## Returns the filename of the application's executable. + ## + ## This procedure will resolve symlinks. + ## ## **Note**: This does not work reliably on BSD. # Linux: /proc//exe @@ -1397,6 +1400,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} = result = newString(int(size)) if getExecPath2(result, size): result = "" # error! + if result.len > 0: + result = result.expandFilename else: # little heuristic that may work on other POSIX-like systems: result = string(getEnv("_")) From 317675d5e574acc038a67723e8654b0efcd532e7 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 22 Jun 2013 19:42:26 +0100 Subject: [PATCH 61/83] Changed the type of gboolean to a distinct cint. --- lib/wrappers/gtk/glib2.nim | 8 +++++++- lib/wrappers/gtk/gtk2.nim | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/wrappers/gtk/glib2.nim b/lib/wrappers/gtk/glib2.nim index 4151ba954..3fa672c85 100644 --- a/lib/wrappers/gtk/glib2.nim +++ b/lib/wrappers/gtk/glib2.nim @@ -27,7 +27,7 @@ type gshort* = cshort glong* = clong gint* = cint - gboolean* = bool + gboolean* = distinct gint guchar* = char gushort* = int16 gulong* = int @@ -173,6 +173,12 @@ type TGBoxedFreeFunc* = proc (boxed: gpointer){.cdecl.} PGsource = pointer # I don't know and don't care +converter gbool*(nimbool: bool): gboolean = + return ord(nimbool).gboolean + +converter toBool*(gbool: gboolean): bool = + return int(gbool) == 1 + const G_TYPE_FUNDAMENTAL_SHIFT* = 2 G_TYPE_FUNDAMENTAL_MAX* = 255 shl G_TYPE_FUNDAMENTAL_SHIFT diff --git a/lib/wrappers/gtk/gtk2.nim b/lib/wrappers/gtk/gtk2.nim index 6b418024e..63e69130a 100644 --- a/lib/wrappers/gtk/gtk2.nim +++ b/lib/wrappers/gtk/gtk2.nim @@ -2,6 +2,8 @@ import glib2, atk, pango, gdk2pixbuf, gdk2 +export gbool, toBool + when defined(win32): const lib = "libgtk-win32-2.0-0.dll" From 9540a93d9542d9d1fed4f5a149156ba43da9be80 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 22 Jun 2013 19:57:12 +0100 Subject: [PATCH 62/83] Fixed terminal module so that it doesn't raise an EOS exception when stdout is redirected. --- lib/pure/terminal.nim | 666 +++++++++++++++++++++--------------------- tools/nimgrep.nim | 2 + 2 files changed, 334 insertions(+), 334 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 3be6088ed..9b69bbaa4 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -1,312 +1,310 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module contains a few procedures to control the *terminal* -## (also called *console*). On UNIX, the implementation simply uses ANSI escape -## sequences and does not depend on any other module, on Windows it uses the -## Windows API. -## Changing the style is permanent even after program termination! Use the -## code ``system.addQuitProc(resetAttributes)`` to restore the defaults. - -import macros - -when defined(windows): - import windows, os - - var - conHandle: THandle - # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0) - - block: - var hTemp = GetStdHandle(STD_OUTPUT_HANDLE) - if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), - addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: - OSError() - - proc getCursorPos(): tuple [x,y: int] = - var c: TCONSOLE_SCREEN_BUFFER_INFO - if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError() - return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) - - proc getAttributes(): int16 = - var c: TCONSOLE_SCREEN_BUFFER_INFO - # workaround Windows bugs: try several times - if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0: - return c.wAttributes - else: - OSError() - return 0x70'i16 # ERROR: return white background, black text - - var - oldAttr = getAttributes() - -proc setCursorPos*(x, y: int) = - ## sets the terminal's cursor to the (x,y) position. (0,0) is the - ## upper left of the screen. - when defined(windows): - var c: TCoord - c.x = int16(x) - c.y = int16(y) - if SetConsoleCursorPosition(conHandle, c) == 0: OSError() - else: - stdout.write("\e[" & $y & ';' & $x & 'f') - -proc setCursorXPos*(x: int) = - ## sets the terminal's cursor to the x position. The y position is - ## not changed. - when defined(windows): - var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO - var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() - var origin = scrbuf.dwCursorPosition - origin.x = int16(x) - if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() - else: - stdout.write("\e[" & $x & 'G') - -when defined(windows): - proc setCursorYPos*(y: int) = - ## sets the terminal's cursor to the y position. The x position is - ## not changed. **Warning**: This is not supported on UNIX! - when defined(windows): - var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO - var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() - var origin = scrbuf.dwCursorPosition - origin.y = int16(y) - if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() - else: - nil - -proc CursorUp*(count=1) = - ## Moves the cursor up by `count` rows. - when defined(windows): - var p = getCursorPos() - dec(p.y, count) - setCursorPos(p.x, p.y) - else: - stdout.write("\e[" & $count & 'A') - -proc CursorDown*(count=1) = - ## Moves the cursor down by `count` rows. - when defined(windows): - var p = getCursorPos() - inc(p.y, count) - setCursorPos(p.x, p.y) - else: - stdout.write("\e[" & $count & 'B') - -proc CursorForward*(count=1) = - ## Moves the cursor forward by `count` columns. - when defined(windows): - var p = getCursorPos() - inc(p.x, count) - setCursorPos(p.x, p.y) - else: - stdout.write("\e[" & $count & 'C') - -proc CursorBackward*(count=1) = - ## Moves the cursor backward by `count` columns. - when defined(windows): - var p = getCursorPos() - dec(p.x, count) - setCursorPos(p.x, p.y) - else: - stdout.write("\e[" & $count & 'D') - -when true: - nil -else: - proc EraseLineEnd* = - ## Erases from the current cursor position to the end of the current line. - when defined(windows): - nil - else: - stdout.write("\e[K") - - proc EraseLineStart* = - ## Erases from the current cursor position to the start of the current line. - when defined(windows): - nil - else: - stdout.write("\e[1K") - - proc EraseDown* = - ## Erases the screen from the current line down to the bottom of the screen. - when defined(windows): - nil - else: - stdout.write("\e[J") - - proc EraseUp* = - ## Erases the screen from the current line up to the top of the screen. - when defined(windows): - nil - else: - stdout.write("\e[1J") - -proc EraseLine* = - ## Erases the entire current line. - when defined(windows): - var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO - var numwrote: DWORD - var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() - var origin = scrbuf.dwCursorPosition - origin.x = 0'i16 - if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() - var ht = scrbuf.dwSize.Y - origin.Y - var wt = scrbuf.dwSize.X - origin.X - if FillConsoleOutputCharacter(hStdout,' ', ht*wt, - origin, addr(numwrote)) == 0: - OSError() - if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, - scrbuf.dwCursorPosition, addr(numwrote)) == 0: - OSError() - else: - stdout.write("\e[2K") - setCursorXPos(0) - -proc EraseScreen* = - ## Erases the screen with the background colour and moves the cursor to home. - when defined(windows): - var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO - var numwrote: DWORD - var origin: TCoord # is inititalized to 0, 0 - var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() - if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y, - origin, addr(numwrote)) == 0: - OSError() - if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, - scrbuf.dwSize.X * scrbuf.dwSize.Y, - origin, addr(numwrote)) == 0: - OSError() - setCursorXPos(0) - else: - stdout.write("\e[2J") - -proc ResetAttributes* {.noconv.} = - ## resets all attributes; it is advisable to register this as a quit proc - ## with ``system.addQuitProc(resetAttributes)``. - when defined(windows): - discard SetConsoleTextAttribute(conHandle, oldAttr) - else: - stdout.write("\e[0m") - -type - TStyle* = enum ## different styles for text output - styleBright = 1, ## bright text - styleDim, ## dim text - styleUnknown, ## unknown - styleUnderscore = 4, ## underscored text - styleBlink, ## blinking/bold text - styleReverse = 7, ## unknown - styleHidden ## hidden text - -when not defined(windows): - var - # XXX: These better be thread-local - gFG = 0 - gBG = 0 - -proc setStyle*(style: set[TStyle]) = - ## sets the terminal style - when defined(windows): - var a = 0'i16 - if styleBright in style: a = a or int16(FOREGROUND_INTENSITY) - if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY) - if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO - if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE - discard SetConsoleTextAttribute(conHandle, a) - else: - for s in items(style): - stdout.write("\e[" & $ord(s) & 'm') - -proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) = - ## writes the text `txt` in a given `style`. - when defined(windows): - var old = getAttributes() - setStyle(style) - stdout.write(txt) - discard SetConsoleTextAttribute(conHandle, old) - else: - setStyle(style) - stdout.write(txt) - resetAttributes() - if gFG != 0: - stdout.write("\e[" & $ord(gFG) & 'm') - if gBG != 0: - stdout.write("\e[" & $ord(gBG) & 'm') - -type - TForegroundColor* = enum ## terminal's foreground colors - fgBlack = 30, ## black - fgRed, ## red - fgGreen, ## green - fgYellow, ## yellow - fgBlue, ## blue - fgMagenta, ## magenta - fgCyan, ## cyan - fgWhite ## white - - TBackgroundColor* = enum ## terminal's background colors - bgBlack = 40, ## black - bgRed, ## red - bgGreen, ## green - bgYellow, ## yellow - bgBlue, ## blue - bgMagenta, ## magenta - bgCyan, ## cyan - bgWhite ## white - -proc setForegroundColor*(fg: TForegroundColor, bright=false) = - ## sets the terminal's foreground color - when defined(windows): - var old = getAttributes() and not 0x0007 - if bright: - old = old or FOREGROUND_INTENSITY - const lookup: array [TForegroundColor, int] = [ - 0, - (FOREGROUND_RED), - (FOREGROUND_GREEN), - (FOREGROUND_RED or FOREGROUND_GREEN), - (FOREGROUND_BLUE), - (FOREGROUND_RED or FOREGROUND_BLUE), - (FOREGROUND_BLUE or FOREGROUND_GREEN), - (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] - discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[fg])) - else: - gFG = ord(fg) - if bright: inc(gFG, 60) - stdout.write("\e[" & $gFG & 'm') - -proc setBackgroundColor*(bg: TBackgroundColor, bright=false) = - ## sets the terminal's background color - when defined(windows): - var old = getAttributes() and not 0x0070 - if bright: - old = old or BACKGROUND_INTENSITY - const lookup: array [TBackgroundColor, int] = [ - 0, - (BACKGROUND_RED), - (BACKGROUND_GREEN), - (BACKGROUND_RED or BACKGROUND_GREEN), - (BACKGROUND_BLUE), - (BACKGROUND_RED or BACKGROUND_BLUE), - (BACKGROUND_BLUE or BACKGROUND_GREEN), - (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] - discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[bg])) - else: - gBG = ord(bg) - if bright: inc(gBG, 60) - stdout.write("\e[" & $gBG & 'm') +# +# +# Nimrod's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains a few procedures to control the *terminal* +## (also called *console*). On UNIX, the implementation simply uses ANSI escape +## sequences and does not depend on any other module, on Windows it uses the +## Windows API. +## Changing the style is permanent even after program termination! Use the +## code ``system.addQuitProc(resetAttributes)`` to restore the defaults. + +import macros + +when defined(windows): + import windows, os + + var + conHandle: THandle + # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0) + + block: + var hTemp = GetStdHandle(STD_OUTPUT_HANDLE) + if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), + addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: + OSError() + + proc getCursorPos(): tuple [x,y: int] = + var c: TCONSOLE_SCREEN_BUFFER_INFO + if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError() + return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) + + proc getAttributes(): int16 = + var c: TCONSOLE_SCREEN_BUFFER_INFO + # workaround Windows bugs: try several times + if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0: + return c.wAttributes + return 0x70'i16 # ERROR: return white background, black text + + var + oldAttr = getAttributes() + +proc setCursorPos*(x, y: int) = + ## sets the terminal's cursor to the (x,y) position. (0,0) is the + ## upper left of the screen. + when defined(windows): + var c: TCoord + c.x = int16(x) + c.y = int16(y) + if SetConsoleCursorPosition(conHandle, c) == 0: OSError() + else: + stdout.write("\e[" & $y & ';' & $x & 'f') + +proc setCursorXPos*(x: int) = + ## sets the terminal's cursor to the x position. The y position is + ## not changed. + when defined(windows): + var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO + var hStdout = conHandle + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + var origin = scrbuf.dwCursorPosition + origin.x = int16(x) + if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() + else: + stdout.write("\e[" & $x & 'G') + +when defined(windows): + proc setCursorYPos*(y: int) = + ## sets the terminal's cursor to the y position. The x position is + ## not changed. **Warning**: This is not supported on UNIX! + when defined(windows): + var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO + var hStdout = conHandle + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + var origin = scrbuf.dwCursorPosition + origin.y = int16(y) + if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() + else: + nil + +proc CursorUp*(count=1) = + ## Moves the cursor up by `count` rows. + when defined(windows): + var p = getCursorPos() + dec(p.y, count) + setCursorPos(p.x, p.y) + else: + stdout.write("\e[" & $count & 'A') + +proc CursorDown*(count=1) = + ## Moves the cursor down by `count` rows. + when defined(windows): + var p = getCursorPos() + inc(p.y, count) + setCursorPos(p.x, p.y) + else: + stdout.write("\e[" & $count & 'B') + +proc CursorForward*(count=1) = + ## Moves the cursor forward by `count` columns. + when defined(windows): + var p = getCursorPos() + inc(p.x, count) + setCursorPos(p.x, p.y) + else: + stdout.write("\e[" & $count & 'C') + +proc CursorBackward*(count=1) = + ## Moves the cursor backward by `count` columns. + when defined(windows): + var p = getCursorPos() + dec(p.x, count) + setCursorPos(p.x, p.y) + else: + stdout.write("\e[" & $count & 'D') + +when true: + nil +else: + proc EraseLineEnd* = + ## Erases from the current cursor position to the end of the current line. + when defined(windows): + nil + else: + stdout.write("\e[K") + + proc EraseLineStart* = + ## Erases from the current cursor position to the start of the current line. + when defined(windows): + nil + else: + stdout.write("\e[1K") + + proc EraseDown* = + ## Erases the screen from the current line down to the bottom of the screen. + when defined(windows): + nil + else: + stdout.write("\e[J") + + proc EraseUp* = + ## Erases the screen from the current line up to the top of the screen. + when defined(windows): + nil + else: + stdout.write("\e[1J") + +proc EraseLine* = + ## Erases the entire current line. + when defined(windows): + var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO + var numwrote: DWORD + var hStdout = conHandle + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + var origin = scrbuf.dwCursorPosition + origin.x = 0'i16 + if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() + var ht = scrbuf.dwSize.Y - origin.Y + var wt = scrbuf.dwSize.X - origin.X + if FillConsoleOutputCharacter(hStdout,' ', ht*wt, + origin, addr(numwrote)) == 0: + OSError() + if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, + scrbuf.dwCursorPosition, addr(numwrote)) == 0: + OSError() + else: + stdout.write("\e[2K") + setCursorXPos(0) + +proc EraseScreen* = + ## Erases the screen with the background colour and moves the cursor to home. + when defined(windows): + var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO + var numwrote: DWORD + var origin: TCoord # is inititalized to 0, 0 + var hStdout = conHandle + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y, + origin, addr(numwrote)) == 0: + OSError() + if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, + scrbuf.dwSize.X * scrbuf.dwSize.Y, + origin, addr(numwrote)) == 0: + OSError() + setCursorXPos(0) + else: + stdout.write("\e[2J") + +proc ResetAttributes* {.noconv.} = + ## resets all attributes; it is advisable to register this as a quit proc + ## with ``system.addQuitProc(resetAttributes)``. + when defined(windows): + discard SetConsoleTextAttribute(conHandle, oldAttr) + else: + stdout.write("\e[0m") + +type + TStyle* = enum ## different styles for text output + styleBright = 1, ## bright text + styleDim, ## dim text + styleUnknown, ## unknown + styleUnderscore = 4, ## underscored text + styleBlink, ## blinking/bold text + styleReverse = 7, ## unknown + styleHidden ## hidden text + +when not defined(windows): + var + # XXX: These better be thread-local + gFG = 0 + gBG = 0 + +proc setStyle*(style: set[TStyle]) = + ## sets the terminal style + when defined(windows): + var a = 0'i16 + if styleBright in style: a = a or int16(FOREGROUND_INTENSITY) + if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY) + if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO + if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE + discard SetConsoleTextAttribute(conHandle, a) + else: + for s in items(style): + stdout.write("\e[" & $ord(s) & 'm') + +proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) = + ## writes the text `txt` in a given `style`. + when defined(windows): + var old = getAttributes() + setStyle(style) + stdout.write(txt) + discard SetConsoleTextAttribute(conHandle, old) + else: + setStyle(style) + stdout.write(txt) + resetAttributes() + if gFG != 0: + stdout.write("\e[" & $ord(gFG) & 'm') + if gBG != 0: + stdout.write("\e[" & $ord(gBG) & 'm') + +type + TForegroundColor* = enum ## terminal's foreground colors + fgBlack = 30, ## black + fgRed, ## red + fgGreen, ## green + fgYellow, ## yellow + fgBlue, ## blue + fgMagenta, ## magenta + fgCyan, ## cyan + fgWhite ## white + + TBackgroundColor* = enum ## terminal's background colors + bgBlack = 40, ## black + bgRed, ## red + bgGreen, ## green + bgYellow, ## yellow + bgBlue, ## blue + bgMagenta, ## magenta + bgCyan, ## cyan + bgWhite ## white + +proc setForegroundColor*(fg: TForegroundColor, bright=false) = + ## sets the terminal's foreground color + when defined(windows): + var old = getAttributes() and not 0x0007 + if bright: + old = old or FOREGROUND_INTENSITY + const lookup: array [TForegroundColor, int] = [ + 0, + (FOREGROUND_RED), + (FOREGROUND_GREEN), + (FOREGROUND_RED or FOREGROUND_GREEN), + (FOREGROUND_BLUE), + (FOREGROUND_RED or FOREGROUND_BLUE), + (FOREGROUND_BLUE or FOREGROUND_GREEN), + (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] + discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[fg])) + else: + gFG = ord(fg) + if bright: inc(gFG, 60) + stdout.write("\e[" & $gFG & 'm') + +proc setBackgroundColor*(bg: TBackgroundColor, bright=false) = + ## sets the terminal's background color + when defined(windows): + var old = getAttributes() and not 0x0070 + if bright: + old = old or BACKGROUND_INTENSITY + const lookup: array [TBackgroundColor, int] = [ + 0, + (BACKGROUND_RED), + (BACKGROUND_GREEN), + (BACKGROUND_RED or BACKGROUND_GREEN), + (BACKGROUND_BLUE), + (BACKGROUND_RED or BACKGROUND_BLUE), + (BACKGROUND_BLUE or BACKGROUND_GREEN), + (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] + discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[bg])) + else: + gBG = ord(bg) + if bright: inc(gBG, 60) + stdout.write("\e[" & $gBG & 'm') proc isatty*(f: TFile): bool = ## returns true if `f` is associated with a terminal device. @@ -318,33 +316,33 @@ proc isatty*(f: TFile): bool = importc: "_isatty", header: "".} result = isatty(fileHandle(f)) != 0'i32 - -proc styledEchoProcessArg(s: string) = write stdout, s -proc styledEchoProcessArg(style: TStyle) = setStyle({style}) -proc styledEchoProcessArg(style: set[TStyle]) = setStyle style -proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color -proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color - + +proc styledEchoProcessArg(s: string) = write stdout, s +proc styledEchoProcessArg(style: TStyle) = setStyle({style}) +proc styledEchoProcessArg(style: set[TStyle]) = setStyle style +proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color +proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color + macro styledEcho*(m: varargs[expr]): stmt = ## to be documented. let m = callsite() - result = newNimNode(nnkStmtList) - - for i in countup(1, m.len - 1): - result.add(newCall(bindSym"styledEchoProcessArg", m[i])) - - result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) - result.add(newCall(bindSym"resetAttributes")) - -when isMainModule: - system.addQuitProc(resetAttributes) - write(stdout, "never mind") - eraseLine() - #setCursorPos(2, 2) - writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore}) - setBackGroundColor(bgCyan, true) - setForeGroundColor(fgBlue) - writeln(stdout, "ordinary text") - + result = newNimNode(nnkStmtList) + + for i in countup(1, m.len - 1): + result.add(newCall(bindSym"styledEchoProcessArg", m[i])) + + result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) + result.add(newCall(bindSym"resetAttributes")) + +when isMainModule: + system.addQuitProc(resetAttributes) + write(stdout, "never mind") + eraseLine() + #setCursorPos(2, 2) + writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore}) + setBackGroundColor(bgCyan, true) + setForeGroundColor(fgBlue) + writeln(stdout, "ordinary text") + styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore}) \ No newline at end of file diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index fa14f9e8e..c7893fe78 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -32,6 +32,7 @@ Options: --ignoreCase, -i be case insensitive --ignoreStyle, -y be style insensitive --ext:EX1|EX2|... only search the files with the given extension(s) + --nocolor output will be given without any colours. --verbose be verbose: list every processed file --help, -h shows this help --version, -v shows the version @@ -291,6 +292,7 @@ for kind, key, val in getopt(): of "ignorecase", "i": incl(options, optIgnoreCase) of "ignorestyle", "y": incl(options, optIgnoreStyle) of "ext": extensions = val.split('|') + of "nocolor": useWriteStyled = false of "verbose": incl(options, optVerbose) of "help", "h": writeHelp() of "version", "v": writeVersion() From bacb20a379a7ce607bc53f9c917ac675579569a8 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 25 Jun 2013 20:10:28 +0100 Subject: [PATCH 63/83] Deprecated OSError, and modified it to require an explicit OS error code. The deprecated functions include the OSError and OSErrorMsg, the name did not change however the signature of the new functions did. They now require a TOSErrorCode value be passed to them. This value can be retrieved using OSLastError. The reason this was done is because on Windows any win api call can reset the last error code to 0, this change allows the user to immediately grab the error code and worry about the string representation later if needs be. --- lib/pure/asyncio.nim | 7 +- lib/pure/os.nim | 174 ++++++++++++++++++++++++++++++---------- lib/pure/osproc.nim | 5 +- lib/pure/sockets.nim | 134 ++++++++++++++----------------- lib/windows/winlean.nim | 2 +- 5 files changed, 196 insertions(+), 126 deletions(-) diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index a1ce75af3..4ff6e0ced 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -167,7 +167,7 @@ proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, result = newAsyncSocket() result.socket = socket(domain, typ, protocol, buffered) result.proto = protocol - if result.socket == InvalidSocket: OSError() + if result.socket == InvalidSocket: OSError(OSLastError()) result.socket.setBlocking(false) proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = @@ -349,7 +349,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, client.sslNeedAccept = false client.info = SockConnected - if c == InvalidSocket: OSError() + if c == InvalidSocket: SocketError(server.socket) c.setBlocking(false) # TODO: Needs to be tested. # deleg.open is set in ``toDelegate``. @@ -642,8 +642,7 @@ when isMainModule: proc testRead(s: PAsyncSocket, no: int) = echo("Reading! " & $no) var data = "" - if not s.readLine(data): - OSError() + if not s.readLine(data): return if data == "": echo("Closing connection. " & $no) s.close() diff --git a/lib/pure/os.nim b/lib/pure/os.nim index de4893f8c..eaa22d351 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -39,6 +39,8 @@ type FWriteDir* = object of FWriteIO ## effect that denotes a write operation to ## the directory structure + TOSErrorCode* = distinct int32 ## Specifies an OS Error Code. + const doslike = defined(windows) or defined(OS2) or defined(DOS) # DOS-like filesystem @@ -171,10 +173,13 @@ const ## The character which separates the base filename from the extension; ## for example, the '.' in ``os.nim``. -proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} = +proc OSErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} = ## Retrieves the operating system's error flag, ``errno``. ## On Windows ``GetLastError`` is checked before ``errno``. ## Returns "" if no error occured. + ## + ## **Deprecated since version 0.9.4**: use the other ``OSErrorMsg`` proc. + result = "" when defined(Windows): var err = GetLastError() @@ -194,17 +199,89 @@ proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} = if errno != 0'i32: result = $os.strerror(errno) -proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} = +{.push warning[deprecated]: off.} +proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} = ## raises an EOS exception with the given message ``msg``. ## If ``msg == ""``, the operating system's error flag ## (``errno``) is converted to a readable error message. On Windows ## ``GetLastError`` is checked before ``errno``. ## If no error flag is set, the message ``unknown OS error`` is used. + ## + ## **Deprecated since version 0.9.4**: use the other ``OSError`` proc. if len(msg) == 0: var m = OSErrorMsg() raise newException(EOS, if m.len > 0: m else: "unknown OS error") else: raise newException(EOS, msg) +{.pop.} + +proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.} +proc `$`*(err: TOSErrorCode): string {.borrow.} + +proc OSErrorMsg*(errorCode: TOSErrorCode): string = + ## Converts an OS error code into a human readable string. + ## + ## The error code can be retrieved using the ``OSLastError`` proc. + ## + ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be + ## returned. + ## + ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to + ## make this procedure use the non-unicode Win API calls to retrieve the + ## message. + result = "" + when defined(Windows): + if errorCode != TOSErrorCode(0'i32): + when useWinUnicode: + var msgbuf: widecstring + if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200, + nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: LocalFree(cast[pointer](msgbuf)) + else: + var msgbuf: cstring + if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200, + nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: LocalFree(msgbuf) + else: + if errorCode != TOSErrorCode(0'i32): + result = $os.strerror(errorCode.int32) + +proc OSError*(errorCode: TOSErrorCode) = + ## Raises an ``EOS`` exception. The ``errorCode`` will determine the + ## message, ``OSErrorMsg`` will be used to get this message. + ## + ## The error code can be retrieved using the ``OSLastError`` proc. + ## + ## If the error code is ``0`` or an error message could not be retrieved, + ## the message ``unknown OS error`` will be used. + let msg = OSErrorMsg(errorCode) + if msg == "": + raise newException(EOS, "unknown OS error") + else: + raise newException(EOS, msg) + +{.push stackTrace:off.} +proc OSLastError*(): TOSErrorCode = + ## Retrieves the last operating system error code. + ## + ## This procedure is useful in the event when an OS call fails. In that case + ## this procedure will return the error code describing the reason why the + ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert + ## this code into a string. + ## + ## **Warning**: + ## The behaviour of this procedure varies between Windows and POSIX systems. + ## On Windows some OS calls can reset the error code to ``0`` causing this + ## procedure to return ``0``. It is therefore advised to call this procedure + ## immediately after an OS call fails. On POSIX systems this is not a problem. + + when defined(windows): + result = TOSErrorCode(GetLastError()) + else: + result = TOSErrorCode(errno) +{.pop.} proc UnixToNativePath*(path: string): string {. noSideEffect, rtl, extern: "nos$1".} = @@ -311,12 +388,12 @@ proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} = ## Returns the `file`'s last modification time. when defined(posix): var res: TStat - if stat(file, res) < 0'i32: OSError() + if stat(file, res) < 0'i32: OSError(OSLastError()) return res.st_mtime else: var f: TWIN32_Find_Data var h = findfirstFile(file, f) - if h == -1'i32: OSError() + if h == -1'i32: OSError(OSLastError()) result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)) findclose(h) @@ -324,12 +401,12 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} = ## Returns the `file`'s last read or write access time. when defined(posix): var res: TStat - if stat(file, res) < 0'i32: OSError() + if stat(file, res) < 0'i32: OSError(OSLastError()) return res.st_atime else: var f: TWIN32_Find_Data var h = findfirstFile(file, f) - if h == -1'i32: OSError() + if h == -1'i32: OSError(OSLastError()) result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)) findclose(h) @@ -337,12 +414,12 @@ proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} = ## Returns the `file`'s creation time. when defined(posix): var res: TStat - if stat(file, res) < 0'i32: OSError() + if stat(file, res) < 0'i32: OSError(OSLastError()) return res.st_ctime else: var f: TWIN32_Find_Data var h = findfirstFile(file, f) - if h == -1'i32: OSError() + if h == -1'i32: OSError(OSLastError()) result = winTimeToUnixTime(rdFileTime(f.ftCreationTime)) findclose(h) @@ -358,30 +435,30 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = when useWinUnicode: var res = newWideCString("", bufsize) var L = GetCurrentDirectoryW(bufsize, res) - if L == 0'i32: OSError() + if L == 0'i32: OSError(OSLastError()) result = res$L else: result = newString(bufsize) var L = GetCurrentDirectoryA(bufsize, result) - if L == 0'i32: OSError() + if L == 0'i32: OSError(OSLastError()) setLen(result, L) else: result = newString(bufsize) if getcwd(result, bufsize) != nil: setlen(result, c_strlen(result)) else: - OSError() + OSError(OSLastError()) proc setCurrentDir*(newDir: string) {.inline, tags: [].} = ## Sets the `current working directory`:idx:; `EOS` is raised if ## `newDir` cannot been set. when defined(Windows): when useWinUnicode: - if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError() + if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError(OSLastError()) else: - if SetCurrentDirectoryA(newDir) == 0'i32: OSError() + if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError()) else: - if chdir(newDir) != 0'i32: OSError() + if chdir(newDir) != 0'i32: OSError(OSLastError()) proc JoinPath*(head, tail: string): string {. noSideEffect, rtl, extern: "nos$1".} = @@ -571,23 +648,23 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", var res = newWideCString("", bufsize div 2) var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused) if L <= 0'i32 or L >= bufsize: - OSError() + OSError(OSLastError()) result = res$L else: var unused: cstring result = newString(bufsize) var L = GetFullPathNameA(filename, bufsize, result, unused) - if L <= 0'i32 or L >= bufsize: OSError() + if L <= 0'i32 or L >= bufsize: OSError(OSLastError()) setLen(result, L) elif defined(macosx) or defined(bsd): # On Mac OS X 10.5, realpath does not allocate the buffer on its own var pathBuffer: cstring = newString(pathMax) var resultBuffer = realpath(filename, pathBuffer) - if resultBuffer == nil: OSError() + if resultBuffer == nil: OSError(OSLastError()) result = $resultBuffer else: var res = realpath(filename, nil) - if res == nil: OSError() + if res == nil: OSError(OSLastError()) result = $res c_free(res) @@ -677,6 +754,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", var f1 = OpenHandle(path1) var f2 = OpenHandle(path2) + var lastErr: TOSErrorCode if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE: var fi1, fi2: TBY_HANDLE_FILE_INFORMATION @@ -685,17 +763,21 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and fi1.nFileIndexHigh == fi2.nFileIndexHigh and fi1.nFileIndexLow == fi2.nFileIndexLow - else: success = false - else: success = false + else: + lastErr = OSLastError() + success = false + else: + lastErr = OSLastError() + success = false discard CloseHandle(f1) discard CloseHandle(f2) - if not success: OSError() + if not success: OSError(lastErr) else: var a, b: TStat if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32: - OSError() + OSError(OSLastError()) else: result = a.st_dev == b.st_dev and a.st_ino == b.st_ino @@ -738,17 +820,17 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", when useWinUnicode: let s = newWideCString(source) let d = newWideCString(dest) - if CopyFileW(s, d, 0'i32) == 0'i32: OSError() + if CopyFileW(s, d, 0'i32) == 0'i32: OSError(OSLastError()) else: - if CopyFileA(source, dest, 0'i32) == 0'i32: OSError() + if CopyFileA(source, dest, 0'i32) == 0'i32: OSError(OSLastError()) else: # generic version of copyFile which works for any platform: const bufSize = 8000 # better for memory manager var d, s: TFile - if not open(s, source): OSError() + if not open(s, source): OSError(OSLastError()) if not open(d, dest, fmWrite): close(s) - OSError() + OSError(OSLastError()) var buf = alloc(bufsize) while True: var bytesread = readBuffer(s, buf, bufsize) @@ -758,7 +840,7 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", dealloc(buf) close(s) close(d) - OSError() + OSError(OSLastError()) if bytesread != bufSize: break dealloc(buf) close(s) @@ -767,7 +849,8 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", tags: [FReadIO, FWriteIO].} = ## Moves a file from `source` to `dest`. If this fails, `EOS` is raised. - if crename(source, dest) != 0'i32: OSError() + if crename(source, dest) != 0'i32: + raise newException(EOS, $strerror(errno)) when not defined(ENOENT): var ENOENT {.importc, header: "".}: cint @@ -775,7 +858,8 @@ when not defined(ENOENT): proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = ## Removes the `file`. If this fails, `EOS` is raised. This does not fail ## if the file never existed in the first place. - if cremove(file) != 0'i32 and errno != ENOENT: OSError() + if cremove(file) != 0'i32 and errno != ENOENT: + raise newException(EOS, $strerror(errno)) proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", tags: [FExecIO].} = @@ -907,14 +991,14 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} = indx = high(environment) when defined(unix): if cputenv(environment[indx]) != 0'i32: - OSError() + OSError(OSLastError()) else: when useWinUnicode: var k = newWideCString(key) var v = newWideCString(val) - if SetEnvironmentVariableW(k, v) == 0'i32: OSError() + if SetEnvironmentVariableW(k, v) == 0'i32: OSError(OSLastError()) else: - if SetEnvironmentVariableA(key, val) == 0'i32: OSError() + if SetEnvironmentVariableA(key, val) == 0'i32: OSError(OSLastError()) iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} = ## Iterate over all `environments variables`:idx:. In the first component @@ -1044,10 +1128,12 @@ proc rawRemoveDir(dir: string) = wrapUnary(res, RemoveDirectoryW, dir) else: var res = RemoveDirectoryA(dir) - if res == 0'i32 and GetLastError() != 3'i32 and - GetLastError() != 18'i32: OSError() + let lastError = OSLastError() + if res == 0'i32 and lastError.int32 != 3'i32 and + lastError.int32 != 18'i32 and lastError.int32 != 2'i32: + OSError(lastError) else: - if rmdir(dir) != 0'i32 and errno != ENOENT: OSError() + if rmdir(dir) != 0'i32 and errno != ENOENT: OSError(OSLastError()) proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ FWriteDir, FReadDir].} = @@ -1065,14 +1151,14 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ proc rawCreateDir(dir: string) = when defined(unix): if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST: - OSError() + OSError(OSLastError()) else: when useWinUnicode: wrapUnary(res, CreateDirectoryW, dir) else: var res = CreateDirectoryA(dir) if res == 0'i32 and GetLastError() != 183'i32: - OSError() + OSError(OSLastError()) proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = ## Creates the `directory`:idx: `dir`. @@ -1213,7 +1299,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {. ## permission is available in any case. when defined(posix): var a: TStat - if stat(filename, a) < 0'i32: OSError() + if stat(filename, a) < 0'i32: OSError(OSLastError()) result = {} if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead) if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite) @@ -1231,7 +1317,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {. wrapUnary(res, GetFileAttributesW, filename) else: var res = GetFileAttributesA(filename) - if res == -1'i32: OSError() + if res == -1'i32: OSError(OSLastError()) if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, fpOthersExec, fpOthersRead} @@ -1257,13 +1343,13 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {. if fpOthersWrite in permissions: p = p or S_IWOTH if fpOthersExec in permissions: p = p or S_IXOTH - if chmod(filename, p) != 0: OSError() + if chmod(filename, p) != 0: OSError(OSLastError()) else: when useWinUnicode: wrapUnary(res, GetFileAttributesW, filename) else: var res = GetFileAttributesA(filename) - if res == -1'i32: OSError() + if res == -1'i32: OSError(OSLastError()) if fpUserWrite in permissions: res = res and not FILE_ATTRIBUTE_READONLY else: @@ -1272,7 +1358,7 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {. wrapBinary(res2, SetFileAttributesW, filename, res) else: var res2 = SetFileAttributesA(filename, res) - if res2 == - 1'i32: OSError() + if res2 == - 1'i32: OSError(OSLastError()) proc inclFilePermissions*(filename: string, permissions: set[TFilePermission]) {. @@ -1448,7 +1534,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1", when defined(windows): var a: TWin32FindData var resA = findfirstFile(file, a) - if resA == -1: OSError() + if resA == -1: OSError(OSLastError()) result = rdFileSize(a) findclose(resA) else: @@ -1456,7 +1542,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1", if open(f, file): result = getFileSize(f) close(f) - else: OSError() + else: OSError(OSLastError()) proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} = ## Searches for `exe` in the current working directory and then diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index aa4fbe32d..48a559c4e 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -370,6 +370,7 @@ when defined(Windows) and not defined(useNimRtl): else: success = winlean.CreateProcessA(nil, cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo) + let lastError = OSLastError() if poParentStreams notin options: FileClose(si.hStdInput) @@ -379,7 +380,7 @@ when defined(Windows) and not defined(useNimRtl): if e != nil: dealloc(e) dealloc(cmdl) - if success == 0: OSError() + if success == 0: OSError(lastError) # Close the handle now so anyone waiting is woken: discard closeHandle(procInfo.hThread) result.FProcessHandle = procInfo.hProcess @@ -450,7 +451,7 @@ when defined(Windows) and not defined(useNimRtl): var res = winlean.CreateProcessA(nil, command, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo) if res == 0: - OSError() + OSError(OSLastError()) else: Process = ProcInfo.hProcess discard CloseHandle(ProcInfo.hThread) diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 603fd612b..33df72d38 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -231,7 +231,7 @@ when defined(ssl): if err == 0: raise newException(ESSL, "No error reported.") if err == -1: - OSError() + OSError(OSLastError()) var errStr = ErrErrorString(err, nil) raise newException(ESSL, $errStr) @@ -347,24 +347,23 @@ proc SocketError*(socket: TSocket, err: int = -1, async = false) = else: SSLError("Unknown Error") if err == -1 and not (when defined(ssl): socket.isSSL else: false): + let lastError = OSLastError() if async: when defined(windows): - # TODO: Test on Windows - var err = WSAGetLastError() - if err == WSAEWOULDBLOCK: + if lastError.int32 == WSAEWOULDBLOCK: return - else: OSError() + else: OSError(lastError) else: - if errno == EAGAIN or errno == EWOULDBLOCK: + if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK: return - else: OSError() - else: OSError() + else: OSError(lastError) + else: OSError(lastError) proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} = ## Marks ``socket`` as accepting connections. ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. - if listen(socket.fd, cint(backlog)) < 0'i32: OSError() + if listen(socket.fd, cint(backlog)) < 0'i32: OSError(OSLastError()) proc invalidIp4(s: string) {.noreturn, noinline.} = raise newException(EInvalidValue, "invalid ip4 address: " & s) @@ -403,7 +402,7 @@ template gaiNim(a, p, h, list: expr): stmt = var gaiResult = getAddrInfo(a, $p, addr(h), list) if gaiResult != 0'i32: when defined(windows): - OSError() + OSError(OSLastError()) else: OSError($gai_strerror(gaiResult)) @@ -423,7 +422,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. name.sin_addr.s_addr = sockets.htonl(INADDR_ANY) if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)), sizeof(name).TSockLen) < 0'i32: - OSError() + OSError(OSLastError()) else: var hints: TAddrInfo var aiList: ptr TAddrInfo = nil @@ -432,21 +431,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. hints.ai_protocol = toInt(IPPROTO_TCP) gaiNim(address, port, hints, aiList) if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrLen.TSockLen) < 0'i32: - OSError() - -when false: - proc bindAddr*(socket: TSocket, port = TPort(0)) = - ## binds a port number to a socket. - var name: Tsockaddr_in - when defined(Windows): - name.sin_family = int16(ord(AF_INET)) - else: - name.sin_family = posix.AF_INET - name.sin_port = sockets.htons(int16(port)) - name.sin_addr.s_addr = sockets.htonl(INADDR_ANY) - if bindSocket(cint(socket), cast[ptr TSockAddr](addr(name)), - sizeof(name).TSockLen) < 0'i32: - OSError() + OSError(OSLastError()) proc getSockName*(socket: TSocket): TPort = ## returns the socket's associated port number. @@ -460,7 +445,7 @@ proc getSockName*(socket: TSocket): TPort = var namelen = sizeof(name).TSockLen if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)), addr(namelen)) == -1'i32: - OSError() + OSError(OSLastError()) result = TPort(sockets.ntohs(name.sin_port)) template acceptAddrPlain(noClientRet, successRet: expr, @@ -472,26 +457,25 @@ template acceptAddrPlain(noClientRet, successRet: expr, addr(addrLen)) if sock < 0: - # TODO: Test on Windows. + let err = OSLastError() when defined(windows): - var err = WSAGetLastError() - if err == WSAEINPROGRESS: + if err.int32 == WSAEINPROGRESS: client = InvalidSocket address = "" when noClientRet.int == -1: return else: return noClientRet - else: OSError() + else: OSError(err) else: - if errno == EAGAIN or errno == EWOULDBLOCK: + if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: client = InvalidSocket address = "" when noClientRet.int == -1: return else: return noClientRet - else: OSError() + else: OSError(err) else: client.fd = sock client.isBuffered = server.isBuffered @@ -644,7 +628,7 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = var s = winlean.getservbyname(name, proto) else: var s = posix.getservbyname(name, proto) - if s == nil: OSError() + if s == nil: OSError(OSLastError()) result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) result.port = TPort(s.s_port) @@ -656,7 +640,7 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} = var s = winlean.getservbyport(ze(int16(port)).cint, proto) else: var s = posix.getservbyport(ze(int16(port)).cint, proto) - if s == nil: OSError() + if s == nil: OSError(OSLastError()) result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) result.port = TPort(s.s_port) @@ -670,7 +654,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} = when defined(windows): var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, cint(sockets.AF_INET)) - if s == nil: OSError() + if s == nil: OSError(OSLastError()) else: var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSockLen, cint(posix.AF_INET)) @@ -687,7 +671,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} = elif s.h_addrtype == posix.AF_INET6: result.addrType = AF_INET6 else: - OSError("unknown h_addrtype") + raise newException(EOS, "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) @@ -697,7 +681,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} = var s = winlean.gethostbyname(name) else: var s = posix.gethostbyname(name) - if s == nil: OSError() + if s == nil: OSError(OSLastError()) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) when defined(windows): @@ -708,7 +692,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} = elif s.h_addrtype == posix.AF_INET6: result.addrType = AF_INET6 else: - OSError("unknown h_addrtype") + raise newException(EOS, "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) @@ -719,7 +703,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {. var size = sizeof(res).TSockLen if getsockopt(socket.fd, cint(level), cint(optname), addr(res), addr(size)) < 0'i32: - OSError() + OSError(OSLastError()) result = int(res) proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {. @@ -728,7 +712,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {. var value = cint(optval) if setsockopt(socket.fd, cint(level), cint(optname), addr(value), sizeof(value).TSockLen) < 0'i32: - OSError() + OSError(OSLastError()) proc connect*(socket: TSocket, address: string, port = TPort(0), af: TDomain = AF_INET) {.tags: [FReadIO].} = @@ -746,15 +730,17 @@ proc connect*(socket: TSocket, address: string, port = TPort(0), gaiNim(address, port, hints, aiList) # try all possibilities: var success = false + var lastError: TOSErrorCode var it = aiList while it != nil: if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) == 0'i32: success = true break + else: lastError = OSLastError() it = it.ai_next freeaddrinfo(aiList) - if not success: OSError() + if not success: OSError(lastError) when defined(ssl): if socket.isSSL: @@ -807,6 +793,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0), gaiNim(name, port, hints, aiList) # try all possibilities: var success = false + var lastError: TOSErrorCode var it = aiList while it != nil: var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) @@ -814,22 +801,21 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0), success = true break else: - # TODO: Test on Windows. + lastError = OSLastError() when defined(windows): - var err = WSAGetLastError() # Windows EINTR doesn't behave same as POSIX. - if err == WSAEWOULDBLOCK: + if lastError.int32 == WSAEWOULDBLOCK: success = true break else: - if errno == EINTR or errno == EINPROGRESS: + if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS: success = true break it = it.ai_next freeaddrinfo(aiList) - if not success: OSError() + if not success: OSError(lastError) when defined(ssl): if socket.isSSL: socket.sslNoHandshake = true @@ -1110,7 +1096,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int, var s = @[socket] var startTime = epochTime() let selRet = select(s, timeout - int(waited * 1000.0)) - if selRet < 0: OSError() + if selRet < 0: OSError(OSLastError()) if selRet != 1: raise newException(ETimeout, "Call to '" & funcName & "' timed out.") waited += (epochTime() - startTime) @@ -1259,14 +1245,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {. var c: char discard waitFor(socket, waited, timeout, 1, "readLine") var n = recv(socket, addr(c), 1) - if n < 0: OSError() + if n < 0: OSError(OSLastError()) elif n == 0: return if c == '\r': discard waitFor(socket, waited, timeout, 1, "readLine") n = peekChar(socket, c) if n > 0 and c == '\L': discard recv(socket, addr(c), 1) - elif n <= 0: OSError() + elif n <= 0: OSError(OSLastError()) addNlIfEmpty() return elif c == '\L': @@ -1326,6 +1312,7 @@ proc readLineAsync*(socket: TSocket, while true: var c: char var n = recv(socket, addr(c), 1) + #echo(n) if n < 0: if line.len == 0: errorOrNone else: return ReadPartialLine elif n == 0: @@ -1352,7 +1339,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = var pos = 0 while true: var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1) - if bytesRead == -1: OSError() + if bytesRead == -1: OSError(OSLastError()) setLen(result.string, pos + bytesRead) if bytesRead != bufSize-1: break # increase capacity: @@ -1364,7 +1351,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = while true: var bytesRead = recv(socket, cstring(buf), bufSize-1) # Error - if bytesRead == -1: OSError() + if bytesRead == -1: OSError(OSLastError()) buf[bytesRead] = '\0' # might not be necessary setLen(buf, bytesRead) @@ -1421,16 +1408,15 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {. else: SSLError("Unknown Error") if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false): + let err = OSLastError() when defined(windows): - # TODO: Test on Windows - var err = WSAGetLastError() - if err == WSAEWOULDBLOCK: + if err.int32 == WSAEWOULDBLOCK: return False - else: OSError() + else: OSError(err) else: - if errno == EAGAIN or errno == EWOULDBLOCK: + if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: return False - else: OSError() + else: OSError(err) setLen(s.string, pos + bytesRead) if bytesRead != bufSize-1: break @@ -1475,16 +1461,15 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int, result = true var callRes = recvFrom(socket, data, length, address, port, flags) if callRes < 0: + let err = OSLastError() when defined(windows): - # TODO: Test on Windows - var err = WSAGetLastError() - if err == WSAEWOULDBLOCK: + if err.int32 == WSAEWOULDBLOCK: return False - else: OSError() + else: OSError(err) else: - if errno == EAGAIN or errno == EWOULDBLOCK: + if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: return False - else: OSError() + else: OSError(err) proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} = ## skips all the data that is pending for the socket @@ -1531,7 +1516,7 @@ proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} = if socket.isSSL: SSLError() - OSError() + OSError(OSLastError()) proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} = ## sends data to a non-blocking socket. @@ -1561,16 +1546,15 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} = else: return if result == -1: + let err = OSLastError() when defined(windows): - var err = WSAGetLastError() - # TODO: Test on windows. - if err == WSAEINPROGRESS: + if err.int32 == WSAEINPROGRESS: return 0 - else: OSError() + else: OSError(err) else: - if errno == EAGAIN or errno == EWOULDBLOCK: + if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: return 0 - else: OSError() + else: OSError(err) proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} = @@ -1626,15 +1610,15 @@ proc setBlocking(s: TSocket, blocking: bool) = when defined(Windows): var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1: - OSError() + OSError(OSLastError()) else: # BSD sockets var x: int = fcntl(s.fd, F_GETFL, 0) if x == -1: - OSError() + OSError(OSLastError()) else: var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK if fcntl(s.fd, F_SETFL, mode) == -1: - OSError() + OSError(OSLastError()) s.nonblocking = not blocking proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int, @@ -1665,6 +1649,6 @@ proc getFD*(socket: TSocket): cint = return socket.fd when defined(Windows): var wsa: TWSADATA - if WSAStartup(0x0101'i16, wsa) != 0: OSError() + if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index fa4925ee6..d448d2b10 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -489,7 +489,7 @@ proc FD_SET*(Socket: TWinSocket, FDSet: var TFDSet) = proc FD_ZERO*(FDSet: var TFDSet) = FDSet.fd_count = 0 -proc WSAStartup*(wVersionRequired: int16, WSData: var TWSAData): cint {. +proc WSAStartup*(wVersionRequired: int16, WSData: ptr TWSAData): cint {. stdcall, importc: "WSAStartup", dynlib: ws2dll.} proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo, From 9686d92bfc47cf9f2cf33e982c0f8f8113981faa Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 26 Jun 2013 00:00:07 +0100 Subject: [PATCH 64/83] Fixed OSError deprecation warnings. --- lib/pure/osproc.nim | 34 +++++++++++++++++----------------- lib/pure/terminal.nim | 28 ++++++++++++++-------------- web/news.txt | 5 ++++- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 48a559c4e..b9bde73bc 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -264,7 +264,7 @@ when defined(Windows) and not defined(useNimRtl): # TRUE and zero bytes returned (EOF). # TRUE and n (>0) bytes returned (good data). # FALSE and bytes returned undefined (system error). - if a == 0 and br != 0: OSError() + if a == 0 and br != 0: OSError(OSLastError()) s.atTheEnd = br < bufLen result = br @@ -272,7 +272,7 @@ when defined(Windows) and not defined(useNimRtl): var s = PFileHandleStream(s) var bytesWritten: int32 var a = winlean.writeFile(s.handle, buffer, bufLen.cint, bytesWritten, nil) - if a == 0: OSError() + if a == 0: OSError(OSLastError()) proc newFileHandleStream(handle: THandle): PFileHandleStream = new(result) @@ -313,7 +313,7 @@ when defined(Windows) and not defined(useNimRtl): piInheritablePipe.lpSecurityDescriptor = nil piInheritablePipe.Binherithandle = 1 if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32: - OSError() + OSError(OSLastError()) proc fileClose(h: THandle) {.inline.} = if h > 4: discard CloseHandle(h) @@ -474,7 +474,7 @@ when defined(Windows) and not defined(useNimRtl): of WAIT_TIMEOUT: return 0 of WAIT_FAILED: - OSError() + OSError(OSLastError()) else: var i = ret - WAIT_OBJECT_0 readfds.del(i) @@ -532,7 +532,7 @@ elif not defined(useNimRtl): if poParentStreams notin options: if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32 or pipe(p_stderr) != 0'i32: - OSError() + OSError(OSLastError()) var pid: TPid when defined(posix_spawn) and not defined(useFork): @@ -540,7 +540,7 @@ elif not defined(useNimRtl): var fops: Tposix_spawn_file_actions template chck(e: expr) = - if e != 0'i32: OSError() + if e != 0'i32: OSError(OSLastError()) chck posix_spawn_file_actions_init(fops) chck posix_spawnattr_init(attr) @@ -585,20 +585,20 @@ elif not defined(useNimRtl): else: Pid = fork() - if Pid < 0: OSError() + if Pid < 0: OSError(OSLastError()) if pid == 0: ## child process: if poParentStreams notin options: discard close(p_stdin[writeIdx]) - if dup2(p_stdin[readIdx], readIdx) < 0: OSError() + if dup2(p_stdin[readIdx], readIdx) < 0: OSError(OSLastError()) discard close(p_stdout[readIdx]) - if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError() + if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError(OSLastError()) discard close(p_stderr[readIdx]) if poStdErrToStdOut in options: - if dup2(p_stdout[writeIdx], 2) < 0: OSError() + if dup2(p_stdout[writeIdx], 2) < 0: OSError(OSLastError()) else: - if dup2(p_stderr[writeIdx], 2) < 0: OSError() + if dup2(p_stderr[writeIdx], 2) < 0: OSError(OSLastError()) # Create a new process group if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno)) @@ -653,10 +653,10 @@ elif not defined(useNimRtl): discard close(p.errorHandle) proc suspend(p: PProcess) = - if kill(-p.id, SIGSTOP) != 0'i32: OSError() + if kill(-p.id, SIGSTOP) != 0'i32: OSError(OSLastError()) proc resume(p: PProcess) = - if kill(-p.id, SIGCONT) != 0'i32: OSError() + if kill(-p.id, SIGCONT) != 0'i32: OSError(OSLastError()) proc running(p: PProcess): bool = var ret = waitPid(p.id, p.exitCode, WNOHANG) @@ -666,8 +666,8 @@ elif not defined(useNimRtl): proc terminate(p: PProcess) = if kill(-p.id, SIGTERM) == 0'i32: if p.running(): - if kill(-p.id, SIGKILL) != 0'i32: OSError() - else: OSError() + if kill(-p.id, SIGKILL) != 0'i32: OSError(OSLastError()) + else: OSError(OSLastError()) proc waitForExit(p: PProcess, timeout: int = -1): int = #if waitPid(p.id, p.exitCode, 0) == int(p.id): @@ -677,7 +677,7 @@ elif not defined(useNimRtl): if p.exitCode != -3: return p.exitCode if waitPid(p.id, p.exitCode, 0) < 0: p.exitCode = -3 - OSError() + OSError(OSLastError()) result = int(p.exitCode) shr 8 proc peekExitCode(p: PProcess): int = @@ -691,7 +691,7 @@ elif not defined(useNimRtl): proc createStream(stream: var PStream, handle: var TFileHandle, fileMode: TFileMode) = var f: TFile - if not open(f, handle, fileMode): OSError() + if not open(f, handle, fileMode): OSError(OSLastError()) stream = newFileStream(f) proc inputStream(p: PProcess): PStream = diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 9b69bbaa4..501184aca 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -27,11 +27,11 @@ when defined(windows): var hTemp = GetStdHandle(STD_OUTPUT_HANDLE) if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: - OSError() + OSError(OSLastError()) proc getCursorPos(): tuple [x,y: int] = var c: TCONSOLE_SCREEN_BUFFER_INFO - if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError() + if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError(OSLastError()) return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) proc getAttributes(): int16 = @@ -51,7 +51,7 @@ proc setCursorPos*(x, y: int) = var c: TCoord c.x = int16(x) c.y = int16(y) - if SetConsoleCursorPosition(conHandle, c) == 0: OSError() + if SetConsoleCursorPosition(conHandle, c) == 0: OSError(OSLastError()) else: stdout.write("\e[" & $y & ';' & $x & 'f') @@ -61,10 +61,10 @@ proc setCursorXPos*(x: int) = when defined(windows): var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError()) var origin = scrbuf.dwCursorPosition origin.x = int16(x) - if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() + if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError()) else: stdout.write("\e[" & $x & 'G') @@ -75,10 +75,10 @@ when defined(windows): when defined(windows): var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError()) var origin = scrbuf.dwCursorPosition origin.y = int16(y) - if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() + if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError()) else: nil @@ -155,18 +155,18 @@ proc EraseLine* = var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO var numwrote: DWORD var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError()) var origin = scrbuf.dwCursorPosition origin.x = 0'i16 - if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() + if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError()) var ht = scrbuf.dwSize.Y - origin.Y var wt = scrbuf.dwSize.X - origin.X if FillConsoleOutputCharacter(hStdout,' ', ht*wt, origin, addr(numwrote)) == 0: - OSError() + OSError(OSLastError()) if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, scrbuf.dwCursorPosition, addr(numwrote)) == 0: - OSError() + OSError(OSLastError()) else: stdout.write("\e[2K") setCursorXPos(0) @@ -178,14 +178,14 @@ proc EraseScreen* = var numwrote: DWORD var origin: TCoord # is inititalized to 0, 0 var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError()) if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y, origin, addr(numwrote)) == 0: - OSError() + OSError(OSLastError()) if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, scrbuf.dwSize.X * scrbuf.dwSize.Y, origin, addr(numwrote)) == 0: - OSError() + OSError(OSLastError()) setCursorXPos(0) else: stdout.write("\e[2J") diff --git a/web/news.txt b/web/news.txt index fdbb156a7..5ef96368f 100644 --- a/web/news.txt +++ b/web/news.txt @@ -20,7 +20,10 @@ Changes affecting backwards compatibility - The scoping rules for the ``if`` statement changed for better interaction with the new syntactic construct ``(;)``. - +- ``OSError`` family of procedures has been deprecated. Procedures with the same + name but which take different parameters have been introduced. These procs now + require an error code to be passed to them. This error code can be retrieved + using the new ``OSLastError`` proc. Compiler Additions From a674c039fb959166681f13f809670807b89e0b41 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 01:04:56 +0200 Subject: [PATCH 65/83] fixed a long standing xml parser bug; html parser bugfixes + speed improvements --- compiler/ast.nim | 3 + compiler/sempass2.nim | 9 +- lib/pure/htmlparser.nim | 286 ++++++++++++++++++++++++++++++---------- lib/pure/parsexml.nim | 19 ++- lib/pure/xmltree.nim | 10 ++ lib/system/syslocks.nim | 2 +- todo.txt | 7 +- 7 files changed, 252 insertions(+), 84 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index e4f7f6d98..02396f0bb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -283,6 +283,9 @@ const sfNoForward* = sfRegister # forward declarations are not required (per module) + sfNoRoot* = sfBorrow # a local variable is provably no root so it doesn't + # require RC ops + const # getting ready for the future expr/stmt merge nkWhen* = nkWhenStmt diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 1fc74d31c..f2e4fb02e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -57,14 +57,6 @@ discard """ c() --> we need a stack of scopes for this analysis - - - Effect tracking: - - We track the effects per proc; forward declarations and indirect calls cause - problems: Forward declarations are computed lazily (we do this pass after - a whole module) and indirect calls are assumed the worst, unless they have - an effect annotation. """ type @@ -75,6 +67,7 @@ type owner: PSym init: seq[int] # list of initialized variables guards: TModel # nested guards + locked: seq[PNode] # locked locations PEffects = var TEffects proc isLocalVar(a: PEffects, s: PSym): bool = diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index ad952fc41..c94170803 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2010 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -35,8 +35,12 @@ type tagAddress, ## the HTML ``address`` element tagApplet, ## the deprecated HTML ``applet`` element tagArea, ## the HTML ``area`` element + tagArticle, ## the HTML ``article`` element + tagAside, ## the HTML ``aside`` element + tagAudio, ## the HTML ``audio`` element tagB, ## the HTML ``b`` element tagBase, ## the HTML ``base`` element + tagBdi, ## the HTML ``bdi`` element tagBdo, ## the deprecated HTML ``dbo`` element tagBasefont, ## the deprecated HTML ``basefont`` element tagBig, ## the HTML ``big`` element @@ -44,22 +48,31 @@ type tagBody, ## the HTML ``body`` element tagBr, ## the HTML ``br`` element tagButton, ## the HTML ``button`` element + tagCanvas, ## the HTML ``canvas`` element tagCaption, ## the HTML ``caption`` element tagCenter, ## the deprecated HTML ``center`` element tagCite, ## the HTML ``cite`` element tagCode, ## the HTML ``code`` element tagCol, ## the HTML ``col`` element tagColgroup, ## the HTML ``colgroup`` element + tagCommand, ## the HTML ``command`` element + tagDatalist, ## the HTML ``datalist`` element tagDd, ## the HTML ``dd`` element tagDel, ## the HTML ``del`` element + tagDetails, ## the HTML ``details`` element tagDfn, ## the HTML ``dfn`` element + tagDialog, ## the HTML ``dialog`` element tagDiv, ## the HTML ``div`` element tagDir, ## the deprecated HTLM ``dir`` element tagDl, ## the HTML ``dl`` element tagDt, ## the HTML ``dt`` element tagEm, ## the HTML ``em`` element + tagEmbed, ## the HTML ``embed`` element tagFieldset, ## the HTML ``fieldset`` element + tagFigcaption, ## the HTML ``figcaption`` element + tagFigure, ## the HTML ``figure`` element tagFont, ## the deprecated HTML ``font`` element + tagFooter, ## the HTML ``footer`` element tagForm, ## the HTML ``form`` element tagFrame, ## the HTML ``frame`` element tagFrameset, ## the deprecated HTML ``frameset`` element @@ -70,6 +83,8 @@ type tagH5, ## the HTML ``h5`` element tagH6, ## the HTML ``h6`` element tagHead, ## the HTML ``head`` element + tagHeader, ## the HTML ``header`` element + tagHgroup, ## the HTML ``hgroup`` element tagHtml, ## the HTML ``html`` element tagHr, ## the HTML ``hr`` element tagI, ## the HTML ``i`` element @@ -79,13 +94,17 @@ type tagIns, ## the HTML ``ins`` element tagIsindex, ## the deprecated HTML ``isindex`` element tagKbd, ## the HTML ``kbd`` element + tagKeygen, ## the HTML ``keygen`` element tagLabel, ## the HTML ``label`` element tagLegend, ## the HTML ``legend`` element tagLi, ## the HTML ``li`` element tagLink, ## the HTML ``link`` element tagMap, ## the HTML ``map`` element + tagMark, ## the HTML ``mark`` element tagMenu, ## the deprecated HTML ``menu`` element tagMeta, ## the HTML ``meta`` element + tagMeter, ## the HTML ``meter`` element + tagNav, ## the HTML ``nav`` element tagNobr, ## the deprecated HTML ``nobr`` element tagNoframes, ## the deprecated HTML ``noframes`` element tagNoscript, ## the HTML ``noscript`` element @@ -93,20 +112,28 @@ type tagOl, ## the HTML ``ol`` element tagOptgroup, ## the HTML ``optgroup`` element tagOption, ## the HTML ``option`` element + tagOutput, ## the HTML ``output`` element tagP, ## the HTML ``p`` element tagParam, ## the HTML ``param`` element tagPre, ## the HTML ``pre`` element + tagProgress, ## the HTML ``progress`` element tagQ, ## the HTML ``q`` element + tagRp, ## the HTML ``rp`` element + tagRt, ## the HTML ``rt`` element + tagRuby, ## the HTML ``ruby`` element tagS, ## the deprecated HTML ``s`` element tagSamp, ## the HTML ``samp`` element tagScript, ## the HTML ``script`` element + tagSection, ## the HTML ``section`` element tagSelect, ## the HTML ``select`` element tagSmall, ## the HTML ``small`` element + tagSource, ## the HTML ``source`` element tagSpan, ## the HTML ``span`` element tagStrike, ## the deprecated HTML ``strike`` element tagStrong, ## the HTML ``strong`` element tagStyle, ## the HTML ``style`` element tagSub, ## the HTML ``sub`` element + tagSummary, ## the HTML ``summary`` element tagSup, ## the HTML ``sup`` element tagTable, ## the HTML ``table`` element tagTbody, ## the HTML ``tbody`` element @@ -115,37 +142,46 @@ type tagTfoot, ## the HTML ``tfoot`` element tagTh, ## the HTML ``th`` element tagThead, ## the HTML ``thead`` element + tagTime, ## the HTML ``time`` element tagTitle, ## the HTML ``title`` element tagTr, ## the HTML ``tr`` element + tagTrack, ## the HTML ``track`` element tagTt, ## the HTML ``tt`` element tagU, ## the deprecated HTML ``u`` element tagUl, ## the HTML ``ul`` element - tagVar ## the HTML ``var`` element + tagVar, ## the HTML ``var`` element + tagVideo, ## the HTML ``video`` element + tagWbr ## the HTML ``wbr`` element const tagToStr* = [ - "a", "abbr", "acronym", "address", "applet", "area", - "b", "base", "basefont", "bdo", "big", "blockquote", "body", - "br", "button", "caption", "center", "cite", "code", - "col", "colgroup", "dd", "del", "dfn", "div", - "dir", "dl", "dt", "em", "fieldset", "font", + "a", "abbr", "acronym", "address", "applet", "area", "article", + "aside", "audio", + "b", "base", "basefont", "bdi", "bdo", "big", "blockquote", "body", + "br", "button", "canvas", "caption", "center", "cite", "code", + "col", "colgroup", "command", + "datalist", "dd", "del", "details", "dfn", "dialog", "div", + "dir", "dl", "dt", "em", "embed", "fieldset", + "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "h1", "h2", "h3", - "h4", "h5", "h6", "head", "html", "hr", + "h4", "h5", "h6", "head", "header", "hgroup", "html", "hr", "i", "iframe", "img", "input", "ins", "isindex", - "kbd", "label", "legend", "li", "link", "map", - "menu", "meta", "nobr", "noframes", "noscript", "object", "ol", - "optgroup", "option", "p", "param", "pre", "q", - "s", "samp", "script", "select", "small", "span", - "strike", "strong", "style", "sub", "sup", "table", - "tbody", "td", "textarea", "tfoot", "th", "thead", - "title", "tr", "tt", "u", "ul", "var"] + "kbd", "keygen", "label", "legend", "li", "link", "map", "mark", + "menu", "meta", "meter", "nav", "nobr", "noframes", "noscript", + "object", "ol", + "optgroup", "option", "output", "p", "param", "pre", "progress", "q", + "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", + "source", "span", "strike", "strong", "style", + "sub", "summary", "sup", "table", + "tbody", "td", "textarea", "tfoot", "th", "thead", "time", + "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"] InlineTags* = {tagA, tagAbbr, tagAcronym, tagApplet, tagB, tagBasefont, tagBdo, tagBig, tagBr, tagButton, tagCite, tagCode, tagDel, tagDfn, tagEm, tagFont, tagI, tagImg, tagIns, tagInput, tagIframe, tagKbd, tagLabel, tagMap, tagObject, tagQ, tagSamp, tagScript, tagSelect, tagSmall, tagSpan, tagStrong, tagSub, tagSup, tagTextarea, tagTt, tagVar, tagApplet, tagBasefont, tagFont, tagIframe, tagU, tagS, - tagStrike} + tagStrike, tagWbr} BlockTags* = {tagAddress, tagBlockquote, tagCenter, tagDel, tagDir, tagDiv, tagDl, tagFieldset, tagForm, tagH1, tagH2, tagH3, tagH4, tagH5, tagH6, tagHr, tagIns, tagIsindex, tagMenu, tagNoframes, tagNoscript, @@ -153,7 +189,7 @@ const tagMenu, tagNoframes} SingleTags* = {tagArea, tagBase, tagBasefont, tagBr, tagCol, tagFrame, tagHr, tagImg, tagIsindex, - tagLink, tagMeta, tagParam} + tagLink, tagMeta, tagParam, tagWbr} Entities = [ ("nbsp", 0x00A0), ("iexcl", 0x00A1), ("cent", 0x00A2), ("pound", 0x00A3), @@ -228,28 +264,149 @@ const ("loz", 0x25CA), ("spades", 0x2660), ("clubs", 0x2663), ("hearts", 0x2665), ("diams", 0x2666)] -proc binaryStrSearch(x: openarray[string], y: string): int = - ## XXX put this into the library somewhere! - var a = 0 - var b = len(x) - 1 - while a <= b: - var mid = (a + b) div 2 - var c = cmp(x[mid], y) - if c < 0: a = mid + 1 - elif c > 0: b = mid - 1 - else: return mid - result = - 1 +proc allLower(s: string): bool = + for c in s: + if c < 'a' or c > 'z': return false + return true + +proc toHtmlTag(s: string): THtmlTag = + case s + of "a": tagA + of "abbr": tagAbbr + of "acronym": tagAcronym + of "address": tagAddress + of "applet": tagApplet + of "area": tagArea + of "article": tagArticle + of "aside": tagAside + of "audio": tagAudio + of "b": tagB + of "base": tagBase + of "basefont": tagBasefont + of "bdi": tagBdi + of "bdo": tagBdo + of "big": tagBig + of "blockquote": tagBlockquote + of "body": tagBody + of "br": tagBr + of "button": tagButton + of "canvas": tagCanvas + of "caption": tagCaption + of "center": tagCenter + of "cite": tagCite + of "code": tagCode + of "col": tagCol + of "colgroup": tagColgroup + of "command": tagCommand + of "datalist": tagDatalist + of "dd": tagDd + of "del": tagDel + of "details": tagDetails + of "dfn": tagDfn + of "dialog": tagDialog + of "div": tagDiv + of "dir": tagDir + of "dl": tagDl + of "dt": tagDt + of "em": tagEm + of "embed": tagEmbed + of "fieldset": tagFieldset + of "figcaption": tagFigcaption + of "figure": tagFigure + of "font": tagFont + of "footer": tagFooter + of "form": tagForm + of "frame": tagFrame + of "frameset": tagFrameset + of "h1": tagH1 + of "h2": tagH2 + of "h3": tagH3 + of "h4": tagH4 + of "h5": tagH5 + of "h6": tagH6 + of "head": tagHead + of "header": tagHeader + of "hgroup": tagHgroup + of "html": tagHtml + of "hr": tagHr + of "i": tagI + of "iframe": tagIframe + of "img": tagImg + of "input": tagInput + of "ins": tagIns + of "isindex": tagIsindex + of "kbd": tagKbd + of "keygen": tagKeygen + of "label": tagLabel + of "legend": tagLegend + of "li": tagLi + of "link": tagLink + of "map": tagMap + of "mark": tagMark + of "menu": tagMenu + of "meta": tagMeta + of "meter": tagMeter + of "nav": tagNav + of "nobr": tagNobr + of "noframes": tagNoframes + of "noscript": tagNoscript + of "object": tagObject + of "ol": tagOl + of "optgroup": tagOptgroup + of "option": tagOption + of "output": tagOutput + of "p": tagP + of "param": tagParam + of "pre": tagPre + of "progress": tagProgress + of "q": tagQ + of "rp": tagRp + of "rt": tagRt + of "ruby": tagRuby + of "s": tagS + of "samp": tagSamp + of "script": tagScript + of "section": tagSection + of "select": tagSelect + of "small": tagSmall + of "source": tagSource + of "span": tagSpan + of "strike": tagStrike + of "strong": tagStrong + of "style": tagStyle + of "sub": tagSub + of "summary": tagSummary + of "sup": tagSup + of "table": tagTable + of "tbody": tagTbody + of "td": tagTd + of "textarea": tagTextarea + of "tfoot": tagTfoot + of "th": tagTh + of "thead": tagThead + of "time": tagTime + of "title": tagTitle + of "tr": tagTr + of "track": tagTrack + of "tt": tagTt + of "u": tagU + of "ul": tagUl + of "var": tagVar + of "video": tagVideo + of "wbr": tagWbr + else: tagUnknown proc htmlTag*(n: PXmlNode): THtmlTag = ## gets `n`'s tag as a ``THtmlTag``. if n.clientData == 0: - n.clientData = binaryStrSearch(tagToStr, n.tag)+1 + n.clientData = toHtmlTag(n.tag).ord result = THtmlTag(n.clientData) proc htmlTag*(s: string): THtmlTag = ## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is ## returned. - result = THtmlTag(binaryStrSearch(tagToStr, s.toLower)+1) + let s = if allLower(s): s else: s.toLower + result = toHtmlTag(s) proc entityToUtf8*(entity: string): string = ## converts an HTML entity name like ``Ü`` to its UTF-8 equivalent. @@ -267,11 +424,13 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode proc expected(x: var TXmlParser, n: PXmlNode): string = result = errorMsg(x, " expected") +template elemName(x: expr): expr = rawData(x) + proc untilElementEnd(x: var TXmlParser, result: PXmlNode, errors: var seq[string]) = # we parsed e.g. ``
`` and don't really expect a ``
``: if result.htmlTag in singleTags: - if x.kind != xmlElementEnd or cmpIgnoreCase(x.elementName, result.tag) != 0: + if x.kind != xmlElementEnd or cmpIgnoreCase(x.elemName, result.tag) != 0: return while true: case x.kind @@ -279,28 +438,29 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode, case result.htmlTag of tagLi, tagP, tagDt, tagDd, tagInput, tagOption: # some tags are common to have no ````, like ``
  • ``: - if htmlTag(x.elementName) in {tagLi, tagP, tagDt, tagDd, tagInput, - tagOption}: + if htmlTag(x.elemName) in {tagLi, tagP, tagDt, tagDd, tagInput, + tagOption}: errors.add(expected(x, result)) break - when false: - if htmlTag(x.elementName) notin InlineTags: - errors.add(expected(x, result)) - break - of tagTr, tagTd, tagTh, tagTfoot, tagThead: - if htmlTag(x.elementName) in {tagTr, tagTd, tagTh, tagTfoot, tagThead}: + of tagTd, tagTh, tagTfoot, tagThead: + if htmlTag(x.elemName) in {tagTr, tagTd, tagTh, tagTfoot, tagThead}: + errors.add(expected(x, result)) + break + of tagTr: + if htmlTag(x.elemName) == tagTr: errors.add(expected(x, result)) break of tagOptgroup: - if htmlTag(x.elementName) in {tagOption, tagOptgroup}: + if htmlTag(x.elemName) in {tagOption, tagOptgroup}: errors.add(expected(x, result)) break else: nil result.addNode(parse(x, errors)) of xmlElementEnd: - if cmpIgnoreCase(x.elementName, result.tag) == 0: + if cmpIgnoreCase(x.elemName, result.tag) == 0: next(x) else: + echo "5; expected: ", result.htmltag, " ", x.elemName errors.add(expected(x, result)) # do not skip it here! break @@ -313,10 +473,10 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode, proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = case x.kind of xmlComment: - result = newComment(x.charData) + result = newComment(x.rawData) next(x) of xmlCharData, xmlWhitespace: - result = newText(x.charData) + result = newText(x.rawData) next(x) of xmlPI, xmlSpecial: # we just ignore processing instructions for now @@ -325,19 +485,19 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = errors.add(errorMsg(x)) next(x) of xmlElementStart: - result = newElement(x.elementName.toLower) + result = newElement(x.elemName.toLower) next(x) untilElementEnd(x, result, errors) of xmlElementEnd: - errors.add(errorMsg(x, "unexpected ending tag: " & x.elementName)) + errors.add(errorMsg(x, "unexpected ending tag: " & x.elemName)) of xmlElementOpen: - result = newElement(x.elementName.toLower) + result = newElement(x.elemName.toLower) next(x) result.attrs = newStringTable() while true: case x.kind of xmlAttribute: - result.attrs[x.attrKey] = x.attrValue + result.attrs[x.rawData] = x.rawData2 next(x) of xmlElementClose: next(x) @@ -355,10 +515,10 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = errors.add(errorMsg(x, " expected")) next(x) of xmlCData: - result = newCData(x.charData) + result = newCData(x.rawData) next(x) of xmlEntity: - var u = entityToUtf8(x.entityName) + var u = entityToUtf8(x.rawData) if u.len != 0: result = newText(u) next(x) of xmlEof: nil @@ -372,15 +532,17 @@ proc parseHtml*(s: PStream, filename: string, next(x) # skip the DOCTYPE: if x.kind == xmlSpecial: next(x) - result = parse(x, errors) - if x.kind != xmlEof: - errors.add(errorMsg(x, "EOF expected")) + + result = newElement("document") + result.addNode(parse(x, errors)) + #if x.kind != xmlEof: + # errors.add(errorMsg(x, "EOF expected")) while x.kind != xmlEof: var oldPos = x.bufpos # little hack to see if we made any progess result.addNode(parse(x, errors)) - if x.bufpos == oldPos: + if x.bufpos == oldPos: # force progress! - next(x) + next(x) close(x) proc parseHtml*(s: PStream): PXmlNode = @@ -400,22 +562,9 @@ proc loadHtml*(path: string, errors: var seq[string]): PXmlNode = proc loadHtml*(path: string): PXmlNode = ## Loads and parses HTML from file specified by ``path``, and returns ## a ``PXmlNode``. All parsing errors are ignored. - var errors: seq[string] = @[] + var errors: seq[string] = @[] result = loadHtml(path, errors) -when true: - nil -else: - proc checkHtmlAux(n: PXmlNode, errors: var seq[string]) = - nil - - proc checkHtmlStructure*(n: PXmlNode, errors: var seq[string]) = - ## checks the HTML structure after parsing for other errors like - ## a ``

    `` element within a ``

    `` element. - if n == nil or n.htmlTag != tagHtml: - errors.add(" tag expected") - checkHtmlAux(n, errors) - when isMainModule: import os @@ -429,4 +578,3 @@ when isMainModule: f.close() else: quit("cannot write test.txt") - diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index c49986087..667b8aed6 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -131,6 +131,10 @@ proc close*(my: var TXmlParser) {.inline.} = ## closes the parser `my` and its associated input stream. lexbase.close(my) +proc kind*(my: TXmlParser): TXmlEventKind {.inline.} = + ## returns the current event type for the XML parser + return my.kind + proc charData*(my: TXmlParser): string {.inline.} = ## returns the character data for the events: ``xmlCharData``, ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial`` @@ -138,10 +142,6 @@ proc charData*(my: TXmlParser): string {.inline.} = xmlSpecial}) return my.a -proc kind*(my: TXmlParser): TXmlEventKind {.inline.} = - ## returns the current event type for the XML parser - return my.kind - proc elementName*(my: TXmlParser): string {.inline.} = ## returns the element name for the events: ``xmlElementStart``, ## ``xmlElementEnd``, ``xmlElementOpen`` @@ -173,6 +173,16 @@ proc PIRest*(my: TXmlParser): string {.inline.} = assert(my.kind == xmlPI) return my.b +proc rawData*(my: TXmlParser): string {.inline.} = + ## returns the underlying 'data' string by reference. + ## This is only used for speed hacks. + shallowCopy(result, my.a) + +proc rawData2*(my: TXmlParser): string {.inline.} = + ## returns the underlying second 'data' string by reference. + ## This is only used for speed hacks. + shallowCopy(result, my.b) + proc getColumn*(my: TXmlParser): int {.inline.} = ## get the current column the parser has arrived at. result = getColNumber(my, my.bufPos) @@ -439,6 +449,7 @@ proc parseTag(my: var TXmlParser) = if my.buf[my.bufpos] == '/' and my.buf[my.bufpos+1] == '>': inc(my.bufpos, 2) my.state = stateEmptyElementTag + my.c = nil elif my.buf[my.bufpos] == '>': inc(my.bufpos) else: diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index e3fc27237..d5821d8f3 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -71,6 +71,16 @@ proc text*(n: PXmlNode): string {.inline.} = assert n.k in {xnText, xnComment, xnCData, xnEntity} result = n.fText +proc rawText*(n: PXmlNode): string {.inline.} = + ## returns the underlying 'text' string by reference. + ## This is only used for speed hacks. + shallowCopy(result, n.fText) + +proc rawTag*(n: PXmlNode): string {.inline.} = + ## returns the underlying 'tag' string by reference. + ## This is only used for speed hacks. + shallowCopy(result, n.fTag) + proc innerText*(n: PXmlNode): string = ## gets the inner text of `n`. `n` has to be an ``xnElement`` node. Only ## ``xnText`` and ``xnEntity`` nodes are considered part of `n`'s inner text, diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index 4619eaddb..4d81dee01 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -19,7 +19,7 @@ when defined(Windows): OwningThread: int LockSemaphore: int Reserved: int32 - + TSysCond = THandle proc InitSysLock(L: var TSysLock) {.stdcall, noSideEffect, diff --git a/todo.txt b/todo.txt index 4bdd004cd..7f00d8172 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,6 @@ version 0.9.4 ============= - make 'bind' default for templates and introduce 'mixin' -- prove array accesses - special rule for ``[]=`` - ``=`` should be overloadable; requires specialization for ``=``; general lift mechanism in the compiler is already implemented for 'fields' @@ -16,8 +15,11 @@ version 0.9.4 Bugs ==== +- simple closure iterator doesn't work +- marshal doesn't support unsigned types +- sfAnon is not respected in renderer - docgen: sometimes effects are listed twice -- 'result' is not properly cleaned for NRVO +- 'result' is not properly cleaned for NRVO --> use uninit checking instead - sneaking with qualifiedLookup() is really broken! - aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' -- use a qualifier @@ -140,6 +142,7 @@ Not essential for 1.0.0 - implement closures that support nesting of *procs* > 1 - object constructors: static check for fields if discriminator is known at compile time +- prove array accesses Optimizations From 82ceca071710d693e1611505c9dfff7aee375045 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 07:43:04 +0200 Subject: [PATCH 66/83] fixes #492 --- lib/pure/htmlparser.nim | 2 ++ lib/pure/xmlparser.nim | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index c94170803..e1a0096f6 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -544,6 +544,8 @@ proc parseHtml*(s: PStream, filename: string, # force progress! next(x) close(x) + if result.len == 1: + result = result[0] proc parseHtml*(s: PStream): PXmlNode = ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 8c5c5f5ab..355053e0d 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -110,11 +110,9 @@ proc parseXml*(s: PStream, filename: string, of xmlElementOpen, xmlElementStart: result = parse(x, errors) break - of xmlComment, xmlWhitespace: nil # just skip it + of xmlComment, xmlWhitespace, xmlSpecial: nil # just skip it of xmlError: errors.add(errorMsg(x)) - of xmlSpecial: - errors.add(errorMsg(x, " expected")) else: errors.add(errorMsg(x, " expected")) break From ed56b8c17372328726eb75542d6a873b279864e4 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 08:06:55 +0200 Subject: [PATCH 67/83] fixes #442 --- compiler/sigmatch.nim | 31 +++++++++++++++++--------- tests/reject/tnicerrorforsymchoice.nim | 18 +++++++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 tests/reject/tnicerrorforsymchoice.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6fb250179..041baf126 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -166,6 +166,15 @@ proc writeMatches*(c: TCandidate) = Writeln(stdout, "intconv matches: " & $c.intConvMatches) Writeln(stdout, "generic matches: " & $c.genericMatches) +proc argTypeToString(arg: PNode): string = + if arg.kind in nkSymChoices: + result = typeToString(arg[0].typ) + for i in 1 .. Date: Thu, 27 Jun 2013 08:19:03 +0200 Subject: [PATCH 68/83] fixes #488 --- compiler/semexprs.nim | 1 + tests/compile/tmacrostmt.nim | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d418cd1e7..94453be15 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1290,6 +1290,7 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo, proc semExpandToAst(c: PContext, n: PNode): PNode = var macroCall = n[1] var expandedSym = expectMacroOrTemplateCall(c, macroCall) + if expandedSym.kind == skError: return n macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) markUsed(n, expandedSym) diff --git a/tests/compile/tmacrostmt.nim b/tests/compile/tmacrostmt.nim index 69b49f692..d9c70197d 100644 --- a/tests/compile/tmacrostmt.nim +++ b/tests/compile/tmacrostmt.nim @@ -15,3 +15,12 @@ else: return tkUnknown case_token: inc i + +#bug #488 + +macro foo: stmt = + var exp = newCall("whatwhat", newIntLitNode(1)) + if compiles(getAst(exp)): return exp + else: echo "Does not compute!" + +foo() From e599fdd60d3a4110a2e7f9303a66853a4fa73108 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 08:39:41 +0200 Subject: [PATCH 69/83] bugfix: marshal supports unsigned numbers --- lib/core/typeinfo.nim | 12 ++++++++++-- lib/pure/marshal.nim | 2 +- todo.txt | 1 - 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 6f8081b5f..4ce65ae91 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -379,7 +379,7 @@ proc getBiggestInt*(x: TAny): biggestInt = of tyInt8: result = biggestInt(cast[ptr int8](x.value)[]) of tyInt16: result = biggestInt(cast[ptr int16](x.value)[]) of tyInt32: result = biggestInt(cast[ptr int32](x.value)[]) - of tyInt64: result = biggestInt(cast[ptr int64](x.value)[]) + of tyInt64, tyUInt64: result = biggestInt(cast[ptr int64](x.value)[]) of tyBool: result = biggestInt(cast[ptr bool](x.value)[]) of tyChar: result = biggestInt(cast[ptr char](x.value)[]) of tyEnum, tySet: @@ -389,6 +389,10 @@ proc getBiggestInt*(x: TAny): biggestInt = of 4: result = biggestInt(cast[ptr int32](x.value)[]) of 8: result = biggestInt(cast[ptr int64](x.value)[]) else: assert false + of tyUInt: result = biggestInt(cast[ptr uint](x.value)[]) + of tyUInt8: result = biggestInt(cast[ptr uint8](x.value)[]) + of tyUInt16: result = biggestInt(cast[ptr uint16](x.value)[]) + of tyUInt32: result = biggestInt(cast[ptr uint32](x.value)[]) else: assert false proc setBiggestInt*(x: TAny, y: biggestInt) = @@ -400,7 +404,7 @@ proc setBiggestInt*(x: TAny, y: biggestInt) = of tyInt8: cast[ptr int8](x.value)[] = int8(y) of tyInt16: cast[ptr int16](x.value)[] = int16(y) of tyInt32: cast[ptr int32](x.value)[] = int32(y) - of tyInt64: cast[ptr int64](x.value)[] = int64(y) + of tyInt64, tyUInt64: cast[ptr int64](x.value)[] = int64(y) of tyBool: cast[ptr bool](x.value)[] = y != 0 of tyChar: cast[ptr char](x.value)[] = chr(y.int) of tyEnum, tySet: @@ -410,6 +414,10 @@ proc setBiggestInt*(x: TAny, y: biggestInt) = of 4: cast[ptr int32](x.value)[] = int32(y) of 8: cast[ptr int64](x.value)[] = y else: assert false + of tyUInt: cast[ptr uint](x.value)[] = uint(y) + of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y) + of tyUInt16: cast[ptr uint16](x.value)[] = uint16(y) + of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y) else: assert false proc getChar*(x: TAny): char = diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index f52b554cc..789f6ad76 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -252,7 +252,7 @@ when isMainModule: ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] testit(x) - var test2: tuple[name: string, s: int] = ("tuple test", 56) + var test2: tuple[name: string, s: uint] = ("tuple test", 56u) testit(test2) type diff --git a/todo.txt b/todo.txt index 7f00d8172..48e35b903 100644 --- a/todo.txt +++ b/todo.txt @@ -16,7 +16,6 @@ Bugs ==== - simple closure iterator doesn't work -- marshal doesn't support unsigned types - sfAnon is not respected in renderer - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead From a9f2c3ffaf0959be03c290f9e72ed71495b156aa Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 27 Jun 2013 15:06:30 +0100 Subject: [PATCH 70/83] Fixed OSError + recvLine deprecation warnings. --- lib/pure/httpserver.nim | 190 +++++++++++++++++++------------------ lib/pure/memfiles.nim | 34 ++++--- lib/pure/redis.nim | 57 +++++------ tests/compile/tsockets.nim | 6 +- 4 files changed, 143 insertions(+), 144 deletions(-) diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 61399c2d8..e24709451 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -47,10 +47,11 @@ proc badRequest(client: TSocket) = send(client, "

    Your browser sent a bad request, " & "such as a POST without a Content-Length.

    " & wwwNL) -proc cannotExec(client: TSocket) = - send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL) - sendTextContentType(client) - send(client, "

    Error prohibited CGI execution." & wwwNL) +when false: + proc cannotExec(client: TSocket) = + send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL) + sendTextContentType(client) + send(client, "

    Error prohibited CGI execution." & wwwNL) proc headers(client: TSocket, filename: string) = # XXX could use filename to determine file type @@ -79,11 +80,11 @@ proc unimplemented(client: TSocket) = # ----------------- file serving --------------------------------------------- -proc discardHeaders(client: TSocket) = skip(client) +when false: + proc discardHeaders(client: TSocket) = skip(client) proc serveFile*(client: TSocket, filename: string) = ## serves a file to the client. - when false: discardHeaders(client) var f: TFile if open(f, filename): headers(client, filename) @@ -96,7 +97,7 @@ proc serveFile*(client: TSocket, filename: string) = if bytesread != bytesWritten: dealloc(buf) close(f) - OSError() + OSError(OSLastError()) if bytesread != bufSize: break dealloc(buf) close(f) @@ -104,108 +105,109 @@ proc serveFile*(client: TSocket, filename: string) = notFound(client) # ------------------ CGI execution ------------------------------------------- +when false: + # TODO: Fix this, or get rid of it. + type + TRequestMethod = enum reqGet, reqPost -type - TRequestMethod = enum reqGet, reqPost + proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = + var env = newStringTable(modeCaseInsensitive) + var contentLength = -1 + case meth + of reqGet: + discardHeaders(client) -proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = - var env = newStringTable(modeCaseInsensitive) - var contentLength = -1 - case meth - of reqGet: - discardHeaders(client) + env["REQUEST_METHOD"] = "GET" + env["QUERY_STRING"] = query + of reqPost: + var buf = TaintedString"" + var dataAvail = false + while dataAvail: + dataAvail = recvLine(client, buf) # TODO: This is incorrect. + var L = toLower(buf.string) + if L.startsWith("content-length:"): + var i = len("content-length:") + while L[i] in Whitespace: inc(i) + contentLength = parseInt(substr(L, i)) - env["REQUEST_METHOD"] = "GET" - env["QUERY_STRING"] = query - of reqPost: - var buf = TaintedString"" - var dataAvail = false - while dataAvail: - dataAvail = recvLine(client, buf) # TODO: This is incorrect. - var L = toLower(buf.string) - if L.startsWith("content-length:"): - var i = len("content-length:") - while L[i] in Whitespace: inc(i) - contentLength = parseInt(substr(L, i)) + if contentLength < 0: + badRequest(client) + return - if contentLength < 0: - badRequest(client) - return + env["REQUEST_METHOD"] = "POST" + env["CONTENT_LENGTH"] = $contentLength - env["REQUEST_METHOD"] = "POST" - env["CONTENT_LENGTH"] = $contentLength + send(client, "HTTP/1.0 200 OK" & wwwNL) - send(client, "HTTP/1.0 200 OK" & wwwNL) - - var process = startProcess(command=path, env=env) - if meth == reqPost: - # get from client and post to CGI program: - var buf = alloc(contentLength) - if recv(client, buf, contentLength) != contentLength: + var process = startProcess(command=path, env=env) + if meth == reqPost: + # get from client and post to CGI program: + var buf = alloc(contentLength) + if recv(client, buf, contentLength) != contentLength: + dealloc(buf) + OSError() + var inp = process.inputStream + inp.writeData(buf, contentLength) dealloc(buf) - OSError() - var inp = process.inputStream - inp.writeData(buf, contentLength) - dealloc(buf) - var outp = process.outputStream - var line = newStringOfCap(120).TaintedString - while true: - if outp.readLine(line): - send(client, line.string) - send(client, wwwNL) - elif not running(process): break + var outp = process.outputStream + var line = newStringOfCap(120).TaintedString + while true: + if outp.readLine(line): + send(client, line.string) + send(client, wwwNL) + elif not running(process): break -# --------------- Server Setup ----------------------------------------------- + # --------------- Server Setup ----------------------------------------------- -proc acceptRequest(client: TSocket) = - var cgi = false - var query = "" - var buf = TaintedString"" - discard recvLine(client, buf) - var path = "" - var data = buf.string.split() - var meth = reqGet + proc acceptRequest(client: TSocket) = + var cgi = false + var query = "" + var buf = TaintedString"" + discard recvLine(client, buf) + var path = "" + var data = buf.string.split() + var meth = reqGet - var q = find(data[1], '?') + var q = find(data[1], '?') - # extract path - if q >= 0: - # strip "?..." from path, this may be found in both POST and GET - path = "." & data[1].substr(0, q-1) - else: - path = "." & data[1] - # path starts with "/", by adding "." in front of it we serve files from cwd - - if cmpIgnoreCase(data[0], "GET") == 0: + # extract path if q >= 0: + # strip "?..." from path, this may be found in both POST and GET + path = "." & data[1].substr(0, q-1) + else: + path = "." & data[1] + # path starts with "/", by adding "." in front of it we serve files from cwd + + if cmpIgnoreCase(data[0], "GET") == 0: + if q >= 0: + cgi = true + query = data[1].substr(q+1) + elif cmpIgnoreCase(data[0], "POST") == 0: cgi = true - query = data[1].substr(q+1) - elif cmpIgnoreCase(data[0], "POST") == 0: - cgi = true - meth = reqPost - else: - unimplemented(client) - - if path[path.len-1] == '/' or existsDir(path): - path = path / "index.html" - - if not ExistsFile(path): - discardHeaders(client) - notFound(client) - else: - when defined(Windows): - var ext = splitFile(path).ext.toLower - if ext == ".exe" or ext == ".cgi": - # XXX: extract interpreter information here? - cgi = true + meth = reqPost else: - if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}: - cgi = true - if not cgi: - serveFile(client, path) + unimplemented(client) + + if path[path.len-1] == '/' or existsDir(path): + path = path / "index.html" + + if not ExistsFile(path): + discardHeaders(client) + notFound(client) else: - executeCgi(client, path, query, meth) + when defined(Windows): + var ext = splitFile(path).ext.toLower + if ext == ".exe" or ext == ".cgi": + # XXX: extract interpreter information here? + cgi = true + else: + if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}: + cgi = true + if not cgi: + serveFile(client, path) + else: + executeCgi(client, path, query, meth) type TServer* = object of TObject ## contains the current server state @@ -226,7 +228,7 @@ proc open*(s: var TServer, port = TPort(80)) = ## creates a new server at port `port`. If ``port == 0`` a free port is ## acquired that can be accessed later by the ``port`` proc. s.socket = socket(AF_INET) - if s.socket == InvalidSocket: OSError() + if s.socket == InvalidSocket: OSError(OSLastError()) bindAddr(s.socket, port) listen(s.socket) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 84a87ba15..9f4094b40 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -50,11 +50,11 @@ proc open*(filename: string, mode: TFileMode = fmRead, result.size = 0 when defined(windows): - template fail(msg: expr) = + template fail(errCode: TOSErrorCode, msg: expr) = rollback() if result.fHandle != 0: discard CloseHandle(result.fHandle) if result.mapHandle != 0: discard CloseHandle(result.mapHandle) - OSError() + OSError(errCode) # return false #raise newException(EIO, msg) @@ -74,7 +74,7 @@ proc open*(filename: string, mode: TFileMode = fmRead, result.fHandle = callCreateFile(CreateFileA, filename) if result.fHandle == INVALID_HANDLE_VALUE: - fail "error opening file" + fail(OSLastError(), "error opening file") if newFileSize != -1: var @@ -83,9 +83,10 @@ proc open*(filename: string, mode: TFileMode = fmRead, var status = SetFilePointer(result.fHandle, sizeLow, addr(sizeHigh), FILE_BEGIN) - if (status == INVALID_SET_FILE_POINTER and GetLastError() != NO_ERROR) or + let lastErr = OSLastError() + if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or (SetEndOfFile(result.fHandle) == 0): - fail "error setting file size" + fail(lastErr, "error setting file size") # since the strings are always 'nil', we simply always call # CreateFileMappingW which should be slightly faster anyway: @@ -95,7 +96,7 @@ proc open*(filename: string, mode: TFileMode = fmRead, 0, 0, nil) if result.mapHandle == 0: - fail "error creating mapping" + fail(OSLastError(), "error creating mapping") result.mem = MapViewOfFileEx( result.mapHandle, @@ -106,22 +107,22 @@ proc open*(filename: string, mode: TFileMode = fmRead, nil) if result.mem == nil: - fail "error mapping view" + fail(OSLastError(), "error mapping view") var hi, low: int32 low = GetFileSize(result.fHandle, addr(hi)) if low == INVALID_FILE_SIZE: - fail "error getting file size" + fail(OSLastError(), "error getting file size") else: var fileSize = (int64(hi) shr 32) or low if mappedSize != -1: result.size = min(fileSize, mappedSize).int else: result.size = fileSize.int else: - template fail(msg: expr) = + template fail(errCode: TOSErrorCode, msg: expr) = rollback() if result.handle != 0: discard close(result.handle) - OSError() + OSError(errCode) var flags = if readonly: O_RDONLY else: O_RDWR @@ -132,11 +133,11 @@ proc open*(filename: string, mode: TFileMode = fmRead, if result.handle == -1: # XXX: errno is supposed to be set here # Is there an exception that wraps it? - fail "error opening file" + fail(OSLastError(), "error opening file") if newFileSize != -1: if ftruncate(result.handle, newFileSize) == -1: - fail "error setting file size" + fail(OSLastError(), "error setting file size") if mappedSize != -1: result.size = mappedSize @@ -147,7 +148,7 @@ proc open*(filename: string, mode: TFileMode = fmRead, # Why is mmap taking int anyway? result.size = int(stat.st_size) else: - fail "error getting file size" + fail(OSLastError(), "error getting file size") result.mem = mmap( nil, @@ -158,21 +159,24 @@ proc open*(filename: string, mode: TFileMode = fmRead, offset) if result.mem == cast[pointer](MAP_FAILED): - fail "file mapping failed" + fail(OSLastError(), "file mapping failed") proc close*(f: var TMemFile) = ## closes the memory mapped file `f`. All changes are written back to the ## file system, if `f` was opened with write access. var error = false + var lastErr: TOSErrorCode when defined(windows): if f.fHandle != INVALID_HANDLE_VALUE: + lastErr = OSLastError() error = UnmapViewOfFile(f.mem) == 0 error = (CloseHandle(f.mapHandle) == 0) or error error = (CloseHandle(f.fHandle) == 0) or error else: if f.handle != 0: + lastErr = OSLastError() error = munmap(f.mem, f.size) != 0 error = (close(f.handle) != 0) or error @@ -185,5 +189,5 @@ proc close*(f: var TMemFile) = else: f.handle = 0 - if error: OSError() + if error: OSError(lastErr) diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index bb6ea6768..8171dc12b 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -36,7 +36,7 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis = ## Opens a connection to the redis server. result.socket = socket(buffered = false) if result.socket == InvalidSocket: - OSError() + OSError(OSLastError()) result.socket.connect(host, port) proc raiseInvalidReply(expected, got: char) = @@ -50,34 +50,31 @@ proc raiseNoOK(status: string) = proc parseStatus(r: TRedis): TRedisStatus = var line = "" - if r.socket.recvLine(line): - if line == "": - raise newException(ERedis, "Server closed connection prematurely") + r.socket.readLine(line) + if line == "": + raise newException(ERedis, "Server closed connection prematurely") + + if line[0] == '-': + raise newException(ERedis, strip(line)) + if line[0] != '+': + raiseInvalidReply('+', line[0]) - if line[0] == '-': - raise newException(ERedis, strip(line)) - if line[0] != '+': - raiseInvalidReply('+', line[0]) - - return line.substr(1) # Strip '+' - else: - OSError() + return line.substr(1) # Strip '+' proc parseInteger(r: TRedis): TRedisInteger = var line = "" - if r.socket.recvLine(line): - if line == "": - raise newException(ERedis, "Server closed connection prematurely") + r.socket.readLine(line) + if line == "": + raise newException(ERedis, "Server closed connection prematurely") - if line[0] == '-': - raise newException(ERedis, strip(line)) - if line[0] != ':': - raiseInvalidReply(':', line[0]) - - # Strip ':' - if parseBiggestInt(line, result, 1) == 0: - raise newException(EInvalidReply, "Unable to parse integer.") - else: OSError() + if line[0] == '-': + raise newException(ERedis, strip(line)) + if line[0] != ':': + raiseInvalidReply(':', line[0]) + + # Strip ':' + if parseBiggestInt(line, result, 1) == 0: + raise newException(EInvalidReply, "Unable to parse integer.") proc recv(sock: TSocket, size: int): TaintedString = result = newString(size).TaintedString @@ -86,8 +83,7 @@ proc recv(sock: TSocket, size: int): TaintedString = proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = var line = "" - if not r.socket.recvLine(line.TaintedString): - raise newException(EInvalidReply, "recvLine failed") + r.socket.readLine(line.TaintedString) # Error. if line[0] == '-': @@ -110,8 +106,7 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = proc parseMultiBulk(r: TRedis): TRedisList = var line = TaintedString"" - if not r.socket.recvLine(line): - raise newException(EInvalidReply, "recvLine failed") + r.socket.readLine(line) if line.string[0] != '*': raiseInvalidReply('*', line.string[0]) @@ -848,10 +843,8 @@ proc shutdown*(r: TRedis) = ## Synchronously save the dataset to disk and then shut down the server r.sendCommand("SHUTDOWN") var s = "".TaintedString - if r.socket.recvLine(s): - if s.string.len != 0: raise newException(ERedis, s.string) - else: - OSError() + r.socket.readLine(s) + if s.string.len != 0: raise newException(ERedis, s.string) proc slaveof*(r: TRedis, host: string, port: string) = ## Make the server a slave of another instance, or promote it as master diff --git a/tests/compile/tsockets.nim b/tests/compile/tsockets.nim index cc7d18b87..6078504f5 100644 --- a/tests/compile/tsockets.nim +++ b/tests/compile/tsockets.nim @@ -4,8 +4,8 @@ s = socket() s.connect("www.google.com", TPort(80)) -var recvData: string = "" -echo(s.recvLine(recvData)) -echo(recvData) +var data: string = "" +s.readLine(data) +echo(data) From 6d7ad6698296f9460fe3bf5fe4a3ef188608a9f5 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 16:34:21 +0200 Subject: [PATCH 71/83] attempt to use the lib.so for libzip --- lib/wrappers/zip/libzip.nim | 103 +++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/lib/wrappers/zip/libzip.nim b/lib/wrappers/zip/libzip.nim index 2f8abc46c..fe4316dc7 100644 --- a/lib/wrappers/zip/libzip.nim +++ b/lib/wrappers/zip/libzip.nim @@ -1,16 +1,14 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2008 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## Interface to the `libzip `_ library by -## Dieter Baron and Thomas Klausner. However, this does not need any external -## library (DLL, lib*.so), as the source for this library is included and -## compiled with this interface. +## Dieter Baron and Thomas Klausner. # # zip.h -- exported declarations. @@ -47,9 +45,15 @@ import times -{.compile: "libzip_all.c".} when defined(unix): {.passl: "-lz".} + when defined(macosx): + {.pragma: mydll, dynlib: "libzip2.dylib".} + else: + {.pragma: mydll, dynlib: "libzip2.so(|.0|.1)".} +else: + {.compile: "libzip_all.c".} + {.pragma: mydll.} type Tzip_source_cmd* = int32 @@ -57,7 +61,7 @@ type Tzip_source_callback* = proc (state: pointer, data: pointer, length: int, cmd: Tzip_source_cmd): int {.cdecl.} Pzip_stat* = ptr Tzip_stat - Tzip_stat* {.final, pure.} = object + Tzip_stat* = object ## the 'zip_stat' struct name*: cstring ## name of the file index*: int32 ## index within archive crc*: int32 ## crc of file data @@ -67,9 +71,9 @@ type comp_method*: int16 ## compression method used encryption_method*: int16 ## encryption method used - Tzip {.final, pure.} = object - Tzip_source {.final, pure.} = object - Tzip_file {.final, pure.} = object + Tzip = object + Tzip_source = object + Tzip_file = object Pzip* = ptr Tzip ## represents a zip archive Pzip_file* = ptr Tzip_file ## represents a file within an archive @@ -160,82 +164,83 @@ const constZIP_SOURCE_FREE* = 5'i32 ## cleanup and free resources proc zip_add*(para1: Pzip, para2: cstring, para3: Pzip_source): int32 {.cdecl, - importc: "zip_add".} + importc: "zip_add", mydll.} proc zip_add_dir*(para1: Pzip, para2: cstring): int32 {.cdecl, - importc: "zip_add_dir".} -proc zip_close*(para1: Pzip) {.cdecl, importc: "zip_close".} -proc zip_delete*(para1: Pzip, para2: int32): int32 {.cdecl, + importc: "zip_add_dir", mydll.} +proc zip_close*(para1: Pzip) {.cdecl, importc: "zip_close", mydll.} +proc zip_delete*(para1: Pzip, para2: int32): int32 {.cdecl, mydll, importc: "zip_delete".} -proc zip_error_clear*(para1: Pzip) {.cdecl, importc: "zip_error_clear".} +proc zip_error_clear*(para1: Pzip) {.cdecl, importc: "zip_error_clear", mydll.} proc zip_error_get*(para1: Pzip, para2: ptr int32, para3: ptr int32) {.cdecl, - importc: "zip_error_get".} -proc zip_error_get_sys_type*(para1: int32): int32 {.cdecl, + importc: "zip_error_get", mydll.} +proc zip_error_get_sys_type*(para1: int32): int32 {.cdecl, mydll, importc: "zip_error_get_sys_type".} proc zip_error_to_str*(para1: cstring, para2: int, para3: int32, - para4: int32): int32 {.cdecl, + para4: int32): int32 {.cdecl, mydll, importc: "zip_error_to_str".} -proc zip_fclose*(para1: Pzip_file) {.cdecl, +proc zip_fclose*(para1: Pzip_file) {.cdecl, mydll, importc: "zip_fclose".} -proc zip_file_error_clear*(para1: Pzip_file) {.cdecl, +proc zip_file_error_clear*(para1: Pzip_file) {.cdecl, mydll, importc: "zip_file_error_clear".} proc zip_file_error_get*(para1: Pzip_file, para2: ptr int32, para3: ptr int32) {. - cdecl, importc: "zip_file_error_get".} -proc zip_file_strerror*(para1: Pzip_file): cstring {.cdecl, + cdecl, mydll, importc: "zip_file_error_get".} +proc zip_file_strerror*(para1: Pzip_file): cstring {.cdecl, mydll, importc: "zip_file_strerror".} proc zip_fopen*(para1: Pzip, para2: cstring, para3: int32): Pzip_file {.cdecl, - importc: "zip_fopen".} + mydll, importc: "zip_fopen".} proc zip_fopen_index*(para1: Pzip, para2: int32, para3: int32): Pzip_file {. - cdecl, importc: "zip_fopen_index".} + cdecl, mydll, importc: "zip_fopen_index".} proc zip_fread*(para1: Pzip_file, para2: pointer, para3: int): int {. - cdecl, importc: "zip_fread".} + cdecl, mydll, importc: "zip_fread".} proc zip_get_archive_comment*(para1: Pzip, para2: ptr int32, para3: int32): cstring {. - cdecl, importc: "zip_get_archive_comment".} + cdecl, mydll, importc: "zip_get_archive_comment".} proc zip_get_archive_flag*(para1: Pzip, para2: int32, para3: int32): int32 {. - cdecl, importc: "zip_get_archive_flag".} + cdecl, mydll, importc: "zip_get_archive_flag".} proc zip_get_file_comment*(para1: Pzip, para2: int32, para3: ptr int32, - para4: int32): cstring {.cdecl, + para4: int32): cstring {.cdecl, mydll, importc: "zip_get_file_comment".} proc zip_get_name*(para1: Pzip, para2: int32, para3: int32): cstring {.cdecl, - importc: "zip_get_name".} + mydll, importc: "zip_get_name".} proc zip_get_num_files*(para1: Pzip): int32 {.cdecl, - importc: "zip_get_num_files".} + mydll, importc: "zip_get_num_files".} proc zip_name_locate*(para1: Pzip, para2: cstring, para3: int32): int32 {.cdecl, - importc: "zip_name_locate".} + mydll, importc: "zip_name_locate".} proc zip_open*(para1: cstring, para2: int32, para3: ptr int32): Pzip {.cdecl, - importc: "zip_open".} + mydll, importc: "zip_open".} proc zip_rename*(para1: Pzip, para2: int32, para3: cstring): int32 {.cdecl, - importc: "zip_rename".} + mydll, importc: "zip_rename".} proc zip_replace*(para1: Pzip, para2: int32, para3: Pzip_source): int32 {.cdecl, - importc: "zip_replace".} + mydll, importc: "zip_replace".} proc zip_set_archive_comment*(para1: Pzip, para2: cstring, para3: int32): int32 {. - cdecl, importc: "zip_set_archive_comment".} + cdecl, mydll, importc: "zip_set_archive_comment".} proc zip_set_archive_flag*(para1: Pzip, para2: int32, para3: int32): int32 {. - cdecl, importc: "zip_set_archive_flag".} + cdecl, mydll, importc: "zip_set_archive_flag".} proc zip_set_file_comment*(para1: Pzip, para2: int32, para3: cstring, - para4: int32): int32 {.cdecl, + para4: int32): int32 {.cdecl, mydll, importc: "zip_set_file_comment".} proc zip_source_buffer*(para1: Pzip, para2: pointer, para3: int, para4: int32): Pzip_source {. - cdecl, importc: "zip_source_buffer".} + cdecl, mydll, importc: "zip_source_buffer".} proc zip_source_file*(para1: Pzip, para2: cstring, para3: int, para4: int): Pzip_source {. - cdecl, importc: "zip_source_file".} + cdecl, mydll, importc: "zip_source_file".} proc zip_source_filep*(para1: Pzip, para2: TFile, para3: int, para4: int): Pzip_source {. - cdecl, importc: "zip_source_filep".} -proc zip_source_free*(para1: Pzip_source) {.cdecl, + cdecl, mydll, importc: "zip_source_filep".} +proc zip_source_free*(para1: Pzip_source) {.cdecl, mydll, importc: "zip_source_free".} proc zip_source_function*(para1: Pzip, para2: Tzip_source_callback, - para3: pointer): Pzip_source {.cdecl, + para3: pointer): Pzip_source {.cdecl, mydll, importc: "zip_source_function".} proc zip_source_zip*(para1: Pzip, para2: Pzip, para3: int32, para4: int32, - para5: int, para6: int): Pzip_source {.cdecl, + para5: int, para6: int): Pzip_source {.cdecl, mydll, importc: "zip_source_zip".} proc zip_stat*(para1: Pzip, para2: cstring, para3: int32, para4: Pzip_stat): int32 {. - cdecl, importc: "zip_stat".} + cdecl, mydll, importc: "zip_stat".} proc zip_stat_index*(para1: Pzip, para2: int32, para3: int32, para4: Pzip_stat): int32 {. - cdecl, importc: "zip_stat_index".} -proc zip_stat_init*(para1: Pzip_stat) {.cdecl, importc: "zip_stat_init".} -proc zip_strerror*(para1: Pzip): cstring {.cdecl, importc: "zip_strerror".} -proc zip_unchange*(para1: Pzip, para2: int32): int32 {.cdecl, + cdecl, mydll, importc: "zip_stat_index".} +proc zip_stat_init*(para1: Pzip_stat) {.cdecl, mydll, importc: "zip_stat_init".} +proc zip_strerror*(para1: Pzip): cstring {.cdecl, mydll, importc: "zip_strerror".} +proc zip_unchange*(para1: Pzip, para2: int32): int32 {.cdecl, mydll, importc: "zip_unchange".} -proc zip_unchange_all*(para1: Pzip): int32 {.cdecl, importc: "zip_unchange_all".} -proc zip_unchange_archive*(para1: Pzip): int32 {.cdecl, +proc zip_unchange_all*(para1: Pzip): int32 {.cdecl, mydll, + importc: "zip_unchange_all".} +proc zip_unchange_archive*(para1: Pzip): int32 {.cdecl, mydll, importc: "zip_unchange_archive".} From 139562cc643c7a4b201b25892d9cc949d3c4e5b8 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 18:13:11 +0200 Subject: [PATCH 72/83] implements the 'codegenDecl' pragma --- compiler/ast.nim | 6 +++--- compiler/ccgtypes.nim | 12 +++++++++--- compiler/cgen.nim | 31 ++++++++++++++++++++----------- compiler/pragmas.nim | 13 +++++++------ compiler/wordrecg.nim | 4 ++-- doc/nimrodc.txt | 15 +++++++++++++++ web/news.txt | 2 ++ 7 files changed, 58 insertions(+), 25 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 02396f0bb..eee1e1ad2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -278,8 +278,6 @@ const # the compiler will avoid printing such names # in user messages. - sfHoist* = sfVolatile ## proc return value can be hoisted - sfNoForward* = sfRegister # forward declarations are not required (per module) @@ -673,7 +671,9 @@ type loc*: TLoc annex*: PLib # additional fields (seldom used, so we use a # reference to another object to safe space) - constraint*: PNode # additional constraints like 'lit|result' + constraint*: PNode # additional constraints like 'lit|result'; also + # misused for the codegenDecl pragma in the hope + # it won't cause problems TTypeSeq* = seq[PType] TType* {.acyclic.} = object of TIdObj # \ diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 88e7e264d..4d0fae451 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -651,7 +651,9 @@ proc finishTypeDescriptions(m: BModule) = while i < len(m.typeStack): discard getTypeDesc(m, m.typeStack[i]) inc(i) - + +template cgDeclFrmt*(s: PSym): string = s.constraint.strVal + proc genProcHeader(m: BModule, prc: PSym): PRope = var rettype, params: PRope @@ -669,8 +671,12 @@ proc genProcHeader(m: BModule, prc: PSym): PRope = genProcParams(m, prc.typ, rettype, params, check) # careful here! don't access ``prc.ast`` as that could reload large parts of # the object graph! - appf(result, "$1($2, $3)$4", - [toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, params]) + if prc.constraint.isNil: + appf(result, "$1($2, $3)$4", + [toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, + params]) + else: + result = ropef(prc.cgDeclFrmt, [rettype, prc.loc.r, params]) # ------------------ type info generation ------------------------------------- diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 45fb9f878..18f33f32a 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -488,12 +488,15 @@ proc assignLocalVar(p: BProc, s: PSym) = fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) var decl = getTypeDesc(p.module, s.loc.t) - if sfRegister in s.flags: app(decl, " register") - #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: - # app(decl, " GC_GUARD") - if sfVolatile in s.flags or p.nestedTryStmts.len > 0: - app(decl, " volatile") - appf(decl, " $1;$n", [s.loc.r]) + if s.constraint.isNil: + if sfRegister in s.flags: app(decl, " register") + #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: + # app(decl, " GC_GUARD") + if sfVolatile in s.flags or p.nestedTryStmts.len > 0: + app(decl, " volatile") + appf(decl, " $1;$n", [s.loc.r]) + else: + decl = ropef(s.cgDeclFrmt & ";$n", decl, s.loc.r) line(p, cpsLocals, decl) localDebugInfo(p, s) @@ -518,11 +521,17 @@ proc assignGlobalVar(p: BProc, s: PSym) = if sfThread in s.flags: declareThreadVar(p.module, s, sfImportc in s.flags) else: - if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ") - app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t)) - if sfRegister in s.flags: app(p.module.s[cfsVars], " register") - if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile") - appf(p.module.s[cfsVars], " $1;$n", [s.loc.r]) + var decl: PRope = nil + var td = getTypeDesc(p.module, s.loc.t) + if s.constraint.isNil: + if sfImportc in s.flags: app(decl, "extern ") + app(decl, td) + if sfRegister in s.flags: app(decl, " register") + if sfVolatile in s.flags: app(decl, " volatile") + appf(decl, " $1;$n", [s.loc.r]) + else: + decl = ropef(s.cgDeclFrmt & ";$n", td, s.loc.r) + app(p.module.s[cfsVars], decl) if p.withinLoop > 0: # fixes tests/run/tzeroarray: resetLoc(p, s.loc) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index cecec8e5e..8f3da9f38 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -23,7 +23,7 @@ const wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, - wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist, + wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wGenSym, wInject, wRaises, wTags} converterPragmas* = procPragmas methodPragmas* = procPragmas @@ -56,7 +56,7 @@ const varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern, wImportcpp, wImportobjc, wError, wNoInit, wCompileTime, wGlobal, - wGenSym, wInject} + wGenSym, wInject, wCodegenDecl} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportcpp, wImportobjc, wError, wGenSym, wInject} letPragmas* = varPragmas @@ -147,7 +147,10 @@ proc expectIntLit(c: PContext, n: PNode): int = proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string = if n.kind == nkExprColonExpr: result = expectStrLit(c, n) else: result = defaultStr - + +proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = + sym.constraint = getStrLitNode(c, n) + proc processMagic(c: PContext, n: PNode, s: PSym) = #if sfSystemModule notin c.module.flags: # liMessage(n.info, errMagicOnlyInSystem) @@ -669,9 +672,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, of wNoInit: noVal(it) if sym != nil: incl(sym.flags, sfNoInit) - of wHoist: - noVal(it) - if sym != nil: incl(sym.flags, sfHoist) + of wCodegenDecl: processCodegenDecl(c, it, sym) of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 2a8a02bd6..06607d2a6 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -62,7 +62,7 @@ type wAcyclic, wShallow, wUnroll, wLinearScanEnd, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wNoStackFrame, - wImplicitStatic, wGlobal, wHoist, + wImplicitStatic, wGlobal, wCodegenDecl, wAuto, wBool, wCatch, wChar, wClass, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, @@ -142,7 +142,7 @@ const "watchpoint", "subschar", "acyclic", "shallow", "unroll", "linearscanend", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", - "nostackframe", "implicitstatic", "global", "hoist", + "nostackframe", "implicitstatic", "global", "codegendecl", "auto", "bool", "catch", "char", "class", "const_cast", "default", "delete", "double", diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 339cee382..ae169829f 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -399,6 +399,21 @@ interfacing with libraries written in Objective C: The compiler needs to be told to generate Objective C (command ``objc``) for this to work. The conditional symbol ``objc`` is defined when the compiler emits Objective C code. + + +CodegenDecl pragma +------------------ + +The `codegenDecl`:idx: pragma can be used to directly influence Nimrod's code +generator. It receives a format string that determines how the variable or +proc is declared in the generated code: + +.. code-block:: nimrod + var + a {.codegenDecl: "$# progmem $#".}: int + + proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = + echo "realistic interrupt handler" LineDir option diff --git a/web/news.txt b/web/news.txt index 5ef96368f..74b33e2bf 100644 --- a/web/news.txt +++ b/web/news.txt @@ -33,6 +33,8 @@ Compiler Additions real uninitialized variables in Nimrod as they are initialized to binary zero). Activate via ``{.warning[Uninit]:on.}``. - The compiler now enforces the ``not nil`` constraint. +- The compiler now supports a ``codegenDecl`` pragma for even more control + over the generated code. Language Additions From 83876b1b8113ddeaa1b0376d39ef2da2525a73b3 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 19:57:01 +0200 Subject: [PATCH 73/83] bugfix: rendering of typ|typ2 --- compiler/renderer.nim | 24 ++++++++++++++++-------- lib/pure/times.nim | 2 +- todo.txt | 1 - 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index d68d2e549..70ce5c27d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -33,6 +33,7 @@ type # indentation value comStack*: seq[PNode] # comment stack flags*: TRenderFlags + checkAnon: bool # we're in a context that can contain sfAnon proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {}) @@ -70,7 +71,8 @@ proc InitSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = g.idx = 0 g.buf = "" g.flags = renderFlags - g.pendingNL = - 1 + g.pendingNL = -1 + g.checkAnon = false proc addTok(g: var TSrcGen, kind: TTokType, s: string) = var length = len(g.tokens) @@ -493,13 +495,15 @@ proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) = proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, theEnd: int = - 1, separator = tkComma) = - for i in countup(start, sonsLen(n) + theEnd): + for i in countup(start, sonsLen(n) + theEnd): var c = i < sonsLen(n) + theEnd var sublen = lsub(n.sons[i]) + ord(c) if not fits(g, sublen) and (ind + sublen < maxLineLen): optNL(g, ind) + let oldLen = g.tokens.len gsub(g, n.sons[i]) - if c: - putWithSpace(g, separator, TokTypeToStr[separator]) + if c: + if g.tokens.len > oldLen: + putWithSpace(g, separator, TokTypeToStr[separator]) if hasCom(n.sons[i]): gcoms(g) optNL(g, ind) @@ -673,7 +677,10 @@ proc gproc(g: var TSrcGen, n: PNode) = if n.sons[patternPos].kind != nkEmpty: gpattern(g, n.sons[patternPos]) + let oldCheckAnon = g.checkAnon + g.checkAnon = true gsub(g, n.sons[genericParamsPos]) + g.checkAnon = oldCheckAnon gsub(g, n.sons[paramsPos]) gsub(g, n.sons[pragmasPos]) if renderNoBody notin g.flags: @@ -724,7 +731,8 @@ proc gasm(g: var TSrcGen, n: PNode) = gcoms(g) gsub(g, n.sons[1]) -proc gident(g: var TSrcGen, n: PNode) = +proc gident(g: var TSrcGen, n: PNode) = + if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return var t: TTokType var s = atom(n) if (s[0] in lexer.SymChars): @@ -897,8 +905,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[pragmasPos]) put(g, tkColon, ":") gsub(g, n.sons[bodyPos]) - of nkConstDef, nkIdentDefs: - gcomma(g, n, 0, - 3) + of nkConstDef, nkIdentDefs: + gcomma(g, n, 0, -3) var L = sonsLen(n) if n.sons[L - 2].kind != nkEmpty: putWithSpace(g, tkColon, ":") @@ -909,7 +917,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[L - 1], c) of nkVarTuple: put(g, tkParLe, "(") - gcomma(g, n, 0, - 3) + gcomma(g, n, 0, -3) put(g, tkParRi, ")") put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 86609c8e3..80a5ad8d3 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/todo.txt b/todo.txt index 48e35b903..35c6a5704 100644 --- a/todo.txt +++ b/todo.txt @@ -16,7 +16,6 @@ Bugs ==== - simple closure iterator doesn't work -- sfAnon is not respected in renderer - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead - sneaking with qualifiedLookup() is really broken! From 86d64b2f5efb03e34cf97c64524f1e49657a63ec Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 21:41:53 +0200 Subject: [PATCH 74/83] attempt to make libzip work on the testing machines --- koch.nim | 17 ++++++++++------- lib/wrappers/zip/libzip.nim | 8 +++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/koch.nim b/koch.nim index 883fffb6d..30ad8c597 100644 --- a/koch.nim +++ b/koch.nim @@ -64,13 +64,16 @@ proc tryExec(cmd: string): bool = echo(cmd) result = execShellCmd(cmd) == 0 +const + compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" + proc csource(args: string) = - exec("nimrod cc $1 -r tools/niminst/niminst --var:version=$2 csource compiler/nimrod.ini $1" % - [args, NimrodVersion]) + exec("nimrod cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" % + [args, NimrodVersion, compileNimInst]) proc zip(args: string) = - exec("nimrod cc -r tools/niminst/niminst --var:version=$# zip compiler/nimrod.ini" % - NimrodVersion) + exec("nimrod cc -r $2 --var:version=$1 zip compiler/nimrod.ini" % + [NimrodVersion, compileNimInst]) proc buildTool(toolname, args: string) = exec("nimrod cc $# $#" % [args, toolname]) @@ -85,9 +88,9 @@ proc inno(args: string) = NimrodVersion) proc install(args: string) = - exec("nimrod cc -r tools/niminst/niminst --var:version=$# scripts compiler/nimrod.ini" % - NimrodVersion) - exec("sh ./install.sh $#" % args) + exec("nimrod cc -r $# --var:version=$# scripts compiler/nimrod.ini" % + [compileNimInst, NimrodVersion]) + exec("sh ./install.sh $#" % args) proc web(args: string) = exec(("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" & diff --git a/lib/wrappers/zip/libzip.nim b/lib/wrappers/zip/libzip.nim index fe4316dc7..26a0ce67c 100644 --- a/lib/wrappers/zip/libzip.nim +++ b/lib/wrappers/zip/libzip.nim @@ -8,7 +8,9 @@ # ## Interface to the `libzip `_ library by -## Dieter Baron and Thomas Klausner. +## Dieter Baron and Thomas Klausner. This version links +## against ``libzip2.so.2`` unless you define the symbol ``useLibzipSrc``; then +## it is compiled against some old ``libizp_all.c`` file. # # zip.h -- exported declarations. @@ -45,12 +47,12 @@ import times -when defined(unix): +when defined(unix) and not defined(useLibzipSrc): {.passl: "-lz".} when defined(macosx): {.pragma: mydll, dynlib: "libzip2.dylib".} else: - {.pragma: mydll, dynlib: "libzip2.so(|.0|.1)".} + {.pragma: mydll, dynlib: "libzip2.so(|.2|.1|.0)".} else: {.compile: "libzip_all.c".} {.pragma: mydll.} From df8e1514081425f63df10884b9d5cf18345dcb6f Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Jun 2013 22:22:49 +0200 Subject: [PATCH 75/83] implements nicer floating point literals --- compiler/lexer.nim | 25 +++++++++++++------------ doc/manual.txt | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 6660ff65c..f4fc4247a 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -235,9 +235,9 @@ proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) = proc matchTwoChars(L: TLexer, first: Char, second: TCharSet): bool = result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in Second) -proc isFloatLiteral(s: string): bool = - for i in countup(0, len(s) + 0 - 1): - if s[i] in {'.', 'e', 'E'}: +proc isFloatLiteral(s: string): bool = + for i in countup(0, len(s) - 1): + if s[i] in {'.', 'e', 'E'}: return true result = false @@ -250,25 +250,26 @@ proc GetNumber(L: var TLexer): TToken = result.literal = "" result.base = base10 # BUGFIX pos = L.bufpos # make sure the literal is correct for error messages: + var eallowed = false if L.buf[pos] == '0' and L.buf[pos+1] in {'X', 'x'}: matchUnderscoreChars(L, result, {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x'}) else: matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'}) + eallowed = true if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): add(result.literal, '.') - inc(L.bufpos) - #matchUnderscoreChars(L, result, ['A'..'Z', 'a'..'z', '0'..'9']) + inc(L.bufpos) matchUnderscoreChars(L, result, {'0'..'9'}) - if L.buf[L.bufpos] in {'e', 'E'}: - add(result.literal, 'e') + eallowed = true + if eallowed and L.buf[L.bufpos] in {'e', 'E'}: + add(result.literal, 'e') + inc(L.bufpos) + if L.buf[L.bufpos] in {'+', '-'}: + add(result.literal, L.buf[L.bufpos]) inc(L.bufpos) - if L.buf[L.bufpos] in {'+', '-'}: - add(result.literal, L.buf[L.bufpos]) - inc(L.bufpos) - matchUnderscoreChars(L, result, {'0'..'9'}) + matchUnderscoreChars(L, result, {'0'..'9'}) endpos = L.bufpos if L.buf[endpos] in {'\'', 'f', 'F', 'i', 'I', 'u', 'U'}: - #matchUnderscoreChars(L, result, ['''', 'f', 'F', 'i', 'I', '0'..'9']); if L.buf[endpos] == '\'': inc(endpos) L.bufpos = pos # restore position case L.buf[endpos] diff --git a/doc/manual.txt b/doc/manual.txt index 2c19911b4..5fdc5a884 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -352,7 +352,7 @@ Numerical constants UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* - FLOAT_LIT = digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent) + FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent) FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32' | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32' FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64' From 2ca05eeb16eaf2f1b811f21dd3d0baec21cd90da Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 28 Jun 2013 08:40:49 +0200 Subject: [PATCH 76/83] var tuple unpacking works at compile time --- compiler/evals.nim | 71 +++++++++++++++++++++++++------------------ compiler/msgs.nim | 11 ++++--- compiler/semstmts.nim | 11 ++++--- todo.txt | 1 + 4 files changed, 54 insertions(+), 40 deletions(-) diff --git a/compiler/evals.nim b/compiler/evals.nim index 6b756ab36..84f0266ea 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -287,41 +287,52 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkCurly, info, t) else: InternalError("getNullValue: " & $t.kind) -proc evalVar(c: PEvalContext, n: PNode): PNode = - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if a.kind != nkIdentDefs: return raiseCannotEval(c, n.info) - # XXX var (x, y) = z support? +proc evalVarValue(c: PEvalContext, n: PNode): PNode = + result = evalAux(c, n, {}) + if result.kind in {nkType..nkNilLit}: result = result.copyNode + +proc evalVar(c: PEvalContext, n: PNode): PNode = + for i in countup(0, sonsLen(n) - 1): + let a = n.sons[i] + if a.kind == nkCommentStmt: continue #assert(a.sons[0].kind == nkSym) can happen for transformed vars - if a.sons[2].kind != nkEmpty: - result = evalAux(c, a.sons[2], {}) - if result.kind in {nkType..nkNilLit}: + if a.kind == nkVarTuple: + result = evalVarValue(c, a.lastSon) + if result.kind in {nkType..nkNilLit}: result = result.copyNode if isSpecial(result): return + if result.kind != nkPar: + return raiseCannotEval(c, n.info) + for i in 0 .. a.len-3: + var v = a.sons[i].sym + IdNodeTablePut(c.tos.mapping, v, result.sons[i]) else: - result = getNullValue(a.sons[0].typ, a.sons[0].info) - if a.sons[0].kind == nkSym: - var v = a.sons[0].sym - IdNodeTablePut(c.tos.mapping, v, result) - else: - # assign to a.sons[0]: - var x = result - result = evalAux(c, a.sons[0], {}) - if isSpecial(result): return - myreset(x) - x.kind = result.kind - x.typ = result.typ - case x.kind - of nkCharLit..nkInt64Lit: x.intVal = result.intVal - of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal - of nkStrLit..nkTripleStrLit: x.strVal = result.strVal - of nkIdent: x.ident = result.ident - of nkSym: x.sym = result.sym + if a.sons[2].kind != nkEmpty: + result = evalVarValue(c, a.sons[2]) + if isSpecial(result): return else: - if x.kind notin {nkEmpty..nkNilLit}: - discardSons(x) - for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j]) + result = getNullValue(a.sons[0].typ, a.sons[0].info) + if a.sons[0].kind == nkSym: + var v = a.sons[0].sym + IdNodeTablePut(c.tos.mapping, v, result) + else: + # assign to a.sons[0]: + var x = result + result = evalAux(c, a.sons[0], {}) + if isSpecial(result): return + myreset(x) + x.kind = result.kind + x.typ = result.typ + case x.kind + of nkCharLit..nkInt64Lit: x.intVal = result.intVal + of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal + of nkStrLit..nkTripleStrLit: x.strVal = result.strVal + of nkIdent: x.ident = result.ident + of nkSym: x.sym = result.sym + else: + if x.kind notin {nkEmpty..nkNilLit}: + discardSons(x) + for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j]) result = emptyNode proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index e57c41f71..711a3c733 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -798,11 +798,12 @@ proc addSourceLine*(fileIdx: int32, line: string) = proc sourceLine*(i: TLineInfo): PRope = if i.fileIndex < 0: return nil - if not optPreserveOrigSource and - fileInfos[i.fileIndex].lines.len == 0: - for line in lines(i.toFullPath): - addSourceLine i.fileIndex, line.string - + if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: + try: + for line in lines(i.toFullPath): + addSourceLine i.fileIndex, line.string + except EIO: + discard InternalAssert i.fileIndex < fileInfos.len # can happen if the error points to EOF: if i.line > fileInfos[i.fileIndex].lines.len: return nil diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 47b591d83..64ef7f4d6 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -380,11 +380,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # side of the '=': if warnShadowIdent in gNotes and not identWithin(def, v.name): Message(a.info, warnShadowIdent, v.name.s) - if def != nil and def.kind != nkEmpty: - # this is needed for the evaluation pass and for the guard checking: - v.ast = def - if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit) if a.kind != nkVarTuple: + if def != nil and def.kind != nkEmpty: + # this is needed for the evaluation pass and for the guard checking: + v.ast = def + if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit) v.typ = typ b = newNodeI(nkIdentDefs, a.info) if importantComments(): @@ -394,7 +394,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addSon(b, a.sons[length-2]) # keep type desc for doc generator addSon(b, copyTree(def)) addSon(result, b) - else: + else: + if def.kind == nkPar: v.ast = def[j] v.typ = tup.sons[j] b.sons[j] = newSymNode(v) checkNilable(v) diff --git a/todo.txt b/todo.txt index 35c6a5704..bf06a1fb0 100644 --- a/todo.txt +++ b/todo.txt @@ -15,6 +15,7 @@ version 0.9.4 Bugs ==== +- REPL: after an error no evaluation is performed for the next line - simple closure iterator doesn't work - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead From 976d5b18edf1e650db21c4f5e6990c037ed305a7 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 28 Jun 2013 16:40:01 +0200 Subject: [PATCH 77/83] small REPL improvements --- compiler/evals.nim | 9 +++++---- compiler/lookups.nim | 3 ++- todo.txt | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/evals.nim b/compiler/evals.nim index 84f0266ea..8c05d57fc 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -1532,10 +1532,11 @@ var oldErrorCount: int proc myProcess(c: PPassContext, n: PNode): PNode = # don't eval errornous code: - if oldErrorCount != msgs.gErrorCounter: - oldErrorCount = msgs.gErrorCounter - return n - result = eval(PEvalContext(c), n) + if oldErrorCount == msgs.gErrorCounter: + result = eval(PEvalContext(c), n) + else: + result = n + oldErrorCount = msgs.gErrorCounter const evalPass* = makePass(myOpen, nil, myProcess, myProcess) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 191a7aa55..e1ec9e14b 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -91,7 +91,8 @@ proc errorSym*(c: PContext, n: PNode): PSym = result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's imported from some unknown module to prevent cascading errors: - c.importTable.addSym(result) + if gCmd != cmdInteractive: + c.importTable.addSym(result) type TOverloadIterMode* = enum diff --git a/todo.txt b/todo.txt index bf06a1fb0..35c6a5704 100644 --- a/todo.txt +++ b/todo.txt @@ -15,7 +15,6 @@ version 0.9.4 Bugs ==== -- REPL: after an error no evaluation is performed for the next line - simple closure iterator doesn't work - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead From a8542fbe0654d2236dc25eb42c111b470416b82e Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 28 Jun 2013 23:36:43 +0200 Subject: [PATCH 78/83] some small performance improvement for the evaluator --- compiler/ast.nim | 1 + compiler/evals.nim | 89 +++++++++++++++++++++++++++------------------ compiler/transf.nim | 29 ++++++++------- 3 files changed, 69 insertions(+), 50 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index eee1e1ad2..3456177d3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -666,6 +666,7 @@ type # (or not in symbol table) # for modules, an unique index corresponding # to the module's fileIdx + # for variables a slot index for the evaluator offset*: int # offset of record field loc*: TLoc diff --git a/compiler/evals.nim b/compiler/evals.nim index 8c05d57fc..1689a37d8 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -23,12 +23,12 @@ when hasFFI: type PStackFrame* = ref TStackFrame - TStackFrame*{.final.} = object - mapping*: TIdNodeTable # mapping from symbols to nodes - prc*: PSym # current prc; proc that is evaluated - call*: PNode - next*: PStackFrame # for stacking - params*: TNodeSeq # parameters passed to the proc + TStackFrame* = object + prc: PSym # current prc; proc that is evaluated + slots: TNodeSeq # parameters passed to the proc + locals; + # parameters come first + call: PNode + next: PStackFrame # for stacking TEvalMode* = enum ## reason for evaluation emRepl, ## evaluate because in REPL mode @@ -67,10 +67,9 @@ const # other idea: use a timeout! -> Wether code compiles depends on the machine # the compiler runs on then! Bad idea! -proc newStackFrame*(): PStackFrame = +proc newStackFrame*(): PStackFrame = new(result) - initIdNodeTable(result.mapping) - result.params = @[] + result.slots = @[] proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext = new(result) @@ -291,6 +290,20 @@ proc evalVarValue(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n, {}) if result.kind in {nkType..nkNilLit}: result = result.copyNode +proc setSlot(c: PStackFrame, sym: PSym, val: PNode) = + assert sym.owner == c.prc + var idx = sym.position + if idx == 0: + idx = c.slots.len + if idx == 0: idx = 1 + sym.position = idx + setLen(c.slots, max(idx+1, c.slots.len)) + c.slots[idx] = val + +proc setVar(c: PEvalContext, v: PSym, n: PNode) = + if sfGlobal notin v.flags: setSlot(c.tos, v, n) + else: IdNodeTablePut(c.globals, v, n) + proc evalVar(c: PEvalContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): let a = n.sons[i] @@ -305,7 +318,7 @@ proc evalVar(c: PEvalContext, n: PNode): PNode = return raiseCannotEval(c, n.info) for i in 0 .. a.len-3: var v = a.sons[i].sym - IdNodeTablePut(c.tos.mapping, v, result.sons[i]) + setVar(c, v, result.sons[i]) else: if a.sons[2].kind != nkEmpty: result = evalVarValue(c, a.sons[2]) @@ -314,7 +327,7 @@ proc evalVar(c: PEvalContext, n: PNode): PNode = result = getNullValue(a.sons[0].typ, a.sons[0].info) if a.sons[0].kind == nkSym: var v = a.sons[0].sym - IdNodeTablePut(c.tos.mapping, v, result) + setVar(c, v, result) else: # assign to a.sons[0]: var x = result @@ -339,21 +352,22 @@ proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = result = efLValue in flags or n.typ == nil or n.typ.kind in {tyExpr, tyStmt, tyTypeDesc} -proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = +proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = # We need to return a node to the actual value, # which can be modified. + assert sym.position != 0 or skResult == sym.kind var x = c - while x != nil: - if sym.kind == skResult and x.params.len > 0: - result = x.params[0] - if result == nil: result = emptyNode + while x != nil: + if sym.owner == c.prc: + result = x.slots[sym.position] + assert result != nil + if not aliasNeeded(result, flags): + result = copyTree(result) return - result = IdNodeTableGet(x.mapping, sym) - if result != nil and not aliasNeeded(result, flags): - result = copyTree(result) - if result != nil: return x = x.next - #internalError(sym.info, "cannot eval " & sym.name.s) + debug sym.owner + debug c.prc + internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position) result = raiseCannotEval(nil, sym.info) #result = emptyNode @@ -385,12 +399,12 @@ proc evalCall(c: PEvalContext, n: PNode): PNode = d.call = n var prc = n.sons[0] let isClosure = prc.kind == nkClosure - setlen(d.params, sonsLen(n) + ord(isClosure)) + setlen(d.slots, sonsLen(n) + ord(isClosure)) if isClosure: #debug prc result = evalAux(c, prc.sons[1], {efLValue}) if isSpecial(result): return - d.params[sonsLen(n)] = result + d.slots[sonsLen(n)] = result result = evalAux(c, prc.sons[0], {}) else: result = evalAux(c, prc, {}) @@ -408,21 +422,21 @@ proc evalCall(c: PEvalContext, n: PNode): PNode = for i in countup(1, sonsLen(n) - 1): result = evalAux(c, n.sons[i], {}) if isSpecial(result): return - d.params[i] = result - if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info) + d.slots[i] = result + if n.typ != nil: d.slots[0] = getNullValue(n.typ, n.info) when hasFFI: if sfImportc in prc.sym.flags and allowFFI in c.features: var newCall = newNodeI(nkCall, n.info, n.len) newCall.sons[0] = evalGlobalVar(c, prc.sym, {}) for i in 1 .. Date: Fri, 28 Jun 2013 23:40:09 +0200 Subject: [PATCH 79/83] got rid of debug calls --- compiler/evals.nim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/evals.nim b/compiler/evals.nim index 1689a37d8..944c8da61 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -365,9 +365,7 @@ proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = result = copyTree(result) return x = x.next - debug sym.owner - debug c.prc - internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position) + #internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position) result = raiseCannotEval(nil, sym.info) #result = emptyNode From 8f0d0250cd795dfaf4d956947ee84c5deeea52fd Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 29 Jun 2013 01:51:42 +0200 Subject: [PATCH 80/83] bugfix: typo --- compiler/evals.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/evals.nim b/compiler/evals.nim index 944c8da61..e33b091d5 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -358,7 +358,7 @@ proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = assert sym.position != 0 or skResult == sym.kind var x = c while x != nil: - if sym.owner == c.prc: + if sym.owner == x.prc: result = x.slots[sym.position] assert result != nil if not aliasNeeded(result, flags): From 43de61f37bb058968f443904c9998dea507ebab2 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 29 Jun 2013 09:08:57 +0200 Subject: [PATCH 81/83] bugfix: pragmas allowed for implicit generics --- compiler/semdata.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4c94d0812..72d5a5fef 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -187,9 +187,9 @@ proc newLib(kind: TLibKind): PLib = new(result) result.kind = kind #initObjectSet(result.syms) -proc addToLib(lib: PLib, sym: PSym) = - #ObjectSetIncl(lib.syms, sym); - if sym.annex != nil: LocalError(sym.info, errInvalidPragma) +proc addToLib(lib: PLib, sym: PSym) = + #if sym.annex != nil and not isGenericRoutine(sym): + # LocalError(sym.info, errInvalidPragma) sym.annex = lib proc makePtrType(c: PContext, baseType: PType): PType = From 1bdc0d1dd301a275a9cd5362018714a8fa2ee12e Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 29 Jun 2013 10:04:18 +0200 Subject: [PATCH 82/83] fixes lots of regressions --- compiler/evals.nim | 11 ++++++----- compiler/semexprs.nim | 5 +++-- compiler/semstmts.nim | 3 ++- compiler/transf.nim | 2 +- tests/run/tpegs.nim | 14 ++++++++------ 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/compiler/evals.nim b/compiler/evals.nim index e33b091d5..9655ef952 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -947,7 +947,7 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode = let typ = n.sons[1].sym.typ.skipTypes({tyTypeDesc}) case n.sons[0].sym.name.s.normalize of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferName)) + result = newStrNode(nkStrLit, typ.typeToString(preferExported)) result.typ = newType(tyString, context) result.info = n.info else: @@ -1493,20 +1493,21 @@ proc eval*(c: PEvalContext, n: PNode): PNode = else: stackTrace(c, result, errCannotInterpretNodeX, renderTree(n)) -proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode = +proc evalConstExprAux(module, prc: PSym, e: PNode, mode: TEvalMode): PNode = var p = newEvalContext(module, mode) var s = newStackFrame() s.call = e + s.prc = prc pushStackFrame(p, s) result = tryEval(p, e) if result != nil and result.kind == nkExceptBranch: result = nil popStackFrame(p) proc evalConstExpr*(module: PSym, e: PNode): PNode = - result = evalConstExprAux(module, e, emConst) + result = evalConstExprAux(module, nil, e, emConst) -proc evalStaticExpr*(module: PSym, e: PNode): PNode = - result = evalConstExprAux(module, e, emStatic) +proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode = + result = evalConstExprAux(module, prc, e, emStatic) proc setupMacroParam(x: PNode): PNode = result = x diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 94453be15..ff68d6b8e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -590,7 +590,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = call.add(a) #echo "NOW evaluating at compile time: ", call.renderTree if sfCompileTime in callee.flags: - result = evalStaticExpr(c.module, call) + result = evalStaticExpr(c.module, call, c.p.owner) if result.isNil: LocalError(n.info, errCannotInterpretNodeX, renderTree(call)) else: @@ -601,9 +601,10 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = proc semStaticExpr(c: PContext, n: PNode): PNode = let a = semExpr(c, n.sons[0]) - result = evalStaticExpr(c.module, a) + result = evalStaticExpr(c.module, a, c.p.owner) if result.isNil: LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) + result = emptyNode proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 64ef7f4d6..dc5c9341e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1139,9 +1139,10 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode = let a = semStmt(c, n.sons[0]) - result = evalStaticExpr(c.module, a) + result = evalStaticExpr(c.module, a, c.p.owner) if result.isNil: LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) + result = emptyNode elif result.kind == nkEmpty: result = newNodeI(nkDiscardStmt, n.info, 1) result.sons[0] = emptyNode diff --git a/compiler/transf.nim b/compiler/transf.nim index 82122d776..a81457b39 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -702,7 +702,7 @@ proc openTransf(module: PSym, filename: string): PTransf = result.module = module proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = - if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}: + if nfTransf in n.flags or prc.kind in {skTemplate}: result = n else: #when useEffectSystem: trackProc(prc, n) diff --git a/tests/run/tpegs.nim b/tests/run/tpegs.nim index efc6a8fa4..bdd8db0f8 100644 --- a/tests/run/tpegs.nim +++ b/tests/run/tpegs.nim @@ -1733,12 +1733,14 @@ when isMainModule: doAssert matches[0] == "a" else: doAssert false - - if match("abcdefg", peg"c {d} ef {g}", matches, 2): - doAssert matches[0] == "d" - doAssert matches[1] == "g" - else: - doAssert false + + block: + var matches: array[0..2, string] + if match("abcdefg", peg"c {d} ef {g}", matches, 2): + doAssert matches[0] == "d" + doAssert matches[1] == "g" + else: + doAssert false for x in findAll("abcdef", peg"{.}", 3): echo x From 651103ff320ef6f2a132be0a102749cefce725c9 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 29 Jun 2013 10:28:28 +0200 Subject: [PATCH 83/83] 2n attempt to fix niminst on the testing machines --- lib/wrappers/zip/libzip.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/wrappers/zip/libzip.nim b/lib/wrappers/zip/libzip.nim index 26a0ce67c..f25e45671 100644 --- a/lib/wrappers/zip/libzip.nim +++ b/lib/wrappers/zip/libzip.nim @@ -48,12 +48,13 @@ import times when defined(unix) and not defined(useLibzipSrc): - {.passl: "-lz".} when defined(macosx): {.pragma: mydll, dynlib: "libzip2.dylib".} else: {.pragma: mydll, dynlib: "libzip2.so(|.2|.1|.0)".} else: + when defined(unix): + {.passl: "-lz".} {.compile: "libzip_all.c".} {.pragma: mydll.}