[svn] r7812: nemerle/trunk: lib/narray.n lib/nstring.n macros/io.n
macros/string.n ncc/Makefile ncc/testsu...
VladD2
svnadmin at nemerle.org
Thu Oct 18 13:18:20 CEST 2007
Log:
Refactoring of io.n. NemerleStringTemplate and spliced string helpers moved into string.n
Author: VladD2
Date: Thu Oct 18 13:18:17 2007
New Revision: 7812
Added:
nemerle/trunk/macros/string.n
Modified:
nemerle/trunk/lib/narray.n
nemerle/trunk/lib/nstring.n
nemerle/trunk/macros/io.n
nemerle/trunk/ncc/Makefile
nemerle/trunk/ncc/testsuite/ (props changed)
Modified: nemerle/trunk/lib/narray.n
==============================================================================
--- nemerle/trunk/lib/narray.n (original)
+++ nemerle/trunk/lib/narray.n Thu Oct 18 13:18:17 2007
@@ -57,6 +57,49 @@
true
}
+ public static Last[T](this lst : SCG.IList[T]) : T
+ {
+ lst[lst.Count - 1]
+ }
+
+ public static Last[T](this lst : array[T]) : T
+ {
+ lst[lst.Length - 1]
+ }
+
+ public static Last[T](this lst : SCG.IEnumerable[T]) : T
+ {
+ mutable exists = false;
+ mutable cur;
+
+ foreach (e in lst)
+ {
+ exists = true;
+ cur = e;
+ }
+
+ if (exists) cur
+ else throw System.IndexOutOfRangeException();
+ }
+
+ public static First[T](this lst : SCG.IList[T]) : T
+ {
+ lst[0]
+ }
+
+ public static First[T](this lst : array[T]) : T
+ {
+ lst[0]
+ }
+
+ public static First[T](this lst : SCG.IEnumerable[T]) : T
+ {
+ foreach (e in lst)
+ return e;
+
+ throw System.IndexOutOfRangeException();
+ }
+
// Lazy functions
//
Modified: nemerle/trunk/lib/nstring.n
==============================================================================
--- nemerle/trunk/lib/nstring.n (original)
+++ nemerle/trunk/lib/nstring.n Thu Oct 18 13:18:17 2007
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (c) 2003, 2004 The University of Wroclaw.
* All rights reserved.
*
@@ -28,6 +28,7 @@
using Nemerle.Collections;
using SCG = System.Collections.Generic;
+using System.Diagnostics.Debug;
namespace Nemerle.Utility
{
@@ -440,4 +441,167 @@
loop (count)
}
}
+
+
+ // TODO: Слить этот класс с предыдущим!
+ public module StringBuilderEx
+ {
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ indent : string,
+ convert : T -> string
+ )
+ : void
+ {
+ mutable firstTime = true;
+
+ Assert(true);
+
+ foreach (elem in seq)
+ {
+ if (firstTime)
+ firstTime = false;
+ else
+ {
+ def pos = builder.Length;
+ _ = builder.Append(seperator);
+ _ = builder.Replace("\n", indent, pos, seperator.Length);
+ }
+
+ def str = convert(elem).Replace("\n", indent);
+ _ = builder.Append(str);
+ }
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ indent : string
+ )
+ : void
+ {
+ mutable firstTime = true;
+
+ Assert(true);
+
+ foreach (elem in seq)
+ {
+ if (firstTime)
+ firstTime = false;
+ else
+ {
+ def pos = builder.Length;
+ _ = builder.Append(seperator);
+ _ = builder.Replace("\n", indent, pos, seperator.Length);
+ }
+
+ def str = elem.ToString().Replace("\n", indent);
+ _ = builder.Append(str);
+ }
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (this builder : NStringBuilder, seq : SCG.IEnumerable [T], seperator : string) : void
+ {
+ Assert(false);
+ mutable firstTime = true;
+
+ foreach (elem in seq)
+ {
+ if (firstTime)
+ firstTime = false;
+ else
+ _ = builder.Append(seperator);
+
+ _ = builder.Append(elem);
+ }
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ convert : T -> string
+ )
+ : void
+ {
+ Assert(false);
+ mutable firstTime = true;
+
+ foreach (elem in seq)
+ {
+ if (firstTime)
+ firstTime = false;
+ else
+ _ = builder.Append(seperator);
+
+ def str = convert(elem);
+ _ = builder.Append(str);
+ }
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (
+ this builder : NStringBuilder,
+ seq : SCG.IEnumerable [T],
+ seperator : string,
+ indent : string,
+ convert : T -> void
+ )
+ : void
+ {
+ Assert(true);
+ mutable firstTime = true;
+
+ foreach (elem in seq)
+ {
+ if (firstTime)
+ firstTime = false;
+ else
+ {
+ def pos = builder.Length;
+ _ = builder.Append(seperator);
+ _ = builder.Replace("\n", indent, pos, seperator.Length);
+ }
+
+ convert(elem);
+ }
+ }
+
+ /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
+ /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
+ /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
+ /// <param name="l">A list. </param>
+ /// <param name="sep">The string used as element separator. </param>
+ public AppendSeq[T] (this builder : NStringBuilder, seq : list [T], seperator : string) : void
+ {
+ _ = builder.AppendList(seq, seperator);
+ }
+ }
}
Modified: nemerle/trunk/macros/io.n
==============================================================================
--- nemerle/trunk/macros/io.n (original)
+++ nemerle/trunk/macros/io.n Thu Oct 18 13:18:17 2007
@@ -44,7 +44,6 @@
using PT = Nemerle.Compiler.Parsetree;
using BF = System.Reflection.BindingFlags;
using SCG = System.Collections.Generic;
-using SB = Ext;//Nemerle.Utility.NStringBuilderExtensions;
namespace Nemerle.IO
{
@@ -399,756 +398,5 @@
};
iter_through (parse_format (format), parms.Length, []);
}
-
-/*
-
- /// If string literal is supplied, then prints it to System.Console, replacing all
- /// occurences of $id with id.ToString () invocation
- /// If any other expression is supplied, it is equivalent to System.Console.Write
- macro print (value)
- {
- match (value) {
- | <[ $(str : string) ]> =>
- def seq = List.RevMap (make_splice_distribution (str, Macros.ImplicitCTX().Env), fun (x) {
- <[ Console.Write ($x) ]>
- });
- <[ {.. $seq } ]>
-
- | _ =>
- <[ Console.Write ($value) ]>
- }
- }
-
- macro sprint (str : string)
- {
- if (string.IsNullOrEmpty (str))
- {
- Message.Warning ("empty spliced string");
- <[ string.Empty ]>
- }
- else
- {
- def seq = make_splice_distribution (str, Macros.ImplicitCTX().Env).Rev ();
-
- match (seq)
- {
- | [PT.PExpr.Literal as lit] =>
- Message.Warning ($"spliced string without splices: '$str'");
- lit
-
- | _ => <[ string.Concat (..$seq) ]>
- }
- }
- }
-
- /// Writes text to given System.IO.TextWriter
- macro fprint (writer, str : string)
- {
- def seq = List.RevMap (make_splice_distribution (str, Macros.ImplicitCTX().Env), fun (x) {
- <[ writer_v.Write ($x) ]>
- });
- <[ def writer_v = $writer : IO.TextWriter; {.. $seq }; ]>
- }
-*/
- }
-}
-
-namespace StringTemplate
-{
- using StringTemplate.Helper;
-
- [MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Class, Inherited = true)]
- macro StringTemplateGroup(tb : TypeBuilder)
- {
- Helper2.BeforeTypedMembers(tb, Nemerle.Macros.ImplicitCTX());
- }
-
- [MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Class, Inherited = true)]
- macro StringTemplateGroup(tb : TypeBuilder)
- {
- Helper2.WithTypedMembers(tb, Nemerle.Macros.ImplicitCTX());
- }
-
- internal module Helper2
- {
- StSuffix = "__StImpl";
-
- internal BeforeTypedMembers(tb : TypeBuilder, ctx : Typer) : void
- {
- ctx.Env.Manager.MacroColors.PushUseSiteColor();
- try
- {
- // Add <MethodName>__StImpl method into STG.
-
- def members1 = tb.GetParsedMembers(true);
- def ideTest = m => m.Tokens != null // BUG in compiler! Workaround!
- && m.Tokens is Token.BracesGroup(Token.LooseGroup(Token.StringLiteral(_)));
- def compilerTest = m => m.Body is PExpr.Sequence([PExpr.Literal(Literal.String(_))]);
- def test = if (ctx.Manager.IsIntelliSenseMode) ideTest else compilerTest;
- def members2 = members1.Filter(_ => { | m is ClassMember.Function when test(m) => true | _ => false});
-
- foreach (method is ClassMember.Function in members2)
- {
- def h = method.header;
- def workMethName = method.Name + StSuffix;
- def newMethodAst = <[ decl:
- protected virtual $(workMethName : usesite) [..$(h.typarms.tyvars)] (..$(h.Parameters)) : void
- where ..$(h.typarms.constraints)
- {
- }]>;
- _ = tb.DefineWithSource(newMethodAst);
- method.UserData = newMethodAst;
- method.Attributes |= NemerleAttributes.Static | NemerleAttributes.Public;
- }
- }
- finally { ctx.Env.Manager.MacroColors.PopColor(); }
- }
-
- internal WithTypedMembers(tb : TypeBuilder, ctx : Typer) : void
- {
- ctx.Env.Manager.MacroColors.PushUseSiteColor();
- try
- {
- tb.Define(<[ decl: protected _builder : StringBuilder = StringBuilder(1024); ]>);
- tb.Define(<[ decl: protected mutable _indent : string = "\n"; ]>);
- tb.Define(<[ decl: protected _indentLenStack : Stack[int] = Stack(); ]>);
- _ = tb.DefineWithSource(<[ decl:
- protected AddIndent(indent : string) : void
- {
- _indentLenStack.Add(indent.Length);
- _indent += indent;
- }
- ]>);
- _ = tb.DefineWithSource(<[ decl:
- protected RemoveLastIndent() : void
- {
- _indent = _indent.Substring(0, _indent.Length - _indentLenStack.Pop());
- }
- ]>);
- _ = tb.DefineWithSource(<[ decl:
- protected PrintNewLineAndIndent() : void
- {
- _ = _builder.Append(_indent);
- }
- ]>);
-
- def beforeBodyTypingHandler(mb : MethodBuilder, pBody) : PExpr
- {
- def pBody = pBody;
-
- match (pBody)
- {
- | PExpr.Sequence([PExpr.Literal(Literal.String(str)) as lit]) =>
-
- // 1. Сгенерировать и подставить тело для х__StImpl-метода.
- // 2. В тело данного метода подставить код вызова этого метода.
-
- def findCorrespondMethod()
- {
- def stAst = mb.Ast.UserData;
- def stMethods = tb.GetMethods(BF.DeclaredOnly | BF.Instance | BF.NonPublic).FindAll(
- _ => { | m is MethodBuilder when m.Ast == stAst => true | _ => false });
-
- match (stMethods)
- {
- | [m is MethodBuilder] => m
- | _ => Util.ice($"Found to many correspond 'string template' methods: ..$stMethods");
- }
- }
-
- def expr = MakeStringTemplateExpr(mb, str, lit.Location, ctx);
- def m = findCorrespondMethod();
-
- m.Body = expr;
-
- def h = mb.Ast.header;
- def workMethName = mb.Ast.Name + StSuffix;
-
- <[ def instance = $(tb.FullName : usesite)();
- instance.$(workMethName : usesite)(..$(h.ParametersReferences));
- instance._builder.ToString() ]>
-
- | _ => pBody
- }
- }
-
- foreach (mb is MethodBuilder in tb.GetMethods(BF.DeclaredOnly | BF.Static | BF.Public))
- when (mb.Ast.UserData is ClassMember.Function)
- mb.AddBeforeBodyTypingHandler(beforeBodyTypingHandler);
- }
- finally { ctx.Env.Manager.MacroColors.PopColor(); }
- }
-
- MakeStringTemplateExpr(mb : MethodBuilder, template : string, loc : Location, ctx : Typer) : PExpr
- {
- def (template, loc) = SquareString(template, loc);
- def makeEllipsisSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
- {
- if (isComplexExpr)
- {
- def pExpr = MainParser.ParseExpr (env, expr, loc);
- match (pExpr)
- {
- | <[ $seqExpr; $sep; $cnvFuncExpr; ]> =>
-
- def loc = loc;
- _ = loc;
-
- // If cnvFuncExpr is a StrinTemplete reference,
- // replace it by <methodName>__StImpl-method.
-
- def mb = mb;
- // Find __StImpl-method corresponding to mb.
- def corespStImplMethod = (mb.Ast.UserData :> ClassMember.Function).Builder;
- // Type fake expression to determinate what is cnvFuncExpr (it type).
- def expr = <[ NCollectionsUtils.MapLazy($seqExpr, $cnvFuncExpr) ]>;
- def typer = Typer(corespStImplMethod);
-
- _ = typer.TypeExpr(expr);
-
- match (cnvFuncExpr.TypedObject)
- {
- | TExpr.StaticRef(_, m is MethodBuilder, _)
- when m.DeclaringType.Equals(mb.DeclaringType) =>
- match (m.Ast.UserData)
- {
- | coresp is ClassMember.Function =>
- <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, this.$(coresp.Name : usesite)); ]>
- | _ => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, $cnvFuncExpr); ]>
- }
- | _ => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, $cnvFuncExpr); ]>
- }
-
- | <[ $seqExpr; $sep; ]> => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent); ]>
- | _ => <[ SB.AppendSeq(_builder, $pExpr, ", ", _indent); ]>
- }
- }
- else if (expr == "this") <[ SB.AppendSeq(_builder, this, ", ", _indent); ]>
- else <[ SB.AppendSeq(_builder, $(expr : usesite), ", ", _indent); ]>
- }
- def makeSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
- {
- def makeExpr(pExpr)
- {
- <[
- if (_indent.Length > 1)
- {
- def pos = _builder.Length;
- _ = _builder.Append($pExpr);
- _ = _builder.Replace("\n", _indent, pos, _builder.Length - pos);
- }
- else
- _ = _builder.Append($pExpr);
- ]>
- }
- if (isComplexExpr)
- {
- env.Manager.MacroColors.PushUseSiteColor ();
- try
- {
- def _builder = StringBuilder();
- def pExpr = MainParser.ParseExpr(env, expr);
- makeExpr(pExpr);
- }
- finally { env.Manager.MacroColors.PopColor (); }
- } else if (expr == "this") makeExpr(<[ this ]>);
- else makeExpr(<[ $(expr : usesite) ]>);
- }
-
- def exprs = Helper.make_splice_distribution2(template, ctx.Env, makeSplaceExpr, makeEllipsisSplaceExpr);
- //def isNeedOptimize(_ : list[StrPart])
- //{
- //| Lit(_) :: Lit(_) :: _ => true
- //| _ :: tail => isNeedOptimize(tail)
- //| [] => false
- //}
- //def optimize(_ : list[StrPart])
- //{
- //| Lit(s1) :: Lit(s2) :: tail => optimize(StrPart.Lit(s2 + s1) :: tail)
- //| x :: tail => x :: optimize(tail)
- //| [] => []
- //}
- //def exprs = if (isNeedOptimize(exprs)) optimize(exprs) else exprs;
- def res = exprs.RevMap(e =>
- match (e : StrPart)
- {
- | Lit(str) => <[ _ = _builder.Append($(str : string)); ]>
- | NewLine => <[ _ = PrintNewLineAndIndent(); ]>
- | Expr(expr) => expr
- | IndentedExpr(indent, expr) => <[
- def indent = $(indent : string);
- _ = _builder.Append(indent);
- AddIndent(indent);
- $expr;
- RemoveLastIndent(); ]>
- });
-
- <[ { ..$res } ]>
- }
-
- public static Last[T](this lst : SCG.IList[T]) : T
- {
- lst[lst.Count - 1]
- }
-
- /// If we have string like this
- /// <#
- /// SomeText1
- /// SomeText2
- /// #>
- /// this function convert it to
- /// "SomeText1\nSomeText2"
- SquareString(str : string, loc : Location) : string * Location
- {
- match (str.LastIndexOfAny(array['\r', '\n']))
- {
- | -1 => (str, Location(loc.FileIndex, loc.Line, loc.Column,
- loc.EndLine, loc.EndColumn))
- | _ =>
- def rows = str.Split(array["\r\n", "\n", "\r"], StringSplitOptions.None);
- when (rows.Length <= 2)
- Message.Error(loc, "The multiline String Template should cantain 3 and more row. "
- "(First and last line in multiline String Template ignored.)");
- def prefix = rows.Last();
- def firstIndex = if (rows[0].ForAll(char.IsWhiteSpace)) 1 else 0;
- def sb = StringBuilder(str.Length - prefix.Length
- - if (firstIndex == 1) rows[0].Length else 0);
- def len = rows.Length - 1;
- mutable isIndentMismatch = false;
- for (mutable i = firstIndex; i < len; i++)
- {
- def row = rows[i];
- if (row.StartsWith(prefix, StringComparison.InvariantCulture))
- _ = sb.AppendLine(row.Substring(prefix.Length, row.Length - prefix.Length));
- else
- {
- Message.Error(Location(loc, loc.Line + i, 1, loc.Line + i, row.Length + 1),
- "Mismatch of the string template strBuilder characters.");
- isIndentMismatch = true;
- _ = sb.AppendLine(row);
- }
- }
-
- when (sb.Length > Environment.NewLine.Length)
- sb.Length -= Environment.NewLine.Length;
-
- when (isIndentMismatch)
- Message.Hint(Location(loc, loc.EndLine, 1, loc.EndLine, loc.EndColumn),
- "Please, make sure that all of the strBuilder characters of your "
- "string template match the last line indentation.");
- // TODO: Локейшон вычисляется неверно. Переделать.
- (sb.ToString(), Location(loc.FileIndex,
- loc.Line + firstIndex, prefix.Length + 1,
- loc.EndLine - firstIndex, rows[len - 1].Length + 1)); // + 1 => Location coordinates 1 bound
- }
- }
-
- //public static Offset(this loc : Location, lineOffset : int, colOffset : int) : Location
- //{
- //Location(loc, loc.Line + lineOffset, loc.Column + colOffset,
- //loc.EndLine + lineOffset, loc.EndColumn + colOffset)
- //}
- }
-
- variant StrPart
- {
- | Lit { str : string; }
- | Expr { expr : PT.PExpr; }
- | NewLine
- | IndentedExpr { indent : string; expr : PT.PExpr; }
-
- public override ToString() : string
- {
- match (this)
- {
- | Lit(str) => $"Lit: '$str'"
- | Expr(expr) => $"Expr: $expr"
- | NewLine => "<\n>"
- | IndentedExpr(indent, expr) => $"IndentedExpr: '$expr' ('$indent')"
- }
- }
- }
-
- internal module Helper
- {
- public make_splice_distribution (str : string, env : GlobalEnv) : list [StrPart]
- {
- def makeEllipsisSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
- {
- if (isComplexExpr)
- {
- env.Manager.MacroColors.PushUseSiteColor ();
- try
- {
- def pExpr = MainParser.ParseExpr (env, expr);
- def makeSeqExpr(seqExpr, sepExpr, cnvFuncExpr)
- {
- <[ string.Join($sepExpr, NCollectionsUtils.MapToArray($seqExpr, $cnvFuncExpr)) ]>
- }
- match (pExpr)
- {
- | <[ $seqExpr; $sepExpr; $cnvFuncExpr; ]> => makeSeqExpr(seqExpr, sepExpr, cnvFuncExpr)
- | <[ $seqExpr; $sepExpr; ]> => makeSeqExpr(seqExpr, sepExpr, <[ Convert.ToString(_) ]>)
- | _ => makeSeqExpr(pExpr, <[ ", " ]>, <[ Convert.ToString(_) ]>)
- }
- }
- finally { env.Manager.MacroColors.PopColor (); }
- } else if (expr == "this")
- <[ string.Join(", ", NCollectionsUtils.MapToArray(this, Convert.ToString(_))) ]>
- else <[ string.Join(", ", NCollectionsUtils.MapToArray($(expr : usesite), Convert.ToString(_))) ]>;
- }
-
- def makeSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
- {
- if (isComplexExpr)
- {
- env.Manager.MacroColors.PushUseSiteColor ();
- try
- {
- def pExpr = MainParser.ParseExpr (env, expr);
- <[ Convert.ToString ($pExpr) ]>
- }
- finally { env.Manager.MacroColors.PopColor (); }
- } else if (expr == "this") <[ Convert.ToString (this) ]>
- else <[ Convert.ToString ($(expr : usesite)) ]>;
- }
-
- make_splice_distribution2 (str, env, makeSplaceExpr, makeEllipsisSplaceExpr)
- }
-
- /** for $(...) and ..$(...) expressions:
- - first evaluate expressions
- - store intermediate results in variables
- - return list of evaluators and reference variables in reverse order
- */
- public make_splice_distribution2 (
- str : string,
- env : GlobalEnv,
- makeSplaceExpr : GlobalEnv * string * bool -> PT.PExpr, // env * strExpr * isComplexExpr
- makeEllipsisSplaceExpr : GlobalEnv * string * bool -> PT.PExpr // env * strExpr * isComplexExpr
- )
- : list [StrPart]
- {
- mutable nestLevel = 0;
- mutable index = -1;
- mutable ch = if (str.Length > 0) str[0] else '\0';
- def strBuilder = StringBuilder();
- def peekN(n) { def next = index + n; if (next < str.Length) str[next] else '\0' }
- def peek() { peekN(1) }
- def next() { ch = peek(); index++; ch }
- def getStrFromBuilder() { def res = strBuilder.ToString(); strBuilder.Length = 0; res }
- def appendToBuilder(chr) { _ = strBuilder.Append(chr) }
- /// ~~~Parse expression based on nested brackets.
- /// Разбирает строку производя поиск закрывающей скобки.
- /// Вложенные скобки игнорируются. В итоге получается строка содержащая
- /// выражение заключенное в скбоки (которое так же может содержать вложенные скобки)
- /// и булево значение говорящее, содержится ли в строке простой идентификатор или варажение.
- /// Returns pare of (exprStr * isIdentifier)
- def parseExpressionStr() : string * bool
- {
- Assert(strBuilder.Length == 0, "strBuilder.Length == 0");
- Assert(ch == '(', "ch == '('");
- /// exprStr * allIsAlphNum
- def loop(balance, allIsAlphNum) : string * bool
- {
- match (peek())
- {
- // TODO: Обработать ситуацию когда скобка не закрыта! См. файл:
- // C:\MyProjects\Nemerle\nemerle\ncc\testsuite\negative\tyenf.n
- | '\0'
- | ')' when balance == 1 => _ = next(); (getStrFromBuilder(), allIsAlphNum)
- | ')' => appendToBuilder(next()); loop(balance - 1, false)
- | '(' => appendToBuilder(next()); loop(balance + 1, false)
- | curCh =>
- appendToBuilder(next());
- loop(balance, allIsAlphNum && (char.IsLetterOrDigit(curCh) || curCh == '_'))
- }
- }
-
- def (expr, allIsAlphNum) = loop(1, true);
- (expr, allIsAlphNum && expr.Length != 0 && expr != "_" && char.IsLetter(expr[0]))
- }
- def parseIdentifier()
- {
- Assert(strBuilder.Length == 0, "strBuilder.Length == 0");
-
- def loop()
- {
- def curCh = peek();
- match (curCh)
- {
- | '_'
- | _ when char.IsLetterOrDigit(curCh) => appendToBuilder(next()); loop()
- | _ => getStrFromBuilder()
- }
- }
-
- if (ch == '_' || char.IsLetter(ch))
- {
- appendToBuilder(ch);
- loop()
- }
- else ""
- }
-
- def loop (res : list[StrPart]) : list[StrPart]
- {
- nestLevel++; Diagnostics.Trace.Assert(nestLevel < 20000, "Prevent stack owerflow"); // Prevent stack owerflow
-
- // Завершает акомуляцию сиволов литерала и создает соотвествующую
- // лексему добавляя ее к началу списка лексем
- def endLiteral()
- {
- if (strBuilder.Length == 0)
- res
- else
- StrPart.Lit(getStrFromBuilder()) :: res
- }
- def isNextDollar(n)
- {
- def ch1 = peekN(n);
- if (char.IsWhiteSpace(ch1)) isNextDollar(n + 1)
- else ch1 == '$'
- }
- def isElipse() { peek() == '.' && isNextDollar(2) }
- def processNewLine() { loop (StrPart.NewLine() :: endLiteral()) }
-
- match (next())
- {
- | '\0' => endLiteral()
- | '$' => parceSpliceEx(endLiteral(), true)
- | '.' when isElipse() => index = str.IndexOf('$', index);
- parceSpliceEx(endLiteral(), false); // '..$'
- | '\r' when peek() == '\n' => _ = next(); processNewLine()
- | '\n' | '\r' => processNewLine()
- | x => appendToBuilder(x); loop(res)
- }
- }
- and parceSpliceEx(res, isSimple)
- {
- when (next() == '\0')
- {
- //Diagnostics.Trace.Assert(false);
- Message.Error ("lone `$' at the end of the format string");
- Nemerle.Imperative.Return ([StrPart.Lit("$")]);
- }
-
- def rtyIndent(res : list[StrPart], expr)
- {
- match (res)
- {
- | Lit(str) :: NewLine :: tail when str.ForAll(char.IsWhiteSpace) =>
- StrPart.IndentedExpr(str, expr) :: StrPart.NewLine() :: tail
- | _ => StrPart.Expr(expr) :: res
- }
- }
-
- def str = str; _ = str;
-
- if (ch == '(')
- {
- //def index1 = index; _ = index1;
- def (exprStr, isIdentifier) = parseExpressionStr();
-
- if (ch == '\0') // скобка не закрыта
- {
- def exprStr = "(" + exprStr;
- Message.Error($"no closing bracket found in `$(exprStr)' "
- "(the closing bracket in format string is probably missing)");
- }
- else when (exprStr.Trim().Length == 0)
- Message.Error("expression without content");
-
- def expr = if (isSimple) makeSplaceExpr(env, exprStr, !isIdentifier)
- else makeEllipsisSplaceExpr(env, exprStr, !isIdentifier);
- loop (rtyIndent(res, expr))
- }
- else if (ch == '$')
- loop (StrPart.Lit("$") :: res)
- else
- {
- //def index1 = index; _ = index1;
- def variableName = parseIdentifier();
-
- if (variableName == "")
- {
- appendToBuilder(ch);
- Message.Warning ("expected variable name or expression enclosed with (..) after $ in splice string");
- loop (StrPart.Lit("$") :: res)
- }
- else
- {
- def expr = if (isSimple) makeSplaceExpr(env, variableName, false)
- else makeEllipsisSplaceExpr(env, variableName, false);
- //def index1 = index; _ = index1;
- loop (rtyIndent(res, expr))
- }
- }
- }
-
- loop ([])
- }
- }
-}
-
-
-public module Ext
-{
- /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
- /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
- /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
- /// <param name="l">A list. </param>
- /// <param name="sep">The string used as element separator. </param>
- public AppendSeq[T] (
- this builder : NStringBuilder,
- seq : SCG.IEnumerable [T],
- seperator : string,
- indent : string,
- convert : T -> string
- )
- : void
- {
- mutable firstTime = true;
-
- Assert(true);
-
- foreach (elem in seq)
- {
- if (firstTime)
- firstTime = false;
- else
- {
- def pos = builder.Length;
- _ = builder.Append(seperator);
- _ = builder.Replace("\n", indent, pos, seperator.Length);
- }
-
- def str = convert(elem).Replace("\n", indent);
- _ = builder.Append(str);
- }
- }
-
- /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
- /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
- /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
- /// <param name="l">A list. </param>
- /// <param name="sep">The string used as element separator. </param>
- public AppendSeq[T] (
- this builder : NStringBuilder,
- seq : SCG.IEnumerable [T],
- seperator : string,
- indent : string
- )
- : void
- {
- mutable firstTime = true;
-
- Assert(true);
-
- foreach (elem in seq)
- {
- if (firstTime)
- firstTime = false;
- else
- {
- def pos = builder.Length;
- _ = builder.Append(seperator);
- _ = builder.Replace("\n", indent, pos, seperator.Length);
- }
-
- def str = elem.ToString().Replace("\n", indent);
- _ = builder.Append(str);
- }
- }
-
- /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
- /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
- /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
- /// <param name="l">A list. </param>
- /// <param name="sep">The string used as element separator. </param>
- public AppendSeq[T] (this builder : NStringBuilder, seq : SCG.IEnumerable [T], seperator : string) : void
- {
- Assert(false);
- mutable firstTime = true;
-
- foreach (elem in seq)
- {
- if (firstTime)
- firstTime = false;
- else
- _ = builder.Append(seperator);
-
- _ = builder.Append(elem);
- }
- }
-
- /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
- /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
- /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
- /// <param name="l">A list. </param>
- /// <param name="sep">The string used as element separator. </param>
- public AppendSeq[T] (
- this builder : NStringBuilder,
- seq : SCG.IEnumerable [T],
- seperator : string,
- convert : T -> string
- )
- : void
- {
- Assert(false);
- mutable firstTime = true;
-
- foreach (elem in seq)
- {
- if (firstTime)
- firstTime = false;
- else
- _ = builder.Append(seperator);
-
- def str = convert(elem);
- _ = builder.Append(str);
- }
- }
-
- /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
- /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
- /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
- /// <param name="l">A list. </param>
- /// <param name="sep">The string used as element separator. </param>
- public AppendSeq[T] (
- this builder : NStringBuilder,
- seq : SCG.IEnumerable [T],
- seperator : string,
- indent : string,
- convert : T -> void
- )
- : void
- {
- Assert(true);
- mutable firstTime = true;
-
- foreach (elem in seq)
- {
- if (firstTime)
- firstTime = false;
- else
- {
- def pos = builder.Length;
- _ = builder.Append(seperator);
- _ = builder.Replace("\n", indent, pos, seperator.Length);
- }
-
- convert(elem);
- }
- }
-
- /// <summary>Appends the string representation of a specified list items to the end of a <see cref="NStringBuilder"/> instance.</summary>
- /// <returns>A reference to the NStringBuilder instance after the append operation has completed.</returns>
- /// <param name="builder">A <see cref="NStringBuilder"/> instance pointer. </param>
- /// <param name="l">A list. </param>
- /// <param name="sep">The string used as element separator. </param>
- public AppendSeq[T] (this builder : NStringBuilder, seq : list [T], seperator : string) : void
- {
- _ = builder.AppendList(seq, seperator);
}
}
Added: nemerle/trunk/macros/string.n
==============================================================================
--- (empty file)
+++ nemerle/trunk/macros/string.n Thu Oct 18 13:18:17 2007
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 2003, 2004 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 StringTemplate;
+
+using Nemerle;
+using Nemerle.Compiler;
+using Nemerle.Collections;
+using Nemerle.Utility;
+using Nemerle.Compiler.Parsetree;
+
+using System;
+using System.Diagnostics.Trace;
+using System.Reflection;
+using System.Text;
+
+using TT = Nemerle.Compiler.Typedtree;
+using TExpr = Nemerle.Compiler.Typedtree.TExpr;
+using PT = Nemerle.Compiler.Parsetree;
+using BF = System.Reflection.BindingFlags;
+using SCG = System.Collections.Generic;
+using SB = Nemerle.Utility.StringBuilderEx;
+
+namespace StringTemplate
+{
+ using StringTemplate.Helper;
+
+ [MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Class, Inherited = true)]
+ macro StringTemplateGroup(tb : TypeBuilder)
+ {
+ Helper2.StringTemplateGroupBeforeTypedMembers(tb, Nemerle.Macros.ImplicitCTX());
+ }
+
+ [MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Class, Inherited = true)]
+ macro StringTemplateGroup(tb : TypeBuilder)
+ {
+ Helper2.StringTemplateGroupWithTypedMembers(tb, Nemerle.Macros.ImplicitCTX());
+ }
+
+ internal module Helper2
+ {
+ StSuffix = "__StImpl";
+
+ internal StringTemplateGroupBeforeTypedMembers(tb : TypeBuilder, ctx : Typer) : void
+ {
+ ctx.Env.Manager.MacroColors.PushUseSiteColor();
+ try
+ {
+ // Add <MethodName>__StImpl method into STG.
+
+ def members1 = tb.GetParsedMembers(true);
+ def ideTest = m => m.Tokens != null // BUG in compiler! Workaround!
+ && m.Tokens is Token.BracesGroup(Token.LooseGroup(Token.StringLiteral(_)));
+ def compilerTest = m => m.Body is PExpr.Sequence([PExpr.Literal(Literal.String(_))]);
+ def test = if (ctx.Manager.IsIntelliSenseMode) ideTest else compilerTest;
+ def members2 = members1.Filter(_ => { | m is ClassMember.Function when test(m) => true | _ => false});
+
+ foreach (method is ClassMember.Function in members2)
+ {
+ def h = method.header;
+ def workMethName = method.Name + StSuffix;
+ def newMethodAst = <[ decl:
+ protected virtual $(workMethName : usesite) [..$(h.typarms.tyvars)] (..$(h.Parameters)) : void
+ where ..$(h.typarms.constraints)
+ {
+ }]>;
+ _ = tb.DefineWithSource(newMethodAst);
+ method.UserData = newMethodAst;
+ method.Attributes |= NemerleAttributes.Static | NemerleAttributes.Public;
+ }
+ }
+ finally { ctx.Env.Manager.MacroColors.PopColor(); }
+ }
+
+ internal StringTemplateGroupWithTypedMembers(tb : TypeBuilder, ctx : Typer) : void
+ {
+ ctx.Env.Manager.MacroColors.PushUseSiteColor();
+ try
+ {
+ tb.Define(<[ decl: protected _builder : StringBuilder = StringBuilder(1024); ]>);
+ tb.Define(<[ decl: protected mutable _indent : string = "\n"; ]>);
+ tb.Define(<[ decl: protected _indentLenStack : Stack[int] = Stack(); ]>);
+ _ = tb.DefineWithSource(<[ decl:
+ protected AddIndent(indent : string) : void
+ {
+ _indentLenStack.Add(indent.Length);
+ _indent += indent;
+ }
+ ]>);
+ _ = tb.DefineWithSource(<[ decl:
+ protected RemoveLastIndent() : void
+ {
+ _indent = _indent.Substring(0, _indent.Length - _indentLenStack.Pop());
+ }
+ ]>);
+ _ = tb.DefineWithSource(<[ decl:
+ protected PrintNewLineAndIndent() : void
+ {
+ _ = _builder.Append(_indent);
+ }
+ ]>);
+
+ def beforeBodyTypingHandler(mb : MethodBuilder, pBody) : PExpr
+ {
+ def pBody = pBody;
+
+ match (pBody)
+ {
+ | PExpr.Sequence([PExpr.Literal(Literal.String(str)) as lit]) =>
+
+ // 1. Сгенерировать и подставить тело для х__StImpl-метода.
+ // 2. В тело данного метода подставить код вызова этого метода.
+
+ def findCorrespondMethod()
+ {
+ def stAst = mb.Ast.UserData;
+ def stMethods = tb.GetMethods(BF.DeclaredOnly | BF.Instance | BF.NonPublic).FindAll(
+ _ => { | m is MethodBuilder when m.Ast == stAst => true | _ => false });
+
+ match (stMethods)
+ {
+ | [m is MethodBuilder] => m
+ | _ => Util.ice($"Found to many correspond 'string template' methods: ..$stMethods");
+ }
+ }
+
+ def expr = MakeStringTemplateExpr(mb, str, lit.Location, ctx);
+ def m = findCorrespondMethod();
+
+ m.Body = expr;
+
+ def h = mb.Ast.header;
+ def workMethName = mb.Ast.Name + StSuffix;
+
+ <[ def instance = $(tb.FullName : usesite)();
+ instance.$(workMethName : usesite)(..$(h.ParametersReferences));
+ instance._builder.ToString() ]>
+
+ | _ => pBody
+ }
+ }
+
+ foreach (mb is MethodBuilder in tb.GetMethods(BF.DeclaredOnly | BF.Static | BF.Public))
+ when (mb.Ast.UserData is ClassMember.Function)
+ mb.AddBeforeBodyTypingHandler(beforeBodyTypingHandler);
+ }
+ finally { ctx.Env.Manager.MacroColors.PopColor(); }
+ }
+
+ MakeStringTemplateExpr(mb : MethodBuilder, template : string, loc : Location, ctx : Typer) : PExpr
+ {
+ def (template, loc) = Helper.SquareString(template, loc);
+ def makeEllipsisSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
+ {
+ if (isComplexExpr)
+ {
+ def pExpr = MainParser.ParseExpr (env, expr, loc);
+ match (pExpr)
+ {
+ | <[ $seqExpr; $sep; $cnvFuncExpr; ]> =>
+
+ def loc = loc;
+ _ = loc;
+
+ // If cnvFuncExpr is a StrinTemplete reference,
+ // replace it by <methodName>__StImpl-method.
+
+ def mb = mb;
+ // Find __StImpl-method corresponding to mb.
+ def corespStImplMethod = (mb.Ast.UserData :> ClassMember.Function).Builder;
+ // Type fake expression to determinate what is cnvFuncExpr (it type).
+ def expr = <[ NCollectionsUtils.MapLazy($seqExpr, $cnvFuncExpr) ]>;
+ def typer = Typer(corespStImplMethod);
+
+ _ = typer.TypeExpr(expr);
+
+ match (cnvFuncExpr.TypedObject)
+ {
+ | TExpr.StaticRef(_, m is MethodBuilder, _)
+ when m.DeclaringType.Equals(mb.DeclaringType) =>
+ match (m.Ast.UserData)
+ {
+ | coresp is ClassMember.Function =>
+ <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, this.$(coresp.Name : usesite)); ]>
+ | _ => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, $cnvFuncExpr); ]>
+ }
+ | _ => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent, $cnvFuncExpr); ]>
+ }
+
+ | <[ $seqExpr; $sep; ]> => <[ SB.AppendSeq(_builder, $seqExpr, $sep, _indent); ]>
+ | _ => <[ SB.AppendSeq(_builder, $pExpr, ", ", _indent); ]>
+ }
+ }
+ else if (expr == "this") <[ SB.AppendSeq(_builder, this, ", ", _indent); ]>
+ else <[ SB.AppendSeq(_builder, $(expr : usesite), ", ", _indent); ]>
+ }
+ def makeSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
+ {
+ def makeExpr(pExpr)
+ {
+ <[
+ if (_indent.Length > 1)
+ {
+ def pos = _builder.Length;
+ _ = _builder.Append($pExpr);
+ _ = _builder.Replace("\n", _indent, pos, _builder.Length - pos);
+ }
+ else
+ _ = _builder.Append($pExpr);
+ ]>
+ }
+ if (isComplexExpr)
+ {
+ env.Manager.MacroColors.PushUseSiteColor ();
+ try
+ {
+ def _builder = StringBuilder();
+ def pExpr = MainParser.ParseExpr(env, expr);
+ makeExpr(pExpr);
+ }
+ finally { env.Manager.MacroColors.PopColor (); }
+ } else if (expr == "this") makeExpr(<[ this ]>);
+ else makeExpr(<[ $(expr : usesite) ]>);
+ }
+
+ def exprs = Helper.make_splice_distribution2(template, ctx.Env, makeSplaceExpr, makeEllipsisSplaceExpr);
+ //def isNeedOptimize(_ : list[StrPart])
+ //{
+ //| Lit(_) :: Lit(_) :: _ => true
+ //| _ :: tail => isNeedOptimize(tail)
+ //| [] => false
+ //}
+ //def optimize(_ : list[StrPart])
+ //{
+ //| Lit(s1) :: Lit(s2) :: tail => optimize(StrPart.Lit(s2 + s1) :: tail)
+ //| x :: tail => x :: optimize(tail)
+ //| [] => []
+ //}
+ //def exprs = if (isNeedOptimize(exprs)) optimize(exprs) else exprs;
+ def res = exprs.RevMap(e =>
+ match (e : StrPart)
+ {
+ | Lit(str) => <[ _ = _builder.Append($(str : string)); ]>
+ | NewLine => <[ _ = PrintNewLineAndIndent(); ]>
+ | Expr(expr) => expr
+ | IndentedExpr(indent, expr) => <[
+ def indent = $(indent : string);
+ _ = _builder.Append(indent);
+ AddIndent(indent);
+ $expr;
+ RemoveLastIndent(); ]>
+ });
+
+ <[ { ..$res } ]>
+ }
+ }
+
+ public variant StrPart
+ {
+ | Lit { str : string; }
+ | Expr { expr : PT.PExpr; }
+ | NewLine
+ | IndentedExpr { indent : string; expr : PT.PExpr; }
+
+ public override ToString() : string
+ {
+ match (this)
+ {
+ | Lit(str) => $"Lit: '$str'"
+ | Expr(expr) => $"Expr: $expr"
+ | NewLine => "<\n>"
+ | IndentedExpr(indent, expr) => $"IndentedExpr: '$expr' ('$indent')"
+ }
+ }
+ }
+
+ public module Helper
+ {
+ /// If we have string like this
+ /// <#
+ /// SomeText1
+ /// SomeText2
+ /// #>
+ /// this function convert it to
+ /// "SomeText1\nSomeText2"
+ public SquareString(str : string, loc : Location) : string * Location
+ {
+ match (str.LastIndexOfAny(array['\r', '\n']))
+ {
+ | -1 => (str, Location(loc.FileIndex, loc.Line, loc.Column,
+ loc.EndLine, loc.EndColumn))
+ | _ =>
+ def rows = str.Split(array["\r\n", "\n", "\r"], StringSplitOptions.None);
+ when (rows.Length <= 2)
+ Message.Error(loc, "The multiline String Template should cantain 3 and more row. "
+ "(First and last line in multiline String Template ignored.)");
+ def prefix = rows.Last();
+ def firstIndex = if (rows[0].ForAll(char.IsWhiteSpace)) 1 else 0;
+ def sb = StringBuilder(str.Length - prefix.Length
+ - if (firstIndex == 1) rows[0].Length else 0);
+ def len = rows.Length - 1;
+ mutable isIndentMismatch = false;
+ for (mutable i = firstIndex; i < len; i++)
+ {
+ def row = rows[i];
+ if (row.StartsWith(prefix, StringComparison.InvariantCulture))
+ _ = sb.AppendLine(row.Substring(prefix.Length, row.Length - prefix.Length));
+ else
+ {
+ Message.Error(Location(loc, loc.Line + i, 1, loc.Line + i, row.Length + 1),
+ "Mismatch of the string template strBuilder characters.");
+ isIndentMismatch = true;
+ _ = sb.AppendLine(row);
+ }
+ }
+
+ when (sb.Length > Environment.NewLine.Length)
+ sb.Length -= Environment.NewLine.Length;
+
+ when (isIndentMismatch)
+ Message.Hint(Location(loc, loc.EndLine, 1, loc.EndLine, loc.EndColumn),
+ "Please, make sure that all of the strBuilder characters of your "
+ "string template match the last line indentation.");
+ // TODO: Локейшон вычисляется неверно. Переделать.
+ (sb.ToString(), Location(loc.FileIndex,
+ loc.Line + firstIndex, prefix.Length + 1,
+ loc.EndLine - firstIndex, rows[len - 1].Length + 1)); // + 1 => Location coordinates 1 bound
+ }
+ }
+
+ public make_splice_distribution (str : string, env : GlobalEnv) : list [StrPart]
+ {
+ def makeEllipsisSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
+ {
+ if (isComplexExpr)
+ {
+ env.Manager.MacroColors.PushUseSiteColor ();
+ try
+ {
+ def pExpr = MainParser.ParseExpr (env, expr);
+ def makeSeqExpr(seqExpr, sepExpr, cnvFuncExpr)
+ {
+ <[ string.Join($sepExpr, NCollectionsUtils.MapToArray($seqExpr, $cnvFuncExpr)) ]>
+ }
+ match (pExpr)
+ {
+ | <[ $seqExpr; $sepExpr; $cnvFuncExpr; ]> => makeSeqExpr(seqExpr, sepExpr, cnvFuncExpr)
+ | <[ $seqExpr; $sepExpr; ]> => makeSeqExpr(seqExpr, sepExpr, <[ Convert.ToString(_) ]>)
+ | _ => makeSeqExpr(pExpr, <[ ", " ]>, <[ Convert.ToString(_) ]>)
+ }
+ }
+ finally { env.Manager.MacroColors.PopColor (); }
+ } else if (expr == "this")
+ <[ string.Join(", ", NCollectionsUtils.MapToArray(this, Convert.ToString(_))) ]>
+ else <[ string.Join(", ", NCollectionsUtils.MapToArray($(expr : usesite), Convert.ToString(_))) ]>;
+ }
+
+ def makeSplaceExpr(env : GlobalEnv, expr : string, isComplexExpr : bool) : PT.PExpr
+ {
+ if (isComplexExpr)
+ {
+ env.Manager.MacroColors.PushUseSiteColor ();
+ try
+ {
+ def pExpr = MainParser.ParseExpr (env, expr);
+ <[ Convert.ToString ($pExpr) ]>
+ }
+ finally { env.Manager.MacroColors.PopColor (); }
+ } else if (expr == "this") <[ Convert.ToString (this) ]>
+ else <[ Convert.ToString ($(expr : usesite)) ]>;
+ }
+
+ make_splice_distribution2 (str, env, makeSplaceExpr, makeEllipsisSplaceExpr)
+ }
+
+ /** for $(...) and ..$(...) expressions:
+ - first evaluate expressions
+ - store intermediate results in variables
+ - return list of evaluators and reference variables in reverse order
+ */
+ public make_splice_distribution2 (
+ str : string,
+ env : GlobalEnv,
+ makeSplaceExpr : GlobalEnv * string * bool -> PT.PExpr, // env * strExpr * isComplexExpr
+ makeEllipsisSplaceExpr : GlobalEnv * string * bool -> PT.PExpr // env * strExpr * isComplexExpr
+ )
+ : list [StrPart]
+ {
+ mutable nestLevel = 0;
+ mutable index = -1;
+ mutable ch = if (str.Length > 0) str[0] else '\0';
+ def strBuilder = StringBuilder();
+ def peekN(n) { def next = index + n; if (next < str.Length) str[next] else '\0' }
+ def peek() { peekN(1) }
+ def next() { ch = peek(); index++; ch }
+ def getStrFromBuilder() { def res = strBuilder.ToString(); strBuilder.Length = 0; res }
+ def appendToBuilder(chr) { _ = strBuilder.Append(chr) }
+ /// ~~~Parse expression based on nested brackets.
+ /// Разбирает строку производя поиск закрывающей скобки.
+ /// Вложенные скобки игнорируются. В итоге получается строка содержащая
+ /// выражение заключенное в скбоки (которое так же может содержать вложенные скобки)
+ /// и булево значение говорящее, содержится ли в строке простой идентификатор или варажение.
+ /// Returns pare of (exprStr * isIdentifier)
+ def parseExpressionStr() : string * bool
+ {
+ Assert(strBuilder.Length == 0, "strBuilder.Length == 0");
+ Assert(ch == '(', "ch == '('");
+ /// exprStr * allIsAlphNum
+ def loop(balance, allIsAlphNum) : string * bool
+ {
+ match (peek())
+ {
+ // TODO: Обработать ситуацию когда скобка не закрыта! См. файл:
+ // C:\MyProjects\Nemerle\nemerle\ncc\testsuite\negative\tyenf.n
+ | '\0'
+ | ')' when balance == 1 => _ = next(); (getStrFromBuilder(), allIsAlphNum)
+ | ')' => appendToBuilder(next()); loop(balance - 1, false)
+ | '(' => appendToBuilder(next()); loop(balance + 1, false)
+ | curCh =>
+ appendToBuilder(next());
+ loop(balance, allIsAlphNum && (char.IsLetterOrDigit(curCh) || curCh == '_'))
+ }
+ }
+
+ def (expr, allIsAlphNum) = loop(1, true);
+ (expr, allIsAlphNum && expr.Length != 0 && expr != "_" && char.IsLetter(expr[0]))
+ }
+ def parseIdentifier()
+ {
+ Assert(strBuilder.Length == 0, "strBuilder.Length == 0");
+
+ def loop()
+ {
+ def curCh = peek();
+ match (curCh)
+ {
+ | '_'
+ | _ when char.IsLetterOrDigit(curCh) => appendToBuilder(next()); loop()
+ | _ => getStrFromBuilder()
+ }
+ }
+
+ if (ch == '_' || char.IsLetter(ch))
+ {
+ appendToBuilder(ch);
+ loop()
+ }
+ else ""
+ }
+
+ def loop (res : list[StrPart]) : list[StrPart]
+ {
+ nestLevel++; Diagnostics.Trace.Assert(nestLevel < 20000, "Prevent stack owerflow"); // Prevent stack owerflow
+
+ // Завершает акомуляцию сиволов литерала и создает соотвествующую
+ // лексему добавляя ее к началу списка лексем
+ def endLiteral()
+ {
+ if (strBuilder.Length == 0)
+ res
+ else
+ StrPart.Lit(getStrFromBuilder()) :: res
+ }
+ def isNextDollar(n)
+ {
+ def ch1 = peekN(n);
+ if (char.IsWhiteSpace(ch1)) isNextDollar(n + 1)
+ else ch1 == '$'
+ }
+ def isElipse() { peek() == '.' && isNextDollar(2) }
+ def processNewLine() { loop (StrPart.NewLine() :: endLiteral()) }
+
+ match (next())
+ {
+ | '\0' => endLiteral()
+ | '$' => parceSpliceEx(endLiteral(), true)
+ | '.' when isElipse() => index = str.IndexOf('$', index);
+ parceSpliceEx(endLiteral(), false); // '..$'
+ | '\r' when peek() == '\n' => _ = next(); processNewLine()
+ | '\n' | '\r' => processNewLine()
+ | x => appendToBuilder(x); loop(res)
+ }
+ }
+ and parceSpliceEx(res, isSimple)
+ {
+ when (next() == '\0')
+ {
+ //Diagnostics.Trace.Assert(false);
+ Message.Error ("lone `$' at the end of the format string");
+ Nemerle.Imperative.Return ([StrPart.Lit("$")]);
+ }
+
+ def rtyIndent(res : list[StrPart], expr)
+ {
+ match (res)
+ {
+ | Lit(str) :: NewLine :: tail when str.ForAll(char.IsWhiteSpace) =>
+ StrPart.IndentedExpr(str, expr) :: StrPart.NewLine() :: tail
+ | _ => StrPart.Expr(expr) :: res
+ }
+ }
+
+ def str = str; _ = str;
+
+ if (ch == '(')
+ {
+ //def index1 = index; _ = index1;
+ def (exprStr, isIdentifier) = parseExpressionStr();
+
+ if (ch == '\0') // скобка не закрыта
+ {
+ def exprStr = "(" + exprStr;
+ Message.Error($"no closing bracket found in `$(exprStr)' "
+ "(the closing bracket in format string is probably missing)");
+ }
+ else when (exprStr.Trim().Length == 0)
+ Message.Error("expression without content");
+
+ def expr = if (isSimple) makeSplaceExpr(env, exprStr, !isIdentifier)
+ else makeEllipsisSplaceExpr(env, exprStr, !isIdentifier);
+ loop (rtyIndent(res, expr))
+ }
+ else if (ch == '$')
+ loop (StrPart.Lit("$") :: res)
+ else
+ {
+ //def index1 = index; _ = index1;
+ def variableName = parseIdentifier();
+
+ if (variableName == "")
+ {
+ appendToBuilder(ch);
+ Message.Warning ("expected variable name or expression enclosed with (..) after $ in splice string");
+ loop (StrPart.Lit("$") :: res)
+ }
+ else
+ {
+ def expr = if (isSimple) makeSplaceExpr(env, variableName, false)
+ else makeEllipsisSplaceExpr(env, variableName, false);
+ //def index1 = index; _ = index1;
+ loop (rtyIndent(res, expr))
+ }
+ }
+ }
+
+ loop ([])
+ }
+ }
+}
Modified: nemerle/trunk/ncc/Makefile
==============================================================================
--- nemerle/trunk/ncc/Makefile (original)
+++ nemerle/trunk/ncc/Makefile Thu Oct 18 13:18:17 2007
@@ -151,6 +151,7 @@
../macros/xml.n \
../macros/text.n \
../macros/io.n \
+ ../macros/string.n \
../macros/core.n \
../macros/compiler.n \
../macros/assertions.n \
More information about the svn
mailing list