/* * Copyright (c) 2003-2008 The University of Wroclaw. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the University may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System.Text; using Nemerle.Compiler; using Nemerle.Compiler.Parsetree; using System; using System.IO; using System.Globalization; using Nemerle.Assertions; namespace Nemerle { [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Class)] macro MarkOptions (t : TypeBuilder, attribute) { // iterate through members of this type and select only variant options foreach (ClassMember.TypeDeclaration (TopDeclaration.VariantOption as vo) in t.GetParsedMembers ()) { // add given custom attribute to this variant option vo.AddCustomAttribute (attribute) } } } namespace Nemerle.Utility { /// Adds property accessor for field. /// By default adds only getter. /// You can specify the following flags: /// WantSetter, Setter, Internal, Protected, Override, Virtual. /// Also you can specify the property name manualy (by default the name /// is generated from the field name). [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Field, Inherited = false, AllowMultiple = true)] macro Accessor (current_type : TypeBuilder, storage_field : ParsedField, params args : list [PExpr]) { def usage = "usage: Accessor (name, flags = MODIFIERS, get (MODIFIERS), set (MODIFIERS), " "attributes (LIST OF ATTRIBUTES)), where all sections are optional"; mutable setterMods = NemerleAttributes.None; mutable getterMods = NemerleAttributes.Public; mutable want_setter = false; mutable attributes = []; mutable oname = None (); def parse_opts (expr, allow_deprec) { match (expr) { | <[ $("WantSetter" : dyn) ]> | <[ $("Setter" : dyn) ]> => unless (allow_deprec) Message.Error ("WantSetter / Setter is not allowed outside 'flags' section"); want_setter = true; NemerleAttributes.None | <[ $("Internal" : dyn) ]> => NemerleAttributes.Internal | <[ $("Protected" : dyn) ]> => NemerleAttributes.Protected | <[ $("Override" : dyn) ]> => NemerleAttributes.Override | <[ $("Virtual" : dyn) ]> => NemerleAttributes.Virtual | <[ None ]> => NemerleAttributes.Extern // will be removed | <[ $e1 | $e2 ]> => (parse_opts (e1, allow_deprec) | parse_opts (e2, allow_deprec)); | e => Message.FatalError ($ "bad accessor option, $e"); } } foreach (a in args) { | <[ $(n : dyn) ]> => oname = Some (n) | <[ flags = $opts ]> => def opts = parse_opts (opts, true); getterMods |= opts; when (want_setter) setterMods |= getterMods; | <[ set ($opts) ]> => setterMods |= parse_opts (opts, false) | <[ get ($opts) ]> => getterMods |= parse_opts (opts, false) | <[ attributes (..$attrs) ]> => attributes += attrs | _ => Message.FatalError (usage); } // __some_foo__bar ==> SomeFooBar def transformed_name = { def sb = StringBuilder (); mutable next_upper = true; foreach (ch in storage_field.Name) if (ch == '_') { next_upper = true; } else if (next_upper) { _ = sb.Append (char.ToUpperInvariant (ch)); next_upper = false; } else _ = sb.Append (ch); sb.ToString () } def name = oname.WithDefault (transformed_name); when (getterMods %&& NemerleAttributes.Extern) getterMods = NemerleAttributes.None; def take_minimum_access (mods) { if (mods %&& NemerleAttributes.Protected && !(mods %&& NemerleAttributes.Internal)) NemerleAttributes.Protected else if (mods %&& NemerleAttributes.Internal && !(mods %&& NemerleAttributes.Protected)) NemerleAttributes.Internal else if (mods %&& NemerleAttributes.Protected && mods %&& NemerleAttributes.Internal) NemerleAttributes.Internal | NemerleAttributes.Protected else if (mods %&& NemerleAttributes.Public) NemerleAttributes.Public else NemerleAttributes.None } getterMods = (getterMods & ~NemerleAttributes.AccessModifiers) | take_minimum_access (getterMods); setterMods = (setterMods & ~NemerleAttributes.AccessModifiers) | take_minimum_access (setterMods); def fieldref = <[ $(storage_field.ParsedName : name) ]>; def setterAttrs = Modifiers (setterMods, [<[ System.Diagnostics.DebuggerStepThroughAttribute ]>]); def getterAttrs = Modifiers (getterMods, [<[ System.Diagnostics.DebuggerStepThroughAttribute ]>]); def propAttrs = Modifiers (NemerleAttributes.None, attributes); def prop = if (setterMods != NemerleAttributes.None && getterMods != NemerleAttributes.None) <[ decl: ..$propAttrs $(name : dyn) : $(storage_field.ty) { ..$setterAttrs set { $fieldref = value; } ..$getterAttrs get { $fieldref } } ]> else if (getterMods != NemerleAttributes.None) <[ decl: ..$propAttrs $(name : dyn) : $(storage_field.ty) { ..$getterAttrs get { $fieldref } } ]>; else if (setterMods != NemerleAttributes.None) <[ decl: ..$propAttrs $(name : dyn) : $(storage_field.ty) { ..$setterAttrs set { $fieldref = value } } ]>; else Message.FatalError ("no accessor will be visible with specified modifiers"); prop.SetEnv (storage_field.Env); def totalMods = getterMods | setterMods; if (totalMods %&& NemerleAttributes.Public) prop.Attributes = (totalMods & ~NemerleAttributes.AccessModifiers) | NemerleAttributes.Public; else if (totalMods %&& NemerleAttributes.Internal && totalMods %&& NemerleAttributes.Protected) prop.Attributes = (totalMods & ~NemerleAttributes.AccessModifiers) | NemerleAttributes.Internal | NemerleAttributes.Protected; else if (totalMods %&& NemerleAttributes.Internal) prop.Attributes = (totalMods & ~NemerleAttributes.AccessModifiers) | NemerleAttributes.Internal; else if (totalMods %&& NemerleAttributes.Protected) prop.Attributes = (totalMods & ~NemerleAttributes.AccessModifiers) | NemerleAttributes.Protected; else prop.Attributes = (totalMods & ~NemerleAttributes.AccessModifiers) | NemerleAttributes.Private; when (storage_field.Attributes %&& NemerleAttributes.Static) prop.Attributes |= NemerleAttributes.Static; current_type.Define (prop); } [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Field, Inherited = false, AllowMultiple = true)] macro FlagAccessor (current_type : TypeBuilder, storage_field : ParsedField, params args : list [PExpr]) { def usage = "usage: FlagAccessor (name1, name2, ..., flags = SOMEFLAGS), " "where flags are optional"; mutable opts = None (); def names = args.Filter (fun (_) { | <[ $("flags" : dyn) = $o ]> => if (opts.IsNone) opts = Some (o); else Message.FatalError (usage); false | <[ $(_ : name) ]> => true | _ => Message.FatalError (usage) }); mutable want_setter = false; mutable want_internal = false; mutable want_protected = false; def parse_opts (expr) { | <[ $("WantSetter" : dyn) ]> => want_setter = true | <[ $("Setter" : dyn) ]> => want_setter = true | <[ $("Internal" : dyn) ]> => want_internal = true; | <[ $("Protected" : dyn) ]> => want_protected = true; | <[ $e1 | $e2 ]> => parse_opts (e1); parse_opts (e2); | e => Message.FatalError ($ "bad accessor option, $e"); } match (opts) { | Some (e) => parse_opts (e) | None => {} } def fieldref = <[ $(storage_field.ParsedName : name) ]>; def enumref = storage_field.ty; foreach (<[ $(name : dyn) ]> in names) { def flag = <[ $enumref . $(name : dyn) ]>; def prop = if (want_setter) <[ decl: public $(name : dyn) : bool { get { $fieldref %&& $flag } set { if (value) $fieldref |= $flag; else $fieldref &= ~ $flag } } ]> else <[ decl: public $(name : dyn) : bool { get { $fieldref %&& $flag } } ]>; when (want_internal) { prop.Attributes &= ~NemerleAttributes.Public; prop.Attributes |= NemerleAttributes.Internal; } when (want_protected) { prop.Attributes &= ~NemerleAttributes.Public; prop.Attributes |= NemerleAttributes.Protected; } when (storage_field.Attributes %&& NemerleAttributes.Static) prop.Attributes |= NemerleAttributes.Static; current_type.Define (prop); } } [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Assembly)] macro AssemblyVersionFromSVN (val : string, fallbackToDate : bool = true) { def svnidx = val.IndexOf ("SVN", System.StringComparison.InvariantCultureIgnoreCase); def val = if (svnidx != -1) { def myLoc = Nemerle.Macros.Manager ().CurrentLocation (); def path = if (myLoc.IsSourceAvailable) Path.GetDirectoryName (Path.GetFullPath (myLoc.File)) else Directory.GetCurrentDirectory (); mutable svnrev = SVNRevisionHelper.FindRevision (path); when (svnrev < 0) svnrev = SVNRevisionHelper.GetRevisionGeneric (path); if (svnrev < 0) if (fallbackToDate) val.Substring (0, svnidx) + ((DateTime.Now.Year % 100).ToString () + DateTime.Now.DayOfYear.ToString ("000")) + val.Substring (svnidx + 3) else Message.FatalError (myLoc, $"could not obtain revision from SVN metadata (error code $(-svnrev))"); else val.Substring (0, svnidx) + svnrev.ToString () + val.Substring (svnidx + 3) } else val; Nemerle.Macros.Manager().Hierarchy.AddAssemblyAttribute (Nemerle.Macros.Manager().CoreEnv, <[ System.Reflection.AssemblyVersion ($(val : string)) ]>); } /// based on svn://rsdn.ru/Janus/trunk/SvnRevision module SVNRevisionHelper { SVN_DIRECTORY_NAME_COMMON = ".svn"; SVN_DIRECTORY_NAME_ALT = "_svn"; SVN_ENTRIES_FILE_NAME = "entries"; SVN_FORMAT_FILE_NAME = "format"; XPATH_REVISION = @"ns:wc-entries/ns:entry/@revision"; public FindRevision(current : string) : int { def dir = Path.Combine (current, SVN_DIRECTORY_NAME_COMMON); if (Directory.Exists (dir)) GetRevisionDetectFormat (dir) else { def dir = Path.Combine (current, SVN_DIRECTORY_NAME_ALT); if (Directory.Exists (dir)) GetRevisionDetectFormat (dir) else -1 } } private GetRevisionDetectFormat ([NotNull] path : string) : int { def filePath = Path.Combine (path, SVN_FORMAT_FILE_NAME); if (File.Exists (filePath)) { using (stream = FileStream (filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { def bytes = array (stream.Length :> int); // Last symbol is '\n' _ = stream.Read(bytes, 0, bytes.Length); match (System.Text.Encoding.ASCII.GetString (bytes).Trim ()) { | "4" => GetRevisionVer4 (path) | "8" | "9" => GetRevisionVer8 (path) | _ => -10 } } } else -9 } private GetRevisionVer4 (path : string) : int { def filePath = Path.Combine (path, SVN_ENTRIES_FILE_NAME); if (!File.Exists (filePath)) -6 else using (reader = StreamReader (filePath)) { def doc = System.Xml.XPath.XPathDocument (reader); def nav = doc.CreateNavigator (); def manager = System.Xml.XmlNamespaceManager (nav.NameTable); manager.AddNamespace ("ns", "svn:"); def expr = nav.Compile (XPATH_REVISION); expr.SetContext (manager); def iterator = nav.Select (expr); if (iterator.MoveNext ()) try { int.Parse (iterator.Current.Value, NumberStyles.Integer, CultureInfo.InvariantCulture) } catch { | _e is FormatException => -8 } else -7 } } private GetRevisionVer8 (path : string) : int { def filePath = Path.Combine (path, SVN_ENTRIES_FILE_NAME); if (!File.Exists (filePath)) -3 else try { using(sr = StreamReader (filePath)) { def loop (lineCounter) { def line = sr.ReadLine (); if (line != null) if (lineCounter == 3) int.Parse (line, NumberStyles.Integer, CultureInfo.InvariantCulture) else loop (lineCounter + 1) else -5 } loop (0); } } catch { _e is FormatException => -4 } } public GetRevisionGeneric (path : string) : int { // Execute "svn info" def process = System.Diagnostics.Process (); process.StartInfo.UseShellExecute = false; process.StartInfo.FileName = "svn"; process.StartInfo.Arguments = "info"; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.WorkingDirectory = path; // Read svn output line by line until regex is matched def loop(reader) : int { match (reader.ReadLine ()) { | null => -5 | line => def pattern = @"Revision:\s+(?\d+)"; def regex = System.Text.RegularExpressions.Regex (pattern); def mc = regex.Match (line); mutable revision; if (mc.Success && int.TryParse (mc.Groups["rev"].Value, out revision)) revision; else loop (reader); } } try { _ = process.Start(); def revision = loop (process.StandardOutput); // Wait for svn client process to terminate unless (process.WaitForExit (2000)) process.Kill (); revision; } catch { | _ => -4; } } } [Nemerle.MacroUsage (Nemerle.MacroPhase.WithTypedMembers, Nemerle.MacroTargets.Class, Inherited = false, AllowMultiple = true)] macro ExtensionPattern (par : TypeBuilder, e) { def get_name (e) { | <[ $(id : dyn) ]> => Some (id) | _ => None () } match (e) { | <[ $(id : dyn) ( .. $ids ) = $pat ]> when ids.ForAll (fun (x) { get_name (x).IsSome }) => def ids = ids.Map (get_name).Map (Option.UnSome); par.GetModifiers ().AddCustomAttribute (<[ Nemerle.Internal.ExtensionPatternEncodingAttribute ( $(id : string), $(ids.ToString (",") : string), $(pat.ToString () : string)) ]>); def ext = ExtensionPattern (parent = par, name = id, pattern = pat, identifiers = ids); par.AddExtensionPattern (ext) | _ => Message.FatalError ("ExtensionPattern syntax is ``name (id_1, " "..., id_n) = pattern''") } } public module ListComprehensionHelper { [Nemerle.Macros.Hygienic] public ExpandRange (inrange : PExpr, acc : PExpr) : option [PExpr] { match (inrange) { | <[ $pat in $[$first, $second .. $last] ]> | <[ $pat in [$first, $second .. $last] ]> => Some (<[ mutable i = $first; mutable delta = $second - i; def last = $last; mutable cond = if (delta < 0) i >= last else i <= last; def pre_last = unchecked (last - delta); when (delta < 0 && pre_last < last || delta > 0 && pre_last > last) // we overflowed delta = -delta; while (cond) { def $pat = i; if (delta < 0) cond = i >= pre_last; else cond = i <= pre_last; unchecked (i += delta); $acc; } ]>) | <[ $pat in $[$first .. $last] ]> | <[ $pat in [$first .. $last] ]> => Some (<[ mutable i = $first; def last = $last; mutable cond = i <= last; mutable pre_last = last; unchecked (pre_last--); // can't use (last - 1) since 1 might change/widen type def overflowed = pre_last > last; while (cond) { def $pat = i; if (overflowed) cond = i >= pre_last; else cond = i <= pre_last; unchecked (i++); $acc; } ]>) | _ => None () } } } macro ExpandListComprehension (params exprs : list [PExpr]) { def adder = <[ def cell = $(exprs.Head) :: []; if (head == null) { head = cell; tail = cell; } else { _N_skip_write_check (tail.tl) = cell; tail = cell; } ]>; def loops = exprs.Tail.Rev ().FoldLeft (adder, fun (e, acc) { match (ListComprehensionHelper.ExpandRange (e, acc)) { | Some (expr) => expr | None => match (e) { | <[ $e1 in $e2 ]> => <[ foreach ($e1 in $e2) $acc ]> | cond => <[ when ($cond) $acc ]> } } }); match (exprs) { | [<[ $second .. $last ]>] => <[ $[ x | x in [$second .. $last]] ]> | [<[ $first ]>, <[ $second .. $last ]>] => <[ $[ x | x in [$first, $second .. $last]] ]> | _ => <[ mutable head = null; mutable tail = null; $loops; if (head == null) [] else head ]> } } }