プログラミング関連の話題・質問用のスレッド。 (全53件の投稿) Atom 1.0

※ブラウザによっては新しい書き込みが表示されない場合があるようなので、返信が表示されていない場合はF5または更新ボタンでページを更新してください。

: ID:cipeco0y

プログラミング以外の話題はロビーのスレッドでお願いします。
ツールのソースについての質問などはこちらでも構いません。

: ID:cipeco0y

http://smdn.invisiblefulmoon.net/temp/20090620/regex-subtract.txtに書いたもの。

Character Class Subtraction is not implemented
http://msdn.microsoft.com/en-us/library/20bw873z(VS.80).aspx

repro code
using System;
using System.Text.RegularExpressions;

class RegexSubtract {
  public static void Main()
  {
    var regex1 = new Regex("[a-z]+");
    var regex2 = new Regex("[a-z-[d-w]]+");
    var regex3 = new Regex("[a-z-[d-w-[m-o]]]+");
    var text = "abcdefghijklmnopqrstuvwxyz";

    Console.WriteLine(Environment.OSVersion);
    Console.WriteLine(Environment.Version);

    Console.WriteLine("regex1");
    foreach (Match m in regex1.Matches(text)) {
      Console.WriteLine(m.Value);
    }

    Console.WriteLine("regex2");
    foreach (Match m in regex2.Matches(text)) {
      Console.WriteLine(m.Value);
    }

    Console.WriteLine("regex3");
    foreach (Match m in regex3.Matches(text)) {
      Console.WriteLine(m.Value);
    }
  }
}
.NET Framework 2.0
Microsoft Windows NT 5.1.2600 Service Pack 3
2.0.50727.3082
regex1
abcdefghijklmnopqrstuvwxyz
regex2
abc
xyz
regex3
abc
mno
xyz
Mono 2.5
Unix 2.6.28.11
2.0.50727.1433
regex1
abcdefghijklmnopqrstuvwxyz
regex2
regex3

http://irc.gimite.net/channel/mono-jp/archive/20090620
https://bugzilla.novell.com/show_bug.cgi?id=476410

: ID:cipeco0y

>>2つづき

regex-subtract.patch
Index: mcs/class/System/System.Text.RegularExpressions/syntax.cs
===================================================================
--- mcs/class/System/System.Text.RegularExpressions/syntax.cs	(revision 133993)
+++ mcs/class/System/System.Text.RegularExpressions/syntax.cs	(working copy)
@@ -871,6 +871,12 @@
 			intervals.Add (new_interval);
 		}
 
+		public void Exclude(CharacterClass excluded)
+		{
+			throw new NotImplementedException ("Character class subtraction is not implemented");
+			// TODO: exclude categories and characters
+		}
+
 		public override void Compile (ICompiler cmp, bool reverse) {
 			// create the meta-collection
 			IntervalCollection meta =
Index: mcs/class/System/System.Text.RegularExpressions/parser.cs
===================================================================
--- mcs/class/System/System.Text.RegularExpressions/parser.cs	(revision 133993)
+++ mcs/class/System/System.Text.RegularExpressions/parser.cs	(working copy)
@@ -651,7 +651,9 @@
 			int c = -1;
 			int last = -1;
 			bool range = false;
+			bool exclude = false;
 			bool closed = false;
+			CharacterClass clsExcluded = null;
 			while (ptr < pattern.Length) {
 				c = pattern[ptr ++];
 
@@ -660,8 +662,17 @@
 					break;
 				}
 
-				if (c == '-' && last >= 0 && !range) {
-					range = true;
+				if (c == '-') {
+					if (last >= 0 && !range) {
+						range = true;
+					}
+					else if (!exclude) {
+						if (pattern[ptr ++] != '[')
+							throw NewParseException ("Malformed character class subtraction.");
+						range = false;
+						exclude = true;
+						clsExcluded = (CharacterClass)ParseCharacterClass (options);
+					}
 					continue;
 				}
 
@@ -723,7 +734,9 @@
 			if (!closed)
 				throw NewParseException ("Unterminated [] set.");
 
-			if (range)
+			if (exclude)
+				cls.Exclude(clsExcluded);
+			else if (range)
 				cls.AddCharacter ('-');
 
 			return cls;
: ID:cipeco0y

http://smdn.invisiblefulmoon.net/temp/20090813/pp-elif.txtに書いたもの

gmcs handle_preprocessing_directive bug

repro code (pp-elif.cs)
using System;

static class PpElifTest {
  public static void Main()
  {
#if DEBUG
    Console.WriteLine("if");
#elif
    Console.WriteLine("elif");
#else
    Console.WriteLine("else");
#endif

#if DEBUG
    Console.WriteLine("if");
#elif *invalid-symbol*
    Console.WriteLine("elif");
#else
    Console.WriteLine("else");
#endif
  }
}
csc
D:\test\pp-elif>csc pp-elif.cs /define:DEBUG
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

pp-elif.cs(8,6): error CS1517: 無効なプリプロセッサの式です。
pp-elif.cs(16,7): error CS1517: 無効なプリプロセッサの式です。

D:\test\pp-elif>csc pp-elif.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

pp-elif.cs(8,6): error CS1517: 無効なプリプロセッサの式です。
pp-elif.cs(16,7): error CS1517: 無効なプリプロセッサの式です。
gmcs
$ gmcs pp-elif.cs -define:DEBUG && mono pp-elif.exe 
if
if

$ gmcs pp-elif.cs && mono pp-elif.exe 
pp-elif.cs(9,0): error CS1517: Invalid preprocessor directive
pp-elif.cs(17,0): error CS1517: Invalid preprocessor directive
pp-elif.cs(17,0): error CS1025: Single-line comment or end-of-line expected
Compilation failed: 3 error(s), 0 warnings

http://irc.gimite.net/channel/mono-jp/archive/20090813

: ID:cipeco0y

CryptoStream.Write throws IndexOutOfRangeException

CanTransformMultipleBlocksがtrueのICryptoTransformを使ってCryptoStream.WriteするとIndexOutOfRangeExceptionがスローされる。

repro code
using System;
using System.IO;
using System.Security.Cryptography;

class ExpandTransform : ICryptoTransform {
  public bool CanReuseTransform {
    get { return true; }
  }

  public bool CanTransformMultipleBlocks {
    get; private set;
  }

  public int InputBlockSize {
    get { return 1; }
  }

  public int OutputBlockSize {
    get; private set;
  }

  public ExpandTransform(bool canTranformMultipleBlocks, int outputBlockSize)
  {
    this.CanTransformMultipleBlocks = canTranformMultipleBlocks;
    this.OutputBlockSize = outputBlockSize;
  }

  public void Dispose()
  {
  }

  public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
  {
    var ret = 0;

    for (var i = 0; i < inputCount; i++, inputOffset++) {
      for (var j = 0; j < OutputBlockSize; j++, outputOffset++, ret++) {
        outputBuffer[outputOffset] = inputBuffer[inputOffset];
      }
    }

    return ret;
  }

  public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
  {
    var outputBuffer = new byte[inputCount * OutputBlockSize];

    TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, 0);

    return outputBuffer;
  }
}

class CryptoStreamTest
{
  public static void Main(string[] args)
  {
    Console.WriteLine(Environment.OSVersion);
    Console.WriteLine(Environment.Version);

    foreach (var transformMultiple in new[] {false, true}) {
      foreach (var outputBlockSize in new[] {1, 2, 3, 4}) {
        Console.WriteLine("TransformMultiple: {0}, OutputBlockSize: {1}", transformMultiple, outputBlockSize);

        var expantedStream = new MemoryStream();
        var inputData = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};

        try {
          using (var stream = new CryptoStream(expantedStream, new ExpandTransform(transformMultiple, outputBlockSize), CryptoStreamMode.Write)) {
            stream.Write(inputData, 0, inputData.Length);
          }

          expantedStream.Close();

          Console.WriteLine(BitConverter.ToString(expantedStream.ToArray()));
        }
        catch (Exception ex) {
          Console.WriteLine(ex);
        }

        Console.WriteLine();
      }
    }
  }
}
.NET Framework
Microsoft Windows NT 5.1.2600 Service Pack 3
2.0.50727.3082
TransformMultiple: False, OutputBlockSize: 1
00-01-02-03-04-05-06-07

TransformMultiple: False, OutputBlockSize: 2
00-00-01-01-02-02-03-03-04-04-05-05-06-06-07-07

TransformMultiple: False, OutputBlockSize: 3
00-00-00-01-01-01-02-02-02-03-03-03-04-04-04-05-05-05-06-06-06-07-07-07

TransformMultiple: False, OutputBlockSize: 4
00-00-00-00-01-01-01-01-02-02-02-02-03-03-03-03-04-04-04-04-05-05-05-05-06-06-06-06-07-07-07-07

TransformMultiple: True, OutputBlockSize: 1
00-01-02-03-04-05-06-07

TransformMultiple: True, OutputBlockSize: 2
00-00-01-01-02-02-03-03-04-04-05-05-06-06-07-07

TransformMultiple: True, OutputBlockSize: 3
00-00-00-01-01-01-02-02-02-03-03-03-04-04-04-05-05-05-06-06-06-07-07-07

TransformMultiple: True, OutputBlockSize: 4
00-00-00-00-01-01-01-01-02-02-02-02-03-03-03-03-04-04-04-04-05-05-05-05-06-06-06-06-07-07-07-07
Mono
Unix 2.6.28.15
2.0.50727.1433
TransformMultiple: False, OutputBlockSize: 1
00-01-02-03-04-05-06-07

TransformMultiple: False, OutputBlockSize: 2
00-00-01-01-02-02-03-03-04-04-05-05-06-06-07-07

TransformMultiple: False, OutputBlockSize: 3
00-00-00-01-01-01-02-02-02-03-03-03-04-04-04-05-05-05-06-06-06-07-07-07

TransformMultiple: False, OutputBlockSize: 4
00-00-00-00-01-01-01-01-02-02-02-02-03-03-03-03-04-04-04-04-05-05-05-05-06-06-06-06-07-07-07-07

TransformMultiple: True, OutputBlockSize: 1
00-01-02-03-04-05-06-07

TransformMultiple: True, OutputBlockSize: 2
System.IndexOutOfRangeException: Array index is out of range.
  at ExpandTransform.TransformBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, System.Byte[] outputBuffer, Int32 outputOffset) [0x00010] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:38 
  at System.Security.Cryptography.CryptoStream.Write (System.Byte[] buffer, Int32 offset, Int32 count) [0x001be] in /srv/shared/build/mono-classes/corlib/System.Security.Cryptography/CryptoStream.cs:297 
  at CryptoStreamTest.Main (System.String[] args) [0x0008c] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:71 

TransformMultiple: True, OutputBlockSize: 3
System.IndexOutOfRangeException: Array index is out of range.
  at ExpandTransform.TransformBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, System.Byte[] outputBuffer, Int32 outputOffset) [0x00010] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:38 
  at System.Security.Cryptography.CryptoStream.Write (System.Byte[] buffer, Int32 offset, Int32 count) [0x001be] in /srv/shared/build/mono-classes/corlib/System.Security.Cryptography/CryptoStream.cs:297 
  at CryptoStreamTest.Main (System.String[] args) [0x0008c] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:71 

TransformMultiple: True, OutputBlockSize: 4
System.IndexOutOfRangeException: Array index is out of range.
  at ExpandTransform.TransformBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, System.Byte[] outputBuffer, Int32 outputOffset) [0x00010] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:38 
  at System.Security.Cryptography.CryptoStream.Write (System.Byte[] buffer, Int32 offset, Int32 count) [0x001be] in /srv/shared/build/mono-classes/corlib/System.Security.Cryptography/CryptoStream.cs:297 
  at CryptoStreamTest.Main (System.String[] args) [0x0008c] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:71 

ICryptoTransform.TransformBlockに渡すバッファのサイズが少なすぎるようなので、それを直す。

patch
Index: System.Security.Cryptography/CryptoStream.cs
===================================================================
--- System.Security.Cryptography/CryptoStream.cs	(revision 139809)
+++ System.Security.Cryptography/CryptoStream.cs	(working copy)
@@ -72,11 +72,14 @@
 			_mode = mode;
 			_disposed = false;
 			if (transform != null) {
-				_workingBlock = new byte [transform.InputBlockSize];
-				if (mode == CryptoStreamMode.Read)
+				if (mode == CryptoStreamMode.Read) {
 					_currentBlock = new byte [transform.InputBlockSize];
-				else if (mode == CryptoStreamMode.Write)
+					_workingBlock = new byte [transform.InputBlockSize];
+				}
+				else if (mode == CryptoStreamMode.Write) {
 					_currentBlock = new byte [transform.OutputBlockSize];
+					_workingBlock = new byte [transform.OutputBlockSize];
+				}
 			}
 		}
 
@@ -285,14 +288,14 @@
 
 				if (_transform.CanTransformMultipleBlocks) {
 					// get the biggest multiple of InputBlockSize in count (without mul or div)
-					int size = (count & ~(_transform.OutputBlockSize - 1));
-					int rem = (count & (_transform.OutputBlockSize - 1));
+					int size = (count & ~(_transform.InputBlockSize - 1));
+					int rem = (count & (_transform.InputBlockSize - 1));
 					// avoid reallocating memory at each call (reuse same buffer whenever possible)
-					if (_workingBlock.Length < size) {
+					int sizeWorkingBlock = (1 + size / _transform.InputBlockSize) * _transform.OutputBlockSize;
+					if (_workingBlock.Length < sizeWorkingBlock) {
 						Array.Clear (_workingBlock, 0, _workingBlock.Length);
-						_workingBlock = new byte [size];
+						_workingBlock = new byte [sizeWorkingBlock];
 					}
-
 					if (size > 0) {
 						int len = _transform.TransformBlock (buffer, offset, size, _workingBlock, 0);
 						_stream.Write (_workingBlock, 0, len);
: ID:cipeco0y

CryptoStream.Read throws IndexOutOfRangeException

OutputBlockSizeが1、InputBlockSizeが3以上のICryptoTransformを使ってCryptoStream.ReadするとIndexOutOfRangeExceptionがスローされる。

repro code
using System;
using System.IO;
using System.Security.Cryptography;

class CompressTransform : ICryptoTransform {
  public bool CanReuseTransform {
    get { return true; }
  }

  public bool CanTransformMultipleBlocks {
    get { return true; }
  }

  public int InputBlockSize {
    get; private set;
  }

  public int OutputBlockSize {
    get { return 1; }
  }

  public CompressTransform(int inputBlockSize)
  {
    this.InputBlockSize = inputBlockSize;
  }

  public void Dispose()
  {
  }

  private int bufferedCount = 0;

  public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
  {
    var ret = 0;

    for (var i = 0; i < inputCount; i++, inputOffset++) {
      if (++bufferedCount == InputBlockSize) {
        outputBuffer[outputOffset++] = inputBuffer[inputOffset];
        ret++;
        bufferedCount = 0;
      }
    }

    return ret;
  }

  public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
  {
    var outputBuffer = new byte[inputCount * OutputBlockSize];
    var ret = TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, 0);

    Array.Resize(ref outputBuffer, ret);

    return outputBuffer;
  }
}

class CryptoStreamTest
{
  public static void Main(string[] args)
  {
    Console.WriteLine(Environment.OSVersion);
    Console.WriteLine(Environment.Version);

    foreach (var inputBlockSize in new[] {1, 2, 3, 4}) {
      Console.WriteLine("InputBlockSize: {0}", inputBlockSize);

      var inputData = new byte[] {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
      };

      try {
        using (var stream = new CryptoStream(new MemoryStream(inputData), new CompressTransform(inputBlockSize), CryptoStreamMode.Read)) {
          var buffer = new byte[inputData.Length];

          var ret = stream.Read(buffer, 0, buffer.Length);

          Console.WriteLine(BitConverter.ToString(buffer, 0, ret));
        }
      }
      catch (Exception ex) {
        Console.WriteLine(ex);
      }

      Console.WriteLine();
    }
  }
}
.NET Framework
Microsoft Windows NT 5.1.2600 Service Pack 3
2.0.50727.3082
InputBlockSize: 1
00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F-10-11-12-13-14-15-16-17-18-19-1A-1B-1C-1D-1E-1F

InputBlockSize: 2
01-03-05-07-09-0B-0D-0F-11-13-15-17-19-1B-1D-1F

InputBlockSize: 3
02-05-08-0B-0E-11-14-17-1A-1D

InputBlockSize: 4
03-07-0B-0F-13-17-1B-1F
Mono
Unix 2.6.28.15
2.0.50727.1433
InputBlockSize: 1
00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F-10-11-12-13-14-15-16-17-18-19-1A-1B-1C-1D-1E-1F

InputBlockSize: 2
01-03-05-07-09-0B-0D-0F-11-13-15-17-19-1B-1D-1F

InputBlockSize: 3
System.IndexOutOfRangeException: Array index is out of range.
  at CompressTransform.TransformBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, System.Byte[] outputBuffer, Int32 outputOffset) [0x00025] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:39 
  at System.Security.Cryptography.CryptoStream.Read (System.Byte[] buffer, Int32 offset, Int32 count) [0x0017a] in /srv/shared/build/mono-classes/corlib/System.Security.Cryptography/CryptoStream.cs:183 
  at CryptoStreamTest.Main (System.String[] args) [0x00072] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:80 

InputBlockSize: 4
System.IndexOutOfRangeException: Array index is out of range.
  at CompressTransform.TransformBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, System.Byte[] outputBuffer, Int32 outputOffset) [0x00025] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:39 
  at System.Security.Cryptography.CryptoStream.Read (System.Byte[] buffer, Int32 offset, Int32 count) [0x0017a] in /srv/shared/build/mono-classes/corlib/System.Security.Cryptography/CryptoStream.cs:183 
  at CryptoStreamTest.Main (System.String[] args) [0x00072] in /srv/shared/work/CryptoStreamTest/CryptoStreamTest/CryptoStreamTest.cs:80 

内部バッファの状態をクリアするタイミングが誤っているようなので、それを直す。

patch
Index: System.Security.Cryptography/CryptoStream.cs
===================================================================
--- System.Security.Cryptography/CryptoStream.cs	(revision 139809)
+++ System.Security.Cryptography/CryptoStream.cs	(working copy)
@@ -212,7 +215,7 @@
 					_transformedCount += transformed;
 				}
 				// compaction
-				if (_transformedPos > _transform.InputBlockSize) {
+				if (_transformedPos > _transform.OutputBlockSize) {
 					Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length);
 					_transformedCount -= _transformedPos;
 					_transformedPos = 0;
: ID:cipeco0y

Mono: DateTimeOffset.ParseExact

書式文字列にzzzを指定し、かつ入力文字列のオフセット部分にDateTimeFormatInfo.TimeSeparatorが含まれない場合、FormatExceptionがスローされる。 ("+09:00"はOK, "+0900"はNG)

repro code
using System;

namespace DateTimeOffsetTest {
  class MainClass
  {
    public static void Main(string[] args)
    {
      Console.WriteLine(Environment.OSVersion);
      Console.WriteLine(Environment.Version);
      Console.WriteLine();

      foreach (var dtm in new[] {
        "13/Oct/2009:22:35:35 +09:00",
        "13/Oct/2009:22:35:35 +0900",
        "13/Oct/2009:22:35:35 +09:30",
        "13/Oct/2009:22:35:35 +0930",
      }) {
        try {
          Console.Write("{0} => ", dtm);
          Console.WriteLine(DateTimeOffset.ParseExact(dtm, "d/MMM/yyyy:HH:mm:ss zzz", System.Globalization.CultureInfo.InvariantCulture));
        }
        catch (FormatException ex) {
          Console.WriteLine(ex);
        }
      }
    }
  }
}
.NET Framework 2.0
Microsoft Windows NT 5.1.2600 Service Pack 3
2.0.50727.3082

13/Oct/2009:22:35:35 +09:00 => 2009/10/13 22:35:35 +09:00
13/Oct/2009:22:35:35 +0900 => 2009/10/13 22:35:35 +09:00
13/Oct/2009:22:35:35 +09:30 => 2009/10/13 22:35:35 +09:30
13/Oct/2009:22:35:35 +0930 => 2009/10/13 22:35:35 +09:30
Mono 2.5
Unix 2.6.28.15
2.0.50727.1433

13/Oct/2009:22:35:35 +09:00 => 2009/10/13 22:35:35 +09:00
13/Oct/2009:22:35:35 +0900 => System.FormatException: Invalid format string
  at System.DateTimeOffset.ParseExact (System.String input, System.String[] formats, IFormatProvider formatProvider, DateTimeStyles styles) [0x00000] 
  at System.DateTimeOffset.ParseExact (System.String input, System.String format, IFormatProvider formatProvider, DateTimeStyles styles) [0x00000] 
  at System.DateTimeOffset.ParseExact (System.String input, System.String format, IFormatProvider formatProvider) [0x00000] 
  at DateTimeOffsetTest.MainClass.Main (System.String[] args) [0x00000] 
13/Oct/2009:22:35:35 +09:30 => 2009/10/13 22:35:35 +09:30
13/Oct/2009:22:35:35 +0930 => System.FormatException: Invalid format string
  at System.DateTimeOffset.ParseExact (System.String input, System.String[] formats, IFormatProvider formatProvider, DateTimeStyles styles) [0x00000] 
  at System.DateTimeOffset.ParseExact (System.String input, System.String format, IFormatProvider formatProvider, DateTimeStyles styles) [0x00000] 
  at System.DateTimeOffset.ParseExact (System.String input, System.String format, IFormatProvider formatProvider) [0x00000] 
  at DateTimeOffsetTest.MainClass.Main (System.String[] args) [0x00000] 
パッチ

DateTimeFormatInfo.TimeSeparatorが含まれていない場合でも不正としないように直す。

パッチ
Index: System/DateTimeOffset.cs
===================================================================
--- System/DateTimeOffset.cs	(revision 144011)
+++ System/DateTimeOffset.cs	(working copy)
@@ -525,7 +525,7 @@
 						ii += ParseEnum (input, ii, new string [] {dfi.TimeSeparator}, false, out temp_int);
 						ii += ParseNumber (input, ii, 2, true, false, out off_m);
 					}
-					if (off_h == -1 || off_m == -1 || sign == -1 || temp_int == -1)
+					if (off_h == -1 || off_m == -1 || sign == -1)
 						return false;
 
 					if (sign == 0)
テスト
Index: Test/System/DateTimeOffsetTest.cs
===================================================================
--- Test/System/DateTimeOffsetTest.cs	(revision 144011)
+++ Test/System/DateTimeOffsetTest.cs	(working copy)
@@ -273,6 +273,27 @@
 		}
 
 		[Test]
+		public void ParseExactOffsetFormat ()
+		{
+			CultureInfo fp = CultureInfo.InvariantCulture;
+			string[] dates = {
+				"13/Oct/2009:22:35:35 +09:00",
+				"13/Oct/2009:22:35:35 +0900",
+				"13/Oct/2009:22:35:35 +09:30",
+				"13/Oct/2009:22:35:35 +0930",
+			};
+			TimeSpan[] offsets = {
+				new TimeSpan (9,0,0),
+				new TimeSpan (9,0,0),
+				new TimeSpan (9,30,0),
+				new TimeSpan (9,30,0),
+			};
+
+			for (int i = 0; i < dates.Length; i++)
+				Assert.AreEqual(offsets[i], DateTimeOffset.ParseExact (dates[i], "d/MMM/yyyy:HH:mm:ss zzz", fp).Offset, dates[i]);
+		}
+
+		[Test]
 		[ExpectedException (typeof (FormatException))]
 		public void ParseExactYearException ()
 		{
メモ
  • http://msdn.microsoft.com/ja-jp/library/8kb3ddd4.aspxを見ても"zzz"でパースするときDateTimeFormatInfo.TimeSeparatorをどう扱うか書いてない。
  • DateTime.ParseExactでもFormatExceptionになりそうな気がするけどOffsetを扱えないし使わないのでスルー。
  • Monoだと"z"または"zz"で"+09:00"をパースしてもFormatExceptionにならない。 これはこれでバグな気がする。
    Console.WriteLine(DateTimeOffset.ParseExact("13/Oct/2009:22:35:35 +09:00", "d/MMM/yyyy:HH:mm:ss z", CultureInfo.InvariantCulture));
    Console.WriteLine(DateTimeOffset.ParseExact("13/Oct/2009:22:35:35 +09:00", "d/MMM/yyyy:HH:mm:ss zz", CultureInfo.InvariantCulture));
    
: ID:cipeco0y

Mono: XmlConvert.ToDateTimeOffset

フォーマットを指定しないでXmlConvert.ToDateTimeOffsetを呼んだ場合、タイムゾーンの部分が読み落とされる。

using System;
using System.Xml;

class XmlConvertToDateTimeOffsetTest {
  public static void Main()
  {
    var dtm = "2009-11-05T20:16:22+09:00";

    Console.WriteLine("{0} => {1}", dtm, XmlConvert.ToDateTimeOffset(dtm));
  }
}
expected result
2009-11-05T20:16:22+09:00 => 2009/11/05 20:16:22 +09:00
actual result
2009-11-05T20:16:22+09:00 => 2009/11/05 20:16:22 +00:00

XmlConvert.ToDateTimeOffsetからDateTimeOffset.ParseExactを呼ぶときに渡されるformatsの順序が

  1. "yyyy-MM-ddTHH:mm:ss" など、Zもzzzも付かない書式
  2. "yyyy-MM-ddTHH:mm:sszzz" など、zzzが付く書式
  3. "yyyy-MM-ddTHH:mm:ssZ" など、Zが付く書式

になっているので、1でパース出来てしまうとタイムゾーンの部分が無視される。 なので、これを

  1. "yyyy-MM-ddTHH:mm:sszzz"
  2. "yyyy-MM-ddTHH:mm:ssZ"
  3. "yyyy-MM-ddTHH:mm:ss"

の順にすれば、タイムゾーンの部分を読み落とさなくなる。

修正案
Index: System.Xml/XmlConvert.cs
===================================================================
--- System.Xml/XmlConvert.cs	(revision 145470)
+++ System.Xml/XmlConvert.cs	(working copy)
@@ -48,14 +48,6 @@
 		
 		static readonly string [] datetimeFormats = {
 		  // dateTime
-		  "yyyy-MM-ddTHH:mm:ss",
-		  "yyyy-MM-ddTHH:mm:ss.f",
-		  "yyyy-MM-ddTHH:mm:ss.ff",
-		  "yyyy-MM-ddTHH:mm:ss.fff",
-		  "yyyy-MM-ddTHH:mm:ss.ffff",
-		  "yyyy-MM-ddTHH:mm:ss.fffff",
-		  "yyyy-MM-ddTHH:mm:ss.ffffff",
-		  "yyyy-MM-ddTHH:mm:ss.fffffff",
 		  "yyyy-MM-ddTHH:mm:sszzz",
 		  "yyyy-MM-ddTHH:mm:ss.fzzz",
 		  "yyyy-MM-ddTHH:mm:ss.ffzzz",
@@ -72,6 +64,14 @@
 		  "yyyy-MM-ddTHH:mm:ss.fffffZ",
 		  "yyyy-MM-ddTHH:mm:ss.ffffffZ",
 		  "yyyy-MM-ddTHH:mm:ss.fffffffZ",
+		  "yyyy-MM-ddTHH:mm:ss",
+		  "yyyy-MM-ddTHH:mm:ss.f",
+		  "yyyy-MM-ddTHH:mm:ss.ff",
+		  "yyyy-MM-ddTHH:mm:ss.fff",
+		  "yyyy-MM-ddTHH:mm:ss.ffff",
+		  "yyyy-MM-ddTHH:mm:ss.fffff",
+		  "yyyy-MM-ddTHH:mm:ss.ffffff",
+		  "yyyy-MM-ddTHH:mm:ss.fffffff",
 		  // time
 		  "HH:mm:ss",
 		  "HH:mm:ss.f",
: ID:cipeco0y

Mono: XmlConvert.ToDateTimeOffset

フォーマットを指定しないでXmlConvert.ToDateTimeOffsetを呼んだ場合、タイムゾーンの部分が読み落とされる。

using System;
using System.Xml;

class XmlConvertToDateTimeOffsetTest {
  public static void Main()
  {
    var dtm = "2009-11-05T20:16:22+09:00";

    Console.WriteLine("{0} => {1}", dtm, XmlConvert.ToDateTimeOffset(dtm));
  }
}
expected result
2009-11-05T20:16:22+09:00 => 2009/11/05 20:16:22 +09:00
actual result
2009-11-05T20:16:22+09:00 => 2009/11/05 20:16:22 +00:00

XmlConvert.ToDateTimeOffsetからDateTimeOffset.ParseExactを呼ぶときに渡されるformatsの順序が

  1. "yyyy-MM-ddTHH:mm:ss" など、Zもzzzも付かない書式
  2. "yyyy-MM-ddTHH:mm:sszzz" など、zzzが付く書式
  3. "yyyy-MM-ddTHH:mm:ssZ" など、Zが付く書式

になっているので、1でパース出来てしまうとタイムゾーンの部分が無視される。 なので、これを

  1. "yyyy-MM-ddTHH:mm:sszzz"
  2. "yyyy-MM-ddTHH:mm:ssZ"
  3. "yyyy-MM-ddTHH:mm:ss"

の順にすれば、タイムゾーンの部分を読み落とさなくなる。

修正案
Index: System.Xml/XmlConvert.cs
===================================================================
--- System.Xml/XmlConvert.cs	(revision 145470)
+++ System.Xml/XmlConvert.cs	(working copy)
@@ -48,14 +48,6 @@
 		
 		static readonly string [] datetimeFormats = {
 		  // dateTime
-		  "yyyy-MM-ddTHH:mm:ss",
-		  "yyyy-MM-ddTHH:mm:ss.f",
-		  "yyyy-MM-ddTHH:mm:ss.ff",
-		  "yyyy-MM-ddTHH:mm:ss.fff",
-		  "yyyy-MM-ddTHH:mm:ss.ffff",
-		  "yyyy-MM-ddTHH:mm:ss.fffff",
-		  "yyyy-MM-ddTHH:mm:ss.ffffff",
-		  "yyyy-MM-ddTHH:mm:ss.fffffff",
 		  "yyyy-MM-ddTHH:mm:sszzz",
 		  "yyyy-MM-ddTHH:mm:ss.fzzz",
 		  "yyyy-MM-ddTHH:mm:ss.ffzzz",
@@ -72,6 +64,14 @@
 		  "yyyy-MM-ddTHH:mm:ss.fffffZ",
 		  "yyyy-MM-ddTHH:mm:ss.ffffffZ",
 		  "yyyy-MM-ddTHH:mm:ss.fffffffZ",
+		  "yyyy-MM-ddTHH:mm:ss",
+		  "yyyy-MM-ddTHH:mm:ss.f",
+		  "yyyy-MM-ddTHH:mm:ss.ff",
+		  "yyyy-MM-ddTHH:mm:ss.fff",
+		  "yyyy-MM-ddTHH:mm:ss.ffff",
+		  "yyyy-MM-ddTHH:mm:ss.fffff",
+		  "yyyy-MM-ddTHH:mm:ss.ffffff",
+		  "yyyy-MM-ddTHH:mm:ss.fffffff",
 		  // time
 		  "HH:mm:ss",
 		  "HH:mm:ss.f",
: ID:Uc3GKKlM

Mono: WebClient.UploadStringAsync

WebClient.UploadStringAsyncが常にNotSupportedExceptionをスローする。

repro code
using System;
using System.Net;

class MainClass {
  public static void Main(string[] args)
  {
    using (var client = new WebClient()) {
      client.UploadStringCompleted += delegate(object sender, UploadStringCompletedEventArgs e) {
        if (e.Error != null)
          Console.WriteLine(e.Error);
      };

      client.UploadStringAsync(new Uri("http://localhost/"), "hoge");

      System.Threading.Thread.Sleep(500);
    }
  }
}
System.Net.WebException: An error occurred performing a WebClient request. ---> System.NotSupportedException: WebClient does not support conccurent I/O operations.
  at System.Net.WebClient.CheckBusy () [0x00016] in /srv/files/build/mono/trunk/mcs/class/System/System.Net/WebClient.cs:226 
  at System.Net.WebClient.SetBusy () [0x00008] in /srv/files/build/mono/trunk/mcs/class/System/System.Net/WebClient.cs:231 
  at System.Net.WebClient.UploadData (System.Uri address, System.String method, System.Byte[] data) [0x00028] in /srv/files/build/mono/trunk/mcs/class/System/System.Net/WebClient.cs:520 
  --- End of inner exception stack trace ---
  at System.Net.WebClient.UploadData (System.Uri address, System.String method, System.Byte[] data) [0x00053] in /srv/files/build/mono/trunk/mcs/class/System/System.Net/WebClient.cs:528 
  at System.Net.WebClient.UploadString (System.Uri address, System.String method, System.String data) [0x00028] in /srv/files/build/mono/trunk/mcs/class/System/System.Net/WebClient.cs:840 
  at System.Net.WebClient.<UploadStringAsync>m__13 (System.Object state) [0x00007] in /srv/files/build/mono/trunk/mcs/class/System/System.Net/WebClient.cs:1412 

WebClient.UploadStringAsync()でSetBusy()が呼ばれたあと、UploadStringAsync()から呼ばれるUploadData()でもう一度SetBusy()が呼ばれている。
SetBusy()ではなく、CheckBusy()を呼べばOK。

パッチ
Index: System/System.Net/WebClient.cs
===================================================================
--- System/System.Net/WebClient.cs	(revision 150935)
+++ System/System.Net/WebClient.cs	(working copy)
@@ -1402,7 +1402,7 @@
 				throw new ArgumentNullException ("data");
 			
 			lock (this) {
-				SetBusy ();
+				CheckBusy ();
 				async = true;
 				
 				async_thread = new Thread (delegate (object state) {
: ID:Uc3GKKlM

gmcs: CS3019

trunk r148762のgmcsで発生。 以下のようなメソッドで警告CS3019が発生する。

  1. アクセス修飾子が指定されていないpartial class内で定義されている(クラス全体はpublic)
  2. CLSCompliant属性が付与されている
repro code
using System;

[assembly: CLSCompliant(true)]

public partial class PartialClass {
  public void Method1(int arg)
  {
  }

  [CLSCompliant(false)]
  public void Method2(uint arg)
  {
  }
}

/*public*/ partial class PartialClass {
  [CLSCompliant(false)]
  public void Method3(uint arg)
  {
  }
}
result
$ gmcs --version
Mono C# compiler version 2.7.0.0

$ gmcs PartialClass.cs /clscheck+ /t:library /o:test.dll
PartialClass.cs(17,4): warning CS3019: CLS compliance checking will not be performed on `PartialClass.Method3(uint)' because it is not visible from outside this assembly
Compilation succeeded - 1 warning(s)

PartialClass.Method3がinternal classのメソッドとみなされているため'is not visible'となる? 試しにpublicを明示的に指定すると警告が出なくなる。

: ID:+iq/7O8y

Mono: Enum.TryParse<TEnum>

TEnumがEnumでない場合でもArgumentExceptionをスローしない。

repro code
using System;

class EnumTest {
  public static void Main()
  {
    Console.WriteLine("{0} {1}", Environment.Version, Environment.OSVersion);

    try {
      Guid parsed;

      var ret = Enum.TryParse("val", out parsed);
      Console.WriteLine("ArgumentException not thrown");
      Console.WriteLine("{0} {1}", ret, parsed);
    }
    catch (ArgumentException ex) {
      Console.WriteLine(ex);
    }
  }
}
.NET Framework 4
4.0.30319.1 Microsoft Windows NT 6.0.6002 Service Pack 2
System.ArgumentException: 指定された型は Enum でなければなりません。
パラメーター名: enumType
   場所 System.Enum.TryParseEnum(Type enumType, String value, Boolean ignoreCase, EnumResult& parseResult)
   場所 System.Enum.TryParse[TEnum](String value, Boolean ignoreCase, TEnum& result)
   場所 EnumTest.Main()
Mono 2.7
4.0.21006.1 Unix 2.6.31.20
ArgumentException not thrown
False 00000000-0000-0000-0000-000000000000
パッチ
Index: System/Enum.cs
===================================================================
--- System/Enum.cs	(revision 155838)
+++ System/Enum.cs	(working copy)
@@ -628,9 +628,13 @@
 		public static bool TryParse<TEnum> (string value, bool ignoreCase, out TEnum result) where TEnum : struct
 		{
 			Type tenum_type = typeof (TEnum);
+
+			if (!tenum_type.IsEnum)
+				throw new ArgumentException("TEnum is not an Enum type.", "enumType");
+
 			result = default (TEnum);
 
-			if (value == null || value.Trim ().Length == 0 || !tenum_type.IsEnum)
+			if (value == null || value.Trim ().Length == 0)
 				return false;
 
 			return Parse (tenum_type, value, ignoreCase, out result);
テスト
Index: Test/System/EnumTest.cs
===================================================================
--- Test/System/EnumTest.cs	(revision 155838)
+++ Test/System/EnumTest.cs	(working copy)
@@ -705,11 +705,18 @@
 			Assert.AreEqual (false, success, "#D1");
 			Assert.AreEqual (TestingEnum.This, result, "#D2");
 
-			// TryParse can accept any struct derived type
-			int n;
-			success = Enum.TryParse<int> ("31416", out n);
-			Assert.AreEqual (false, success, "#E1");
-			Assert.AreEqual (0, n, "#E2");
+			// TryParse throws ArgumentException if TEnum is not an enumeration type
+			try {
+				int n;
+				Enum.TryParse<int> ("31416", out n);
+				Assert.Fail ("#E1");
+			} catch (ArgumentException ex) {
+				Assert.AreEqual (typeof (ArgumentException), ex.GetType (), "#E2");
+				Assert.IsNull (ex.InnerException, "#E3");
+				Assert.IsNotNull (ex.Message, "#E4");
+				Assert.IsNotNull (ex.ParamName, "#E5");
+				Assert.AreEqual ("enumType", ex.ParamName, "#E6");
+			}
 		}
 
 		[Test]
: ID:W66Fhlmt

俺はラムダ式の書式に抵抗感を覚えるようで、
まだ

delegate(int i){ return i; }

という匿名関数の形を良く使っている。

ただ、どうも打ち間違いが多いと思ったら、MonoDevelop 2.8でコードを書いていると、変数iが補完リストに出てこなかったのだ。

これと同じだね。(調査中の過程だったんだ、俺のコメント2は)
http://bugzilla.xamarin.com/show_bug.cgi?id=1132

で、ソースコードを追い始めた。
何かSystem.Console.WriteLineでコンソールに出力できなくって、
ファイルに書き出すコードを突っ込みまくって調べた。

どうやら、ファイルを変更するたび、ソースコードの構文木を作り、
Visitorパターンで最上位から流れるように追っかけるようだ。

ICSharpCode.OldNRefactory.Visitors.LookupTableVisitorは恐らく、変数などのidentifierを集めてAddVariableで変数の型、変数名、宣言自体の位置、変数使用可能範囲等を登録する。

たとえば、変数宣言でいうと、
最初は変数の型、次が変数名、次が、変数の宣言開始位置、次が変数の使用終了位置(おそらくenclosing blockの終わり)、次が修飾子で、
変数使用可能な範囲の始まり(補完リストに追加する地点:inListPosition)が、「セミコロンの後から」

public override object VisitLocalVariableDeclaration(LocalVariableDeclaration localVariableDeclaration, object data)
{
	for (int i = 0; i < localVariableDeclaration.Variables.Count; ++i) {
		VariableDeclaration varDecl = (VariableDeclaration)localVariableDeclaration.Variables[i];
				
		AddVariable(localVariableDeclaration.GetTypeForVariable(i),
				            varDecl.Name,
				            localVariableDeclaration.StartLocation,
				            CurrentEndLocation,
				            (localVariableDeclaration.Modifier & Modifiers.Const) == Modifiers.Const,
				            false, varDecl.Initializer, null, false, localVariableDeclaration.SemicolonPosition);
	}
	return base.VisitLocalVariableDeclaration(localVariableDeclaration, data)
}

これがラムダ式なら、

public override object VisitLambdaExpression(LambdaExpression lambdaExpression, object data)
{
	foreach (ParameterDeclarationExpression p in lambdaExpression.Parameters) {
		AddVariable(p.TypeReference, p.ParameterName,
			lambdaExpression.StartLocation, lambdaExpression.ExtendedEndLocation,
				false, false, null, lambdaExpression, false, lambdaExpression.StartLocation);
	}
	return base.VisitLambdaExpression(lambdaExpression, data);
}

変数使用可能な範囲の始まりは、そのラムダ式の始まり、というわけだ。

これを踏まえて匿名関数のほうを見ると、

public override object VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data)
{
	foreach (ParameterDeclarationExpression p in anonymousMethodExpression.Parameters) {
		AddVariable(p.TypeReference, p.ParameterName,
			anonymousMethodExpression.StartLocation, anonymousMethodExpression.EndLocation,
				            false, false, null, null, false, anonymousMethodExpression.EndLocation);
		}
		return base.VisitAnonymousMethodExpression(anonymousMethodExpression, data);
	}
}

変数使用可能な範囲が
その「匿名関数の終わり」から「匿名関数の終わり」になっている。これは誤りだ。だから以下のように修正する。

public override object VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data)
{
	foreach (ParameterDeclarationExpression p in anonymousMethodExpression.Parameters) {
		AddVariable(p.TypeReference, p.ParameterName,
			anonymousMethodExpression.StartLocation, anonymousMethodExpression.EndLocation,
				            false, false, null, null, false, anonymousMethodExpression.StartLocation);
		}
		return base.VisitAnonymousMethodExpression(anonymousMethodExpression, data);
	}

}

===

CSharpBindingプロジェクトのMonoDevelop.CSharp.Completion.CSharpTextEditorCompletion.HandleCodeCompletion
から615行目default:へ流れて、636行目CreateCtrlSpaceCompletionDataの呼び出しがあり、
1961行目CreateCtrlSpaceCompletionDataの初っ端でこのLookupTableVisitorをメンバに持つ、NRefactoryResolverの設定を行う。
ここで、VisitCompilationUnitが行われている。同時にカーソルの位置も記録。

expressionResult.ExpressionContext == ExpressionContext.Defaultを満たして呼ばれるresolver.AddAccessibleCodeCompletionDataでは
カーソルの位置と、各変数の使用可能範囲であるかを確認して、その範囲内であれば、その変数を補完リストに追加する。
さっきの「匿名関数の終わり」から「匿名関数の終わり」の中にはカーソルが入らないので補完リストに加わらなかったところ、
加わるようになったので、正常に作動するようになる。

: ID:W66Fhlmt

検証してある程度見当がついたら気が抜けてしまい、
後で見返したら自分がどうしてそういうコードだと理解できたのかわからずしばらく悩んだ。

多分、HandleCodeCompletionはCompletionTextEditorExtension.KeyPressから来たんだと思う。
その後、継承先であるCSharpTextEditorCompletion.HandleParameterCompletionが呼び出され、

その中で

NRefactoryParameterDataProviderのコンストラクタのオーバーロードである
public NRefactoryParameterDataProvider (TextEditorData editor, NRefactoryResolver resolver, IType type)
が呼ばれる。何故かコンストラクタを探すためのコードのはずなのにInvokeメソッドを探していたので、


if (type != null) {
	if (type.ClassType == MonoDevelop.Projects.Dom.ClassType.Delegate) {
		if (type is InstantiatedType) {
			this.delegateName = ((InstantiatedType)type).UninstantiatedType.Name;
		} else {
			this.delegateName = type.Name;
		}
		foreach (IMethod method in type.Methods) {
			if (method.Name == ".ctor"){
				methods.Add (method);
			}
		}
		return;
	}
}

等と実験的に変更してみるも、どうやらコンストラクタとなるメソッドが存在せず、実際にビルドして使ってみるとうまくいかなかった。
そこで、ITypeとなる引数を調べてみることにした。

このあたりで、MSDNには該当するInvokeメソッドなんか載ってないことを疑問に思った俺は検索を掛けたところ、
コンパイラが勝手に足す、という情報を得ることが出来た。
http://www.codeproject.com/KB/cs/delegatesneventsinternals.aspx

さて、IType typeはresolver.SearchTypeの戻り値であり、これは結局、ソースコードのパースで得られた型の集まりから、
該当する型を取ってくるものであるから、その生成過程がおかしいと考え、BeginInvokeやInvokeで検索し、その正体がDomTypeであることを突き止める。

https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Dom/DomType.cs#L460
確かにInvokeしか追加されていない。

そこで

DomMethod delegateMethodconstructor = new DomMethod (".ctor", Modifiers.None, MethodModifier.None, location, DomRegion.Empty, new DomReturnType(name));
delegateMethodconstructor.Add (new List<DomParameter>());
result.Add(delegateMethodconstructor);

とか実験的に足してあげたところ、確かに、ビルドして試すと引数なしのコンストラクタの情報が掲示されるようになった。
==============-
自分が今のところわかっていないのは、

上記のコンストラクタにおいて、メソッド名を指定するような形が本来求められるわけだけれど、

一体メソッド名の型は何としたらいいのだろう?ということ。2引数のコンストラクタを定義したらうまくいくなどということもないだろうと思う。
あるいはコンストラクタとしてとらえるのをやめなければならないのだろうか?

ということである。

====
話題的に大丈夫かなあ、自分ではと思って書き込んでみたけれど、失礼だったり、邪魔だと感じる投稿であればそちらで消していただけると幸いです。

: ID:sisSL6UE

私も興味ありますし、こういった内容もいいかなと思っているので、書き込んで頂いて大丈夫です。

: ID:YaEYXrcV

前回の解決をしないまま(面倒くさがりなので今後もしないだろう)
今回はちょっと傾向が異なる話。

発端は「VB.NETで出来ることは、殆どC#で出来るはず…だよね。」と思ったこと。

VBは結構特有の構文があるので、考えてみたわけだ。

脱線 - 昔のVBの構文に無理やり当てはめたC#と誰かが言っているのを見かけた。
構文が美しいのがC#かどうかは紛糾するからおいておくとして
http://logsoku.com/thread/tsushima.2ch.net/news/1253599691/#400

→Declare構文はどうだろう?vbncのソースを追ってみよう!
https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/Members/ExternalSubDeclaration.vb#L97

なるほど。DllImportAttributeをくっつけたものに対する糖衣構文だったわけだ。

じゃあ、C#に同じものがない、とされる文字列同士のLike演算子はどうなっているのかな、と思って追ってみた。
http://dobon.net/vb/dotnet/vb2cs/vblike.html

Microsoft.VisualBasic.CompilerServices.LikeOperatorだの
Microsoft.VisualBasic.CompilerServices.Operatorsだの飛ばされるけど、
Microsoft.VisualBasic.CompilerServices.StringTypeのStrLikeメソッドが本体であるようだった。

なお、こいつらは、MSDNにも載っているから、一応使おうと思えばC#から使うことができないこともない。
ただ、あくまでフレームワーク側をサポートするものであり、消えたり実装の内容が変わったりすることもあるかもしれない。
また、
http://en.sourceforge.jp/forum/forum.php?thread_id=26779&forum_id=15322
こういうことを報告した身としてはあまり使わせる気にはならない。

話を戻そう。
https://github.com/mono/mono-basic/blob/master/vbruntime/Microsoft.VisualBasic/Microsoft.VisualBasic.CompilerServices/StringType.vb#L215

ConvertLikeExpressionで、正規表現を用いた実装に切り替えている。
https://github.com/mono/mono-basic/blob/master/vbruntime/Microsoft.VisualBasic/Microsoft.VisualBasic.CompilerServices/StringType.vb#L239

え?この実装ちょっと待て。


using System;

namespace Like{
	public class Like{
		public static void Main (){
			System.Console.WriteLine(Microsoft.VisualBasic.CompilerServices.StringType.StrLike("\\5","\\#",Microsoft.VisualBasic.CompareMethod.Binary));
		}
	}
}

MSのコンパイラ+ライブラリ→True
Monoのコンパイラ+ライブラリ→False

やっぱりなorz

本家に報告するかはわからん。気分屋なのでね

: ID:gQ1zLaRR

例によって役に立たない挫折日記である。まったく俺は掲示板というものを何だと思っているのやら。
ネタバレ: 最後は挫折。

俺はVista + MonoDevelop 2.9.2という環境で使っている。
先日、Mono 2.11が出たというので、手元の2.10.8と入れ替えることに。

インストール自体は特に問題なく終わった。

以前提出したバグレポは、2.10には入っていないが、2.12の前座であるこのバージョンなら
この修正が入っているかもしれないと思い、試す。

Bug 678357 - Setting System.Console.OutputEncoding should change System.Console.Out.Encoding
https://bugzilla.novell.com/show_bug.cgi?id=678357

MonoDevelopでさくっとコードを書いて、コンパイル…出来ないorz

MonoDevelopはこんなふうにフレームワークのバージョンによって使うコンパイラの名称を場合分けして
https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoFrameworkBackend.cs#L108
でこうやってMonoのインストール先のbinディレクトリからdmcs.batを探すの。
https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFrameworkBackend.cs#L69

ただ、Mono 2.11はこんな風にわかれてない。4.5のコンパイラを利用するmcs.batしかないので、dmcsという拡張子なしのファイルを呼び出そうとしてコンパイルに失敗する。
MonoDevelopのこの部分のコードを治すのはちょっと面倒かな、と思ったので後回しにして、mcs.batをコピーしてdmcs.batに名称を変更する。

コンパイル成功。上記バグは治ってることを確認できた。

余談。
俺はあまり開発を理解していないようだ。躊躇いがあるというかなんというか。
以前、OpenOffice.orgに対してバグ報告した際にも、「これ、俺がやらないといけないことなのか?それとも専門のQAチームに任せなければならないのか?」と、まごついてコメントしている。
正直、現在も自分の立ち位置がわかっていない。批判を少し怖がっているようなところがあって、あまり相談することもないからね。
https://issues.apache.org/ooo/show_bug.cgi?id=105165#c7
上記のMonoのバグに対しても、「テストされれば、2.10にバックポートできるよ」というその「テスト」を「俺がやるべき」だったのか自信が持てず、結局返答を控えることになってしまっている。無駄に引っ掻き回して迷惑になりたくもない。

閑話休題。上記のようにSystem.Console.OutEncodingをUTF-8に設定(BOMを出力しないほうが見やすいが)した時、
System.Console.WriteLine("あ");と入力した時に「アプリケーション出力」に出力される結果は

縺・

UTF-8で「あ」を表した時の3バイトを日本語版Windowsの「Encoding.DefaultであるShift_JISで解釈した」とすると、最初の2バイトが縺で、ラスト1バイトを表すことができずにU+30FBに置き換える羽目になったわけだ。

つまり、このアプリケーション出力では、Unicodeにしか含まれないU+2704等の文字を表示できないってことだ。
イチイチファイルに出力するのも面倒だし。コードページ65001に変更したコマンドプロンプトから起動ってのもなあ…。

…結構前からこれは気になっていて(調査はしてなかった)、バグ報告(という名の丸投げ)しているのだが、皆の関心は殆どないようで、進展は特になさそうだ。

Bug 680024 - Let me change Encoding for OutputPad
https://bugzilla.novell.com/show_bug.cgi?id=680024

丸投げするだけじゃ怒られそうな気がしたため(誰にw?)、少しコードを追ってみることにしたってわけだ。
自信はないが、ここじゃないかなーと思う。もしここで、StandardOutputEncodingを弄ってUTF-8を代入していればなんとかなるんじゃないか。
https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessService.cs#L198

仮説は実際に動かしてみるまで飽くまで仮説。

早速、MonoDevelopでMonoDevelopをビルドすることに。「今まで何度かやってきたから、出来るだろう」などとこの地点では高をくくっていたのだが…。

Mono.AddinsのソースとMonoDevelopのソースをダウンロードして、MonoDevelopの中でMono.Addinsの各プロジェクトへの参照として追加する。
あ、ついでに各プロジェクトを.NET 4.0を対象にしなおしておく。何となく上のバージョンのほうが落ち着く。いつものことだ(おぃ

コンパイルが確率でしか成功しない。しかも失敗するプロジェクトがその時によって違うorz何でだろう。

試行錯誤すると、たまに成功するんだけど…
自分でビルドしたMonoDevelopはMSの実装上ではウィンドウ自体が表示されないので(いつものこと。理由不明。もしかしたらgacにライブラリ突っ込んでおく必要があったりするのかなあとかボンヤリ考えている)
コマンドプロンプトからmono上で起動…今回は今までになくスプラッシュスクリーンでハングしたりlibgtk-win32-2.0-0.dllが原因のクラッシュが発生する。
これまたイヤなことに確率でどっちになるかが別れる。

警告減らすか。えーっとCS1701はMono.Cairoで2.0のライブラリと4.0のライブラリがあって、どっち呼び出すのかわからない、か。
自信はないけど、確かに、参照設定しているライブラリ(どれか忘れた)が、Mono.Cairo 2.0を参照しているようだ。対する俺はさっき、4.0にしたばかり。
もしかしたら、それが原因かもしれない。ただ、そのままのバージョンじゃビルドできないやつ確かあったしなあ。

対処はっと…えー、こんな面倒くさいコードに書き換えて、コンパイルオプション書かなきゃいけないの?
http://msdn.microsoft.com/en-us/library/2h4x8b08%28v=vs.80%29.aspx

ええい、面倒くさいっ!GTK#ごとビルドしてやるっ!
http://www.mono-project.com/Compiling_GtkSharp

Building Gtk# 2.12 with .net >= 2.0 is known not to work.

泣きたい…。

: ID:0r8C5mnA

保守

: ID:QcPoAweP

殴り書き

何度でも繰り返す。「俺は一体掲示板を何だと考えているのだ」と。

makefileを読んだりしていないけれど、Mono,GTK#,MonoDevelopのWindows版公式ビルドはどのようにビルドされているのだろう。
使っているのがMSのコンパイラ + MS.NET Frameworkである気がしてる。

==============【以下しばらく読まなくてもいい】================

Mono上で4.0向けGTK#アプリを作ったところ(実際にやったのはMonoDevelopのslnのビルド)、問題が二点あった。
●バックアップとるとか、あるいはMonoとGTK#の再インストールとか覚悟してね。

●各ライブラリはgithubからzipファイルへのリンクをクリックしてmasterの最新版をダウンロードして展開して参照している。
いい加減バージョン管理ソフトだのビルドツールくらい、コマンドライン慣れろよ>俺

●なお、MonoDevelopはそのソース中、拡張メソッドを使っているので.NET 2.0向けのビルドは不可。
3.5は知らんが、確か参照しているプロジェクトが何か.NET 4.0用のライブラリ・クラスを使っていた気がするのでたぶん無理じゃないかなー。

一つ目。
Mono側に入っているlibgtk-win32-2.0-0.dllのバージョンは2.16.6で、Windows版GTK#についてくるのは2.24とかその辺のバージョンなので、
Mono側から持ってきたファイルで上書きします。(なぜかそうしないとコケる。)

二つ目。
Windows版 GTK#インストーラでは、MS .NET 2.0向けにMono.Cairo.dll,Mono.Posix.dllがインストールされる。
MonoのインストーラではMonoのgacに2.0用と4.0用のMono.Cairo.dll,Mono.Posix.dllがインストールされる。

MonoDevelopをMonoでv4.0用にビルドすると、gacから.NET 4.0のものが引かれる。
できたアセンブリを、Windows上の.NET上で動かそうとすると4.0用のライブラリがないのでコケる。

まぁしょうがないから、Monoからバイナリをコピーしてmonodevelop.exeのディレクトリに入れて…一応動くはず。

●なんかメモによると、internalとかprivateになっているやつをpublicにしないと、MonoDevelopで定義場所探したら何か他のプロジェクトにある同名クラス(っていうか同じ仕組みで生成されるファイル)が参照されていて謎とかあった。当時(2012-06-24)のことあまり思い出せなくて、それでコケたかは忘れた。
https://github.com/mono/monodevelop/blob/master/main/src/addins/MonoDevelop.GtkCore/libsteticui/CodeGenerator.cs#L34

●…とまぁ落とし穴が沢山あるので、
http://atsushieno.hatenablog.com/entry/2012/08/27/130305
を知った時、俺はこっそり色々心配してたとか。(「余計なお世話」なの自覚しているので「こっそりと」)

●Linuxサイドからクロスプラットフォームの世界を見せるなら、v4.0向けの書き方やライブラリ禁止ってことだね。

●まぁ、Monoでのビルドをデフォルトにしている偏屈な俺と違って、
普通なら、WindowsではMS .NET Frameworkを使うだろうし、作った環境で動かすだけならトラブルにはならないだろうから、
過剰な考えなんだろうなーと。

●「多分.NET 2.0より後でGTK#がビルドできないのはMono.PosixやMono.Cairoが提供できないから」って点も
●「MSのコンパイラを使っている」説の傍証の一つになっている。俺の中では。

==================【ここから本題】======================

と、ここまでが前座。気づいた時の経緯の一つで、実は殆ど本題には関係ない(笑)。
「公式ビルドはMSのコンパイラを使っている」と俺が考えている、ってことが大事。

: ID:QcPoAweP

あるとき(Mono 2.11.1,2012-04-23)から、Mono最新版にくっついてくるvbncで、
libpathとか指定なしで、vbのコードをコンパイルしようとするとMicrosoft.VisualBasic.dllが無い!って言ってくるようになったんだ。

●しばらく調査を面倒臭がっていた。

C:\Program Files\Mono\v2.11.4\bin
C:\Program Files\Mono\v2.11.4\lib
C:\Program Files\Mono\v2.11.4
の3つをPATHに放り込んでるから、vbnc.batで実験してた可能性大だが、MonoDevelopなしでも再現する。
また、
vbnc.batの中身を
@"C:\PROGRA~1\Mono\V211~1.4\bin\mono.exe" %MONO_OPTIONS% "C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.5\vbnc.exe" %*
から、
@"C:\PROGRA~1\Mono\V211~1.4\bin\mono.exe" %MONO_OPTIONS% "C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\vbnc.exe" %*
とか書き換えてもまだ「ない」とか言ってきた。

vbncのコードは、一部ライブラリを除きVB.NETで書かれているので、MSのvbcを使って.NET 4.0向けにビルドとかやってみたけど無駄だった。

●MfAもPSSuiteも使ったことないけど。

上記のように色々弄った状態ではあるが、

https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/General/Compiler.vb#L119
あたりがその辺の情報を持ってそうと考え、
https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/General/Compiler.vb#L467
LibPathを参照してそうなところがここしかなく、ここを見る
https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/General/Compiler.vb#L989

こう大雑把に書いてログ取り。

●try catch finallyとかusingどこ行った>俺

Private Function GetSystemDir() As String
    Dim assemblies() As Reflection.Assembly
    Dim result As String
    Dim sw As System.IO.StreamWriter = New System.IO.StreamWriter("D:\compilerdebugging\hoge2.log",True,System.Text.Encoding.UTF8) 

    If Not String.IsNullOrEmpty(CommandLine.SDKPath) Then
        If CommandLine.Verbose Then Report.WriteLine(string.Format ("Using alternate system path: {0}", CommandLine.SDKPath))
        Return CommandLine.SDKPath
    End If

	sw.WriteLine("pass")

    assemblies = AppDomain.CurrentDomain.GetAssemblies

    For Each a As Reflection.Assembly In assemblies
        Dim codebase As String = a.Location
		sw.WriteLine(codebase)

        If codebase.EndsWith("corlib.dll") Then
            result = codebase.Substring(0, codebase.LastIndexOf(System.IO.Path.DirectorySeparatorChar))
            If CommandLine.Verbose Then Report.WriteLine(String.Format("Using system path: {0}", result))
            sw.Flush()
            sw.Close()
            
            Return result
        End If
    Next
    Throw New InternalException("Cannot compute the system directory.")
    Return ""
End Function

出力結果:

pass

C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.Core.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.Xml.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.Configuration.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\I18N.CJK.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\I18N.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\Mono.Cecil.VB.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\Microsoft.VisualBasic.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\vbnc.exe
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.5\mscorlib.dll

何で最後だけ4.5なんだよ(怒)
「AppDomainのソースコードから呼び出されるCのコードはバージョン非依存になってそうな気がする」と勘を働かせ、Cに慣れてないこともありそこから逃避。

ただ、納得がいかず、うんうん唸りながら検索を掛けて一つの仮説を思いつくことになる

MS .NET 4.5って、
http://www.west-wind.com/weblog/posts/2012/Mar/13/NET-45-is-an-inplace-replacement-for-NET-40
MS .NET 4.0を置き換えちゃうんだな。

http://atsushieno.hatenablog.com/entry/2012/06/14/141356
の件があって、RTMを待って入れている俺の環境でもそうなっている。
(8月21日でも、当時結構RCとか紛らわしい状態だった。以上のことは俺がどこも間違えてない前提。)

MSのコンパイラvbcは、ビルドしたアセンブリを4.5扱いにするんじゃね?
で、公式のビルドマシンもそうなっているから、今回のトラブルが発生しているんじゃないか?

ってのが自分なりの今の仮説。
…まだやってないけど、.NET 4に戻せば治っちゃったりして。
バージョンを下げるのって負けた気がするからあまりやりたくないのだが…(いつ検証するか不明。しないかも)

ふと思ったこと。
●エンドユーザーへの説明・影響
●テスト体制・リリースマネジメントの改善

=======================
どうでもいいが個人的にWikiの記法って嫌いだ。コメント扱いのつもりでアスタリスクやナンバーサインを書くとorz

: ID:QcPoAweP

何か重要なこと言ってない気がする。

補足というか本題冒頭への追加というか:

Microsoft.VisualBasic.dllをMonoのgacの4.0のディレクトリから4.5用のディレクトリ(こっちにはこれがない)にコピーすればコンパイルできることから4.0ではなく4.5のディレクトリを読みに行き、同ディレクトリ内のMicrosoft.VisualBasic.dllを探しているものと考えた。そこで、読み込むディレクトリを決定している部分はどこかという発想に至った。

: ID:0r8C5mnA

>>25
vbncは確か4.5が含まれるようになる前から既にMicrosoft.VisualBasic.dllが無いって言うようになってた気がしますね。

どうでもいいが個人的にWikiの記法って嫌いだ。

すみません、使い慣れてる書式なのでこの掲示板でも使ってる次第です。
書式無しで書き込めたほうがいいですか?
そのうちにでも書式選べるようにしてみます。

: ID:2oa1H1/e

保守。

Xamarin Studio 4.0が公開されたため、インストールしてみたのだが、Version Controlメニューを選択するとエラーが出て、かつ、ダイアログのボタンをクリックすると、IDEが落ちる現象に遭遇した。
表示通りVS 2005 Redist SP1を入れても治らず。

エラーメッセージをググったところ、どうやらエラー表示箇所はこいつらしい

https://github.com/mono/monodevelop/blob/master/extras/VersionControl.Subversion.Win32/VersionControl.Subversion.Win32/SvnSharpClient.cs#L52

メンバ変数installErrorを変更しているのが、どうやらここだけっぽい。

https://github.com/mono/monodevelop/blob/master/extras/VersionControl.Subversion.Win32/VersionControl.Subversion.Win32/SvnSharpClient.cs#L28

SvnClientコンストラクタでエラーが出ているわけか。
例外起きたら何でもかんでもこの解決策を掲示されるのはどうかと思う。まぁそういう俺も対処があるのかは知らないまま投げっぱなしだけど。

ここで、Xamarin Studio 4.0にログ機能があることにようやく気づく。
何々SharpSvn.dllが見つからない。csprojとかslnとか、あるいはバイナリを見る形で見つけられるか。

バイナリ中に以下の記述を発見。

https://github.com/mono/monodevelop/blob/master/extras/VersionControl.Subversion.Win32/VersionControl.Subversion.Win32/Manifest.addin.xml

一応このファイルの書式を調べてみた。

http://monoaddins.codeplex.com/wikipage?title=Files%20included%20in%20an%20add-in&referringTitle=Reference%20Manual

どうもアセンブリとfileは異なるものであるようなので、Stirlingで挿入して修正。
機能が使えるかは試していないが、とりあえず落ちることはなくなったようだ。

まぁ何かもう既にバグレポとか出てそうだけど、探すのが面倒。

: ID:2oa1H1/e

どうやら俺はまた誤診をしたようだ。
上記は単に、ファイルを壊した結果アセンブリとして認識されず、当然アドインとして認識されない結果としてエラーが表示されなくなっただけだった。

「エラーメッセージが間違っている」までは正しいが、
SharpSvn.dllはVC9のCRTを参照しているため、VS2008 Redist SP1が正しい。

: ID:vZ7YOR5H

はじめまして。
VisualBASICを使ってバイナリでファイルの入出力を行うサンプルなどを探していて、こちらのサイトにたどり着きました。
説明も解りやすく、サンプルも簡潔で非常に助かりました。
ただ一点どうも思った動作にならないので、自分で別なサンプルを作成したり、MSDNを読んだりしたところ、間違っているようなのでこちらに記載させて頂きます。

BinaryReaderクラス・BinaryWriterクラス
http://smdn.jp/programming/netfx/stream/3_binaryreader_binarywriter/
の中のバイト配列の読み込み (ReadBytes, Read)には
「ReadInt32等のメソッドと同様、ReadBytesメソッドでストリームの残りバイト数よりも多いバイト数を読み込もうとした場合には、例外EndOfStreamExceptionがスローされます。」 

と記載がありますが、スローされませんでした。
このため自分が作成したサンプルで、Try Catchとして、ファイル末尾を判断して処理するようにしましたが、Catchされませんでした。

MSDNのBinaryReader.ReadBytes メソッドにも
http://msdn.microsoft.com/ja-jp/library/system.io.binaryreader.readbytes%28v=vs.95%29.aspx

例外の中にEndOfStreamExceptionの記述がありませんでした。

よろしくおねがいします。
今後もいろいろと参考にさせて頂きます。
ありがとうございます。

: ID:Q4FXA/AS

>>30
BinaryReader.ReadBytesメソッドの動作についてですが、ご指摘頂いた通り
記述されている内容が誤りがありました。

正しくは「ReadBytesメソッドではEndOfStreamExceptionがスローされることはなく、
実際に読み込めた分の長さのバイト配列が返される」となります。

従って、ReadBytesを呼び出す場合はEndOfStreamExceptionがCatchされることはありません。
ファイル末尾に達したかどうかの判断は、ReadBytesが返す配列の長さが
引数で指定したバイト数よりも少ないかどうかで調べることができます。

当該ページの記載内容も修正いたしました。 ご指摘ありがとうございました。

: ID:GM8sbjMe

今でも自分は引っ込み思案なところがあるようで、バグ報告を躊躇う。
何人かにちょろっと話題を出したりすることはあっても(ここに書くのもその一環)、自分ではあまり報告しないのだ。【以下、無関係な話が続くので最後の1行までジャンプ】

LibreOfficeにはStarBasicというマクロ用の言語がついており、COMっぽい仕掛けを多用すること以外の文法的なことはVBA/VBSに似せようと(もともとBasic系共通なのかもしれないが)している気がする。文法書ないんだけどね。

VBAで#を使うケースで俺がポンと出てくるのが、

   Doubleの型指定子: http://msdn.microsoft.com/en-us/library/office/gg264155%28v=office.15%29.aspx
   ファイルIO: http://msdn.microsoft.com/en-us/library/office/gg264163%28v=office.15%29.aspx
   日付リテラル: http://msdn.microsoft.com/en-us/library/office/gg278460%28v=office.15%29.aspx

の3つだ。

さて、StarBasicでこんなコードを書いてみよう。

Sub Main()
Dim x As Date
Dim y As Date
x = #1/2/2013#
y = 1/2/2013
Msgbox(x <> y)
End Sub

Dateが日数単位のDoubleならば、当然x > 1かつ、yは単なる割り算だからy < 1でこの二つが一致するわけがないし、実際VBAではMsgboxはTrueを表示する。(つまり期待通り等しくない)
StarBasicではFalseである。

コンパイラのソース見てみようか。
http://opengrok.libreoffice.org/xref/core/basic/source/comp/scanner.cxx#406
これはそれほどおかしいと思わなかった。
http://opengrok.libreoffice.org/xref/core/basic/source/comp/scanner.cxx#242
bHashって何のためにあるんだろうねー。
http://opengrok.libreoffice.org/xref/core/basic/source/comp/scanner.cxx#481
VBAのリテラルって#で囲まれているんだよね…文字列ルーチンを再利用してしまおうか…何か上の方にあるelse ifあたりの条件足りなくないか?

と、ここまでが無関係な発端。いやmonoのvbncはどう解釈するのかなって話が(凄く短いけど)メイン。
http://ideone.com/HYXMKw
このエラーはどうなんだろう。vbcは演算子が定義されていないというが

: ID:18I6Xog9

「ファイル・ディレクトリの操作」のサンプルソースには下記の構文エラーが発生したので、ご連絡させていただきます。
URL:
http://smdn.jp/programming/netfx/filesystem/1_filesystem/

ーーーーーーーーーー

   // カレントディレクトリにあるfile2.txtをディレクトリE:\dir\に移動する
   File.Move("file2.txt", @"E:\dir\"); //★★エラー

ーーーーーーーーーー

: ID:+LyRQS9z

ご報告ありがとうございます。
検証したところご指摘のC#コードには文法上の問題はないことを確認しております。

: ID:cLS5Tirc

Mono のビルド・インストール(下のURL)で、MonoLiteが入手できない場合は、以下の5つのバイナリを別の環境からコピーするなどして手動で../mcs/class/lib/monolite/に配置する。と書かれているんだけど、具体的にmonoliteフォルダーを含むmcsフォルダーはどこのディレクトリーに配置すればコマンドとして機能するのか教えて欲しい。
http://smdn.jp/programming/mono/build_install/mono_from_git-master/

: ID:+LyRQS9z

>>35
例えばgit cloneで/home/user/mono/にクローンした場合なら、/home/user/mono/mcs/class/lib/monolite/になります。

mcs/class/lib/のディレクトリはソースツリーの中に存在しているはずなので、そこにmonoliteディレクトリを作成して、
バイナリをコピーしてくればよいことになります。

ただ注意事項としても書いているように現在のバージョンでも機能するかはわかりませんし、System.Core.dllなど
ほかに必要になるアセンブリもあるかもしれません。

: ID:cLS5Tirc

回答をありがとう。35で質問した者です。
自分は、MONOの圧縮ファイルを解凍してビルドしたんだけど、自分でビルドしたものをインストールするのと、ここで(http://www.mono-project.com/download/)ダウンロードしたものをインストールするのでは何か意味や目的が違うのか教えてほしい

: ID:+LyRQS9z

>>37
例えば、インストールするディレクトリを自分で決めたいとか、コンパイルオプション等を指定してビルドしたいとか、
あるいはソースを修正しながらデバッグやテストをする必要があるとか、そういった目的がある場合は
自分でソースからビルドするという選択になると思います。

逆にhttp://www.mono-project.com/download/で紹介されているパッケージの場合はビルド済みのものを
インストールすることになるので、上記のようなことはできませんが、特にそういう目的に使う予定がなく、
とりあえず動作する環境が用意できればよいのであれば、パッケージからインストールするという選択になると思います。

: ID:+LyRQS9z

>>37
追記です。

安定版はパッケージが用意されますが、開発中の最新の状態を使いたい場合はパッケージがない
ことがほとんどなので、そういった場合も最新のソースを取得してビルドすることになります。

: ID:cLS5Tirc

ありがとう

: ID:cLS5Tirc

windowsでソースを取得してここのサイト(http://www.mono-project.com/docs/compiling-mono/windows/)を見ながらVisualStudioを使ってMONO(32bit版)をビルドしたんだけど、これはwindowsだけでなくMacOSやLinuxでもインストールできるものか教えて欲しい

: ID:+LyRQS9z

>>41
32bitということは./autogen.shで'--host=i686-w64-mingw32'を指定したものかと思いますが、
だとするとビルド成果物のMonoランタイムはWin32 APIに依存するものになっているので、
Windows以外では動作しません。

Windows上でビルドしたものをMacOSやLinuxにインストールするのであれば、その環境向けに
クロスコンパイルする必要がありますが、実際にVisual StudioとMonoのソースでそういったビルドが
できるのかはわかりません。

: ID:kmpqh+b/

1>>
「二分木を使った数式の逆ポーランド記法化と計算」のページを拝見しました。
難解なアルゴリズムがわかりやすく簡潔に解説されていてとても肝銘を受けました。すばらしいです!!
このページのC言語のソースコードを読んでいて、strncpyの使い方が気になったのでご報告します。具体的には次のような行です。
  strncpy(node->left->exp, node->exp, pos_op);
strncpyはコピー先に行末コードを書き込みませんので、次のような記述を追加した方が良いかも知れません。
  node->left->exp[pos_op] = '\0';
なおstrncpyはrightのnode用に使われており、計2箇所あります。
私の勘違いかも知れませんが、よろしくお願い致します。

: ID:+LyRQS9z

>>43
問題のコードについて、strncpyが終端文字を書き込まない場合があることを把握していませんでした。
コードを修正し、終端文字で終端させるよう記述を変更しました。
なお、読みやすさを考慮してmemsetでバッファ全体をゼロクリアしてからstrncpyする方法を
採らせていただきました。

記事をお読みいただき、またご指摘いただきありがとうございました。

: ID:kmpqh+b/

>>44
「二分木を使った数式の逆ポーランド記法化と計算」のコードを修正していただき、どうもありがとうございます。
二度手間で大変申し訳ありませんが、気になる箇所をもう1つ見つけてしまいました。
計算式から括弧( )を取り除くための関数remove_bracketの最後の部分です。自分自身を再帰呼びだしした直後にreturn 0で正常終了しています。具体的には次の3行です。

   if (exp[0] == '(')
       remove_bracket(exp);
   return 0;

この書き方だと、最後から2行目のremove_bracket(exp)がエラーコード(-1)を返したとしても、必ず次の行のreturn 0が実行され、呼びだし元にはエラーの発生は伝わらないと思われます。
細かなこと恐縮ですが、ご確認いただければ幸です。

: ID:+LyRQS9z

>>45
ご指摘のとおり、再帰的に呼び出したremove_bracketの結果が返されず常に0となるため、現在の実装には問題があります。
(本来return remove_bracket(exp);とすべきところでreturnの記述が抜けていました)
ただこの問題以前に、そもそも正しく開いていない/閉じていないカッコの扱い自体に不備が多いため、
それも含めて修正したいと思います。

修正次第あらためてお知らせしますが、もしカッコの処理(remove_bracket)以外で不備が見つかりましたら
遠慮無くご報告いただければと思います。

なお現在の実装での動作です。 いずれもエラーとなるよう修正したいと思います。

  • 2+3)→左項2・演算子+・右項3)と解釈される
  • (2+3→演算子なしの単項(2+3と解釈される
  • 1+(2+3))→左項1・演算子+・右項(2+3))と解釈される
  • 1+((2+3)→エラーとなる(意図した動作) unbalanced bracket: ((3+2)
: ID:+LyRQS9z

>>45
遅くなりましたが、ご指摘いただいた件を含め、コードの不備を修正しました。

全体的なアルゴリズムそのものは変更していませんが、remove_bracket関数の処理が変わったほか、
calculate関数等にも変更を施しています。
そのため、コード全体で変更箇所が多くなっている点についてはご了承ください。

変更点の詳細については下記の更新情報をご参照ください。
http://smdn.jp/programming/tips/polish/#DocumentLog

問題点のご報告ありがとうございました。

: ID:kmpqh+b/

「二分木を使った数式の逆ポーランド記法化と計算」のコードを修正していただき、どうも有り難う御座います。
今までとは趣きが大きく変わりましたので正直なところ驚きました。

本格的に書き直していただきましたので、今一度、細部に渡って確認させていただきました。
致命的な不具合は見つかりませんでしたが、気になった箇所が幾つかありましたので、それらを列挙させていただきます。
どうでも良い点ばかりかも知れませんが、多少なりとも何かのヒントにでも繋がれば幸です。

get_pos_operator関数(size_t型)の戻り値が-1になる場合がある。
処理系によってはNG?
size_tは通常unsigned型で定義されるので、ゼロ以下の値は避けた方が良い。
この関数中のpos_operator変数(size_t型)も同様。
この関数の呼びだし元も見直した方がよいかも知れません。

C言語の場合はchar型のポインタで書き換えた方がスッキリすると思います。
ポインタ型が使えない他言語とアルゴリズムを揃えておきたい場合は、size_tをint型やlong型で置き換えるとよいかも。

この関数内では演算子の最低の優先度をあらわす変数priority_currentを4で初期化していますが、
対応する演算子を増やすなどした場合、この値を変更するのを忘れると不具合の原因になりえます。
4よりも大きな、できるだけ大きな整数値で初期化した方が良いと思います。

以下のコードの中で★印で囲まれたコメントは、私が追記したモノです。
==========================================================================

size_t get_pos_operator(char *exp) // ★ size_t型の関数の戻り値に-1? ★
{

   size_t i;                      // ★ size_tは必要? INT_MAX超の長大な式を扱う?                   ★
   size_t pos_operator = -1;      // ★ size_t型に-1?                                               ★
   int priority_current = 4;      // ★ <limits.h>で定義されるINT_MAX等の充分大きな値で初期化したい  ★
                                  // ★ 対応する演算子を増やした場合に、ここを変更し忘れてもOK     ★

==========================================================================

calculate関数内で子ノードのポインタにNULLを代入している次の2行は不要?
==========================================================================
int calculate(Node* node)

        :
   // 左右の子ノードの値からノードの値が求まったため、
   // このノードは左右に子ノードを持たない値のみのノードとする
   node->left  = NULL;  // ★ 不要 ★
   node->right = NULL;  // ★ 不要 ★

==========================================================================

remove_outer_most_bracket関数の先頭部分が冗長で少々難解。次の2点で簡素化可能。

   ・int has_outer_most_bracket = ( '(' == exp[0] );  //  boolean
   ・for (i = 0; i < len; i++) {  //  i=1ではなく、i=0から開始

この関数は何度も呼び出されるので、できるだけ先頭部の処理を軽くしておきたい。
丸括弧の対応を厳密にチェックする関数を別途用意して、数式入力直後に1度だけチェックするようにすれば、この関数の動作を簡素化できる。
この関数の目的を「式全体が括弧で括られている場合の処理のみ」に限定した方がよい。
特にこの関数の先頭付近でのstrlenは止めた方がよい。
==========================================================================
int remove_outer_most_bracket(char *exp)
{

   size_t i;
   // ★ expが長大な場合、strlenは遅い                                                           ★
   // ★ この関数は再帰的に何度も呼び出されるので、できるだけ先頭の処理を軽くしたい              ★
   // ★ 括弧の対応をチェックする際にexpを最後までスキャンすれば、strlen無しでも文字列長は分かる ★
   size_t len = strlen(exp);
   int has_outer_most_bracket = 0; // 最も外側に括弧を持つかどうか(0=持たない、1=持つ)
   // ★ int has_outer_most_bracket = ( '(' == exp[0] );  で初期化した方がよい?                 ★
   int nest = 0;
   // ★ この後のfor文を工夫すれば、次の10行は不要? ★
   if ('(' == exp[0]) {
       // 0文字目が開き丸括弧の場合、最も外側に丸括弧があると仮定する
       nest = 1;
       has_outer_most_bracket = 1;
   }
   else if (')' == exp[0]) {
       // 0文字目が閉じ丸括弧の場合、エラーとする
       fprintf(stderr, "unbalanced bracket: %s\n", exp);
       return -1;
   }
   // 1文字目以降を1文字ずつ検証
   // ★ この前のifとelse ifの両ブロックを削除した場合は、i=1ではなく、i=0からスキャン開始 ★
   for (i = 1; i < len; i++) {
       if ('(' == exp[i]) {
           // 開き丸括弧なので深度を1増やす
           nest++;
       }
       else if (')' == exp[i]) {
           // 閉じ丸括弧なので深度を1減らす
           nest--;
           // ★ ここに nest<0 の場合のエラー処理を入れる? ★
           // 最後の文字以外で閉じ丸括弧が現れた場合、最も外側には丸括弧がないと判断する
           if (i < len - 1 && 0 == nest)
             has_outer_most_bracket = 0;  // ★ false ★
       }
   }
                   :
       // 最初と最後の文字を取り除く
       for (i = 0; i < len - 2; i++) {
           exp[i] = exp[i + 1];
       }
       exp[i] = '\0';
       // 再帰的に呼び出す
       // "((1+2))"など、多重になっている括弧を取り除くため再帰的に呼び出す

// ★ この関数は重いので、旧バージョンのように必要な場合のみ呼び出した方が良い ★
// ★ 例えば、次のように変更 ★
// ★ if ( exp[0] == '(' && exp[i-1] == ')' { ★
// ★ if ( remove_outer_most_bracket(exp) < 0 ) return -1; ★
// ★ } ★
// ★ return 0; ★

       return remove_outer_most_bracket(exp);
     }

==========================================================================

parse_expression関数内で使われているmemsetは避けた方がよい?
計算式が長大にると遅くなる。
==========================================================================
int parse_expression(Node* node)
{

             :
       // 演算子の左側を左の部分式としてノードを構成
       // ★ expが長大になるとmemsetは遅くなる                               ★
       // ★ strncpy後にnode->left->exp[pos_operator]='\0'で終端した方が速い ★
       memset(node->left->exp, 0, MAX_EXP_LEN);
       strncpy(node->left->exp, node->exp, pos_operator);
       // 左側のノード(部分式)について、再帰的に二分木へと分割する
       if (parse_expression(node->left) < 0)
           return -1;
       // 演算子の右側を右の部分式としてノードを構成
       // ★ node->expはnull文字で終端されているのでstrncpyではなく、strcpyでOK ★
       // ★     strcpy(node->right->exp, node->exp + pos_operator + 1);          ★
       memset(node->right->exp, 0, MAX_EXP_LEN);
       strncpy(node->right->exp, node->exp + pos_operator + 1, len_exp - pos_operator);

==========================================================================

3つのtraverse関数の出力結果が見難いので、各式の間に空白等の区切り文字を入れた方がよい。
区切り文字を入れないと、2桁以上の数値を計算した場合に問題あり。
==========================================================================
void traverse_postorder(Node* node)
{

            :
   // 巡回を終えた後でノードの演算子または項を表示する
   printf("%s", node->exp);  // ★ 空白を追加して"%s "とすると見易くなる ★

}
==========================================================================

次はソースコードの書き方に関する問題なので、何らかの正解があるわけではありません。
私個人の意見としては "else if"が何度も続くコードは少々読みにくいと感じます。
次の場合のように、if文に連なるブロックが途中でreturnで抜けている場合は、
後続の条件判断は"else if"ではなく"if"文にした方がスッキリして読みやすいと思います。
==========================================================================
int parse_expression(Node* node)
{

          :
   if ((size_t)0 == pos_operator || (size_t)(len_exp - 1) == pos_operator) {
     // 演算子の位置が式の先頭または末尾の場合は不正な式とする
       fprintf(stderr, "invalid expression: %s\n", node->exp);
       return -1;
   }
   else if ((size_t)-1 == pos_operator) { //  ★ else if ではなく、if の方が読みやすい ★
       // 式Expressionに演算子が含まれない場合、Expressionは項であるとみなす
       // (左右に子ノードを持たないノードとする)
       node->left  = NULL;
       node->right = NULL;
       return 0;
   }
   else {  //  ★ else ブロックを止めた方が読みやすい ★
       // 左側・右側のノードを作成
       node->left   = create_node();
       node->right  = create_node();

==========================================================================

その他

 ・コメント文が充実しているのは理解の助けになり大変ありがたいのですが、
   体言止めを使うなどして、できるだけ文章を簡潔に表現した方が読みやすくなると思います。

任意演算のアルゴリズムは処理速度を要求されるインタープリタの一部として使用されるケースもあると思います。
多少は速度を意識した書き方にしておいた方が、多くの方から好感を持たれると思います。

それでは良いお年をお迎えください。

: ID:+LyRQS9z

詳細にわたりレビューしていただきありがとうございます。
C言語は普段使いの言語ではないため細部が甘くなりがちで、またこういった意見を
いただく機会もなかなかないので、大変ありがたく思います。

指摘いただいた箇所については反映したい部分が多々ありますが、修正が完了するまでには
また時間をいただくことになりそうなので、勝手ながら留意事項として本文中に
書き込みへのリンクを貼らせていただく形での暫定的な対応とさせていただきました。
(http://smdn.jp/programming/tips/polish/#Implementation_C)

現状本文の方も変更を考えている部分があるので、それと合わせて修正後に
また改めてお知らせいたします。
(おそらく来年2018年1月中旬〜下旬ごろになると思います)

なお、以下は頂いた部分についての現時点での修正対応予定です。

概ね、コードの簡略化・読みやすさを優先とし、恐縮ながら速度の向上が得られる
のみとなる修正はしない、という方針に基づく判断となります。

  • 採用の方向
    • size_t型に-1? (char*よりはintへの変更で修正予定)
    • priority_currentの初期値をINT_MAXに (なぜ今までそうしなかったのか…)
    • traverse系関数の出力に空白を追加 (前回変更は出力を変えない変更のみとしたため、今後本文と合わせて修正予定)
  • 採用に肯定的
    • remove_outer_most_bracketの先頭部の簡素化 (現行実装が冗長な感があったので、頂いた案を精査の上、改善)
    • remove_outer_most_bracketの再帰呼出し前の必要性チェック (上をそのまま採用するなら、これも同様に)
    • returnで抜けるifの分離とelse部の展開 (読みやすさを見て決定)
  • 採用に消極的
    • parse_expressionでのmemset,strncpy (C言語レベルでもleftとrightで処理に相違がないようにしたい、C言語レベルでの差異を理由に意味的にも何か違いがあるのかという疑問が生じないようにしたい)
    • calculate内で子ノードのポインタにNULL (処理上は冗長だが、子ノードを持たない状態にすることを明示しておきたい。 また、今後の拡張として計算過程のツリーを表示する場合を考えていたので、そのままのしておきたい。)
    • コメント文 (サンプルコードの説明ではコードの意図と目的を明確にしたく、それに従い体現止めの採用には消極的、ただ文体の不一致修正はする)

実用ではなくあくまで理解のためのサンプルコードという位置づけのため、また扱う文字列長も
高々キロバイト単位のため、実用や高速化を目的とした改善はコードの利用者各自で
行ってもらいたく思っています。

速度を意識した改善は行いたくはありますが、あくまでサンプルコードとしての位置づけでの公開ですし、
また実用を考慮しだすと速度以外にも変更したい部分は多々あります。
例えば、演算子も*や/ではなく×や÷を使えるようにしたい、その他記号等も使用したいと考えると
char配列ではなく非ASCIIに対応する文字列型を使うべき。 あるいは計算では浮動小数点ではなく
固定小数点を使うべき、など。 これらに対応するとコード量が増えてサンプルとしては長大になるため、
ここで扱うべき範囲を超えるとして省略しています。

もちろん、memsetやstrlenに関する点は実用を主眼とした場合には有用ですので、今後コードを書く際の
参考にしたいと思います。

いずれにせよ、改善に繋がるご意見は大変ありがたいので、他にももし改良点がありましたら
気兼ねなく書き込んでいただければと思います。
あるいは、改善点を修正したコード全文をこちらに貼っていいただければ、本文に掲載する
コードに直接反映しないにしても、本文中にて紹介という形にさせていただくこともできます。

それでは良いお年を。

: ID:+LyRQS9z

>>48
大変遅くなりましたが、頂いたご指摘をもとに修正した内容を先ほど公開いたしました。
変更した箇所としなかった箇所は下記のとおりとなります。
パフォーマンス関連での修正にはご満足いただけない部分があるかとは思いますが、ご確認いただければと思います。

変更した箇所

size_t型に-1
indexはintに、lengthのみsize_tを使用するように変更
priority_currentの初期値をINT_MAXに
INT_MAXを使用するように変更(他言語も同様に整数の最大に相当する定数値に置き換え)
traverse系関数の出力に空白を追加
すべての記法で項および演算子の間に空白を入れるように変更
前置記法・後置記法では末尾にも空白が出てしまうが、実装の簡略化のため割り切り
remove_outer_most_bracketの先頭部の簡素化
括弧の対応の検証は、新たに関数validate_bracket_balanceを用意して分離し、parseする前に一度だけ呼び出すように変更
式中の文字の検証と同時にlenを計上することにより、strlenの呼び出しを削除
冒頭のif-else ifブロックは変更せず(以下理由)

以下のようにすればi=0からスキャンになり冒頭の見かけ上の冗長さはなくなるが、逆に開き括弧が出現するたびにif (0 == i)が評価されることになり、最終的な判断としては変更なしに

        for (i = 0, len = 0; exp[i]; i++, len++) {
            if ('(' == exp[i]) {
                // 開き丸括弧なので深度を1増やす
                nest++;

                // 0文字目が開き丸括弧の場合、最も外側に丸括弧があると仮定する
                if (0 == i)
                    has_outer_most_bracket = 1;
            }
remove_outer_most_bracketの再帰呼出し前の必要性チェック
括弧を削除したあと、さらに外側に括弧が残る場合のみ再起呼び出しするように変更
returnで抜けるifの分離とelse部の展開
if内でreturnする場合はifを単離し、else部は展開
parse_expressionではreturnする前に不要な処理を行わないよう評価順序も変更

変更しなかった箇所

has_outer_most_bracketの値にtrue/false
関数の成功/失敗をintで統一している、またMSVCでのstdbool.hの扱いに難あり?
検証の上使用するにしても結局ここだけなので、現状維持
parse_expressionのmemset
先にmemsetするべきと考える文化に従うならここ(あるいはcreate_node)でやるのが適当、それを冗長と考えるなら\0で終端するのが適当、そのどちらに寄せるかの判断はここでは留保とし、現状維持
(パフォーマンスを再優先にした改善や、より高度な実用を目指した変更は、やはり目的に応じて個々でやってもらいたいし、それができるようMITライセンスを設定している)
parse_expressionのrightに対するstrncpyはstrcpyにできる
leftとrightはC言語レベルでも差異が出ないようにしておきたい
三項以上の演算子や関数に対応する場合など(cond ? cond-true : cond-false, func(arg1, arg2, arg3...))を考えると、二分木の場合に特化した最適化の感もある
calculateでの子ノードへのポインタにNULL設定は省略できる
冗長といっても単純代入、再帰呼出しやstrlen等に比べればコストは軽微と考え、意味上の明確さを優先のため現状維持

変更箇所の差分を次の投稿にて別途掲載しますので合わせてご覧ください。 また、上記以外(コード以外)の変更点については下記の更新情報をご参照ください。
http://smdn.jp/programming/tips/polish/#DocumentLog

改めて、ご指摘をいただきありがとうございました。

: ID:+LyRQS9z

>>50
以下、変更箇所の差分です。

diff --git a/contents/programming/tips/polish/_source/polish.c b/contents/programming/tips/polish/_source/polish.c
index abd4b22..ef61b7a 100644
--- a/contents/programming/tips/polish/_source/polish.c
+++ b/contents/programming/tips/polish/_source/polish.c
@@ -2,6 +2,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
 typedef struct Node Node;
 
@@ -32,24 +33,19 @@ Node* create_node()
 // (成功した場合は0、エラーの場合は-1を返す)
 int remove_outer_most_bracket(char *exp)
 {
-    size_t i;
-    size_t len = strlen(exp);
+    int i;
+    size_t len;
     int has_outer_most_bracket = 0; // 最も外側に括弧を持つかどうか(0=持たない、1=持つ)
-    int nest = 0;
+    int nest = 0; // 丸括弧の深度(式中で開かれた括弧が閉じられたかどうか調べるために用いる)
 
     if ('(' == exp[0]) {
         // 0文字目が開き丸括弧の場合、最も外側に丸括弧があると仮定する
-        nest = 1;
         has_outer_most_bracket = 1;
-    }
-    else if (')' == exp[0]) {
-        // 0文字目が閉じ丸括弧の場合、エラーとする
-        fprintf(stderr, "unbalanced bracket: %s\n", exp);
-        return -1;
+        nest = 1;
     }
 
     // 1文字目以降を1文字ずつ検証
-    for (i = 1; i < len; i++) {
+    for (i = 1, len = 1; exp[i]; i++, len++) {
         if ('(' == exp[i]) {
             // 開き丸括弧なので深度を1増やす
             nest++;
@@ -58,51 +54,48 @@ int remove_outer_most_bracket(char *exp)
             // 閉じ丸括弧なので深度を1減らす
             nest--;
 
-            // 最後の文字以外で閉じ丸括弧が現れた場合、最も外側には丸括弧がないと判断する
-            if (i < len - 1 && 0 == nest)
+            // 最後の文字以外で開き丸括弧がすべて閉じられた場合、最も外側には丸括弧がないと判断する
+            // 例:"(1+2)+(3+4)"などの場合
+            if (0 == nest && exp[i + 1]) {
                 has_outer_most_bracket = 0;
+                break;
             }
         }
-
-    // 括弧の深度が0以外の場合
-    if (0 != nest) {
-        // 開かれていない/閉じられていない括弧があるので、エラーとする
-        fprintf(stderr, "unbalanced bracket: %s\n", exp);
-        return -1;
     }
-    // 最も外側に丸括弧がある場合
-    else if (0 != has_outer_most_bracket) {
-      if (len <= 2) {
+
+    // 最も外側に丸括弧がない場合は、何もしない
+    if (0 == has_outer_most_bracket)
+        return 0;
+
     // 文字列の長さが2未満の場合は、つまり空の丸括弧"()"なのでエラーとする
+    if (len <= 2) {
         fprintf(stderr, "empty bracket: %s\n", exp);
         return -1;
     }
-      else {
-        // 最初と最後の文字を取り除く
+
+    // 最初と最後の文字を取り除く(最も外側の丸括弧を取り除く)
     for (i = 0; i < len - 2; i++) {
         exp[i] = exp[i + 1];
     }
     exp[i] = '\0';
 
-        // 再帰的に呼び出す
-        // "((1+2))"など、多重になっている括弧を取り除くため再帰的に呼び出す
+    // 取り除いた後の文字列の最も外側に括弧が残っている場合
+    // 例:"((1+2))"などの場合
+    if ('(' == exp[0] && ')' == exp[i - 1])
+        // 再帰的に呼び出して取り除く
         return remove_outer_most_bracket(exp);
-      }
-    }
-    // 最も外側に丸括弧がない場合
-    else {
-        // 何もしない
+    else
+        // そうでない場合は処理を終える
         return 0;
-    }
 }
 
-// 式expから最も優先順位が低い演算子を探して位置を返す関数
+// 式expから最も右側にあり、かつ優先順位が低い演算子を探して位置を返す関数
 // (演算子がない場合は-1を返す)
-size_t get_pos_operator(char *exp)
+int get_pos_operator(char *exp)
 {
-    size_t i;
-    size_t pos_operator = -1; // 現在見つかっている演算子の位置(初期値として-1=演算子なしを設定)
-    int priority_current = 4; // 現在見つかっている演算子の優先順位(初期値として4=最高(3)+1を設定)
+    int i;
+    int pos_operator = -1; // 現在見つかっている演算子の位置(初期値として-1=演算子なしを設定)
+    int priority_current = INT_MAX; // 現在見つかっている演算子の優先順位(初期値としてINT_MAXを設定)
     int nest = 0; // 丸括弧の深度(括弧でくくられていない部分の演算子を「最も優先順位が低い」と判断するために用いる)
     int priority;
 
@@ -127,6 +120,7 @@ size_t get_pos_operator(char *exp)
 
         // 括弧の深度が0(丸括弧でくくられていない部分)かつ、
         // 現在見つかっている演算子よりも優先順位が同じか低い場合
+        // (優先順位が同じ場合は、より右側に同じ優先順位の演算子があることになる)
         if (0 == nest && priority <= priority_current) {
           // 最も優先順位が低い演算子とみなし、その位置を保存する
           priority_current = priority;
@@ -142,8 +136,8 @@ size_t get_pos_operator(char *exp)
 // (成功した場合は0、エラーの場合は-1を返す)
 int parse_expression(Node* node)
 {
-    size_t pos_operator;
-    size_t len_exp;
+    int pos_operator;
+    size_t len;
 
     if (!node)
         return -1;
@@ -152,26 +146,28 @@ int parse_expression(Node* node)
     if (remove_outer_most_bracket(node->exp) < 0)
         return -1;
 
-    len_exp = strlen(node->exp);
-
     // 式expから演算子を探して位置を取得する
     pos_operator = get_pos_operator(node->exp);
 
-    if ((size_t)0 == pos_operator || (size_t)(len_exp - 1) == pos_operator) {
-      // 演算子の位置が式の先頭または末尾の場合は不正な式とする
-        fprintf(stderr, "invalid expression: %s\n", node->exp);
-        return -1;
-    }
-    else if ((size_t)-1 == pos_operator) {
-        // 式Expressionに演算子が含まれない場合、Expressionは項であるとみなす
+    if (-1 == pos_operator) {
+        // 式expに演算子が含まれない場合、expは項であるとみなす
         // (左右に子ノードを持たないノードとする)
         node->left  = NULL;
         node->right = NULL;
-
         return 0;
     }
-    else {
-        // 左側・右側のノードを作成
+
+    len = strlen(node->exp);
+
+    if (0 == pos_operator || (len - 1) == pos_operator) {
+      // 演算子の位置が式の先頭または末尾の場合は不正な式とする
+        fprintf(stderr, "invalid expression: %s\n", node->exp);
+        return -1;
+    }
+
+    // 以下、演算子の位置をもとに左右の部分式に分割する
+
+    // 左側・右側のノードを作成する
     node->left   = create_node();
     node->right  = create_node();
 
@@ -181,7 +177,7 @@ int parse_expression(Node* node)
         return -1;
     }
 
-        // 演算子の左側を左の部分式としてノードを構成
+    // 演算子の左側を左の部分式としてノードを構成する
     memset(node->left->exp, 0, MAX_EXP_LEN);
     strncpy(node->left->exp, node->exp, pos_operator);
 
@@ -189,9 +185,9 @@ int parse_expression(Node* node)
     if (parse_expression(node->left) < 0)
         return -1;
 
-        // 演算子の右側を右の部分式としてノードを構成
+    // 演算子の右側を右の部分式としてノードを構成する
     memset(node->right->exp, 0, MAX_EXP_LEN);
-        strncpy(node->right->exp, node->exp + pos_operator + 1, len_exp - pos_operator);
+    strncpy(node->right->exp, node->exp + pos_operator + 1, len - pos_operator);
 
     // 右側のノード(部分式)について、再帰的に二分木へと分割する
     if (parse_expression(node->right) < 0)
@@ -202,7 +198,6 @@ int parse_expression(Node* node)
     node->exp[1] = '\0';
 
     return 0;
-    }
 }
 
 // 後行順序訪問(帰りがけ順)で二分木を巡回して
@@ -216,7 +211,8 @@ void traverse_postorder(Node* node)
         traverse_postorder(node->right);
 
     // 巡回を終えた後でノードの演算子または項を表示する
-    printf("%s", node->exp);
+    // (読みやすさのために項の後に空白を補って表示する)
+    printf("%s ", node->exp);
 }
 
 // 中間順序訪問(通りがけ順)で二分木を巡回して
@@ -228,27 +224,36 @@ void traverse_inorder(Node* node)
         printf("(");
 
     // 表示する前に左の子ノードを再帰的に巡回する
-    if (node->left)
+    if (node->left) {
         traverse_inorder(node->left);
 
+        // 読みやすさのために空白を補う
+        printf(" ");
+    }
+
     // 左の子ノードの巡回を終えた後でノードの演算子または項を表示する
     printf("%s", node->exp);
 
     // 表示した後に右の子ノードを再帰的に巡回する
-    if (node->right)
+    if (node->right) {
+        // 読みやすさのために空白を補う
+        printf(" ");
+
         traverse_inorder(node->right);
+    }
 
     // 左右に項を持つ場合、読みやすさのために項の後に閉じ括弧を補う
     if (node->left && node->right)
         printf(")");
 }
 
-  // 先行順序訪問(行きがけ順)で二分木を巡回して
-  // すべてのノードの演算子または項を表示する関数
+// 先行順序訪問(行きがけ順)で二分木を巡回して
+// すべてのノードの演算子または項を表示する関数
 void traverse_preorder(Node* node)
 {
     // 巡回を始める前にノードの演算子または項を表示する
-    printf("%s", node->exp);
+    // (読みやすさのために項の後に空白を補って表示する)
+    printf("%s ", node->exp);
 
     // 左右に子ノードをもつ場合、表示した後にノードを再帰的に巡回する
     if (node->left)
@@ -317,6 +322,40 @@ void remove_space(char *exp)
     *dst = '\0';
 }
 
+// 式exp内の括弧の対応を検証する関数
+// 開き括弧と閉じ括弧が同数でない場合はエラーとして0以外、同数の場合は0を返す
+int validate_bracket_balance(char *exp)
+{
+    int i;
+    int nest = 0; // 丸括弧の深度(くくられる括弧の数を計上するために用いる)
+
+    // 1文字ずつ検証する
+    for (i = 0; exp[i]; i++) {
+        if ('(' == exp[i]) {
+            // 開き丸括弧なので深度を1増やす
+            nest++;
+        }
+        else if (')' == exp[i]) {
+            // 閉じ丸括弧なので深度を1減らす
+            nest--;
+
+            // 深度が負になった場合
+            if (nest < 0)
+                // 式中で開かれた括弧よりも閉じ括弧が多いため、その時点でエラーとする
+                // 例:"(1+2))"などの場合
+                break;
+        }
+    }
+
+    // 深度が0でない場合
+    if (0 != nest)
+        // 式中に開かれていない/閉じられていない括弧があるので、エラーとする
+        // 例:"((1+2)"などの場合
+        fprintf(stderr, "unbalanced bracket: %s\n", exp);
+
+    return nest;
+}
+
 int main()
 {
     // 二分木の根(root)ノードを作成する
@@ -329,23 +368,27 @@ int main()
     // 入力された式から空白を除去する
     remove_space(root->exp);
 
+    // 入力された式における括弧の対応数をチェックする
+    if (0 != validate_bracket_balance(root->exp))
+        return -1;
+
     printf("expression: %s\n", root->exp);
 
     // 根ノードに格納した式を二分木へと分割する
     if (parse_expression(root) < 0)
         return -1;
 
-    // 分割した二分木を帰りがけ順で巡回して表示(前置記法/逆ポーランド記法で表示される)
+    // 分割した二分木を帰りがけ順で巡回して表示する(前置記法/逆ポーランド記法で表示される)
     printf("reverse polish notation: ");
     traverse_postorder(root);
     printf("\n");
 
-    // 分割した二分木を通りがけ順で巡回して表示(中置記法で表示される)
+    // 分割した二分木を通りがけ順で巡回して表示する(中置記法で表示される)
     printf("infix notation: ");
     traverse_inorder(root);
     printf("\n");
 
-    // 分割した二分木を行きがけ順で巡回して表示(後置記法/ポーランド記法で表示される)
+    // 分割した二分木を行きがけ順で巡回して表示する(後置記法/ポーランド記法で表示される)
     printf("polish notation: ");
     traverse_preorder(root);
     printf("\n");
: ID:JffJ8cfs

はじめまして。
プログラムの参考に閲覧しました。
プログラムに疑問がわいたのでこちらに失礼します。

Programming / Tips / 二分木を使った数式の逆ポーランド記法化と計算
にある最後のC言語でのコードについて。
入力計算式を
1 (10 * 10) / 10
などとカッコの前後に演算子のない数値を記入したとき。
計算成功,になりますが,計算結果はでたらめです。
本来は計算成功ではないと思われますが,これは仕様なのでしょうか。

初歩的な質問で,失礼しました。

: ID:5U/9Z0Js

>>52
ご質問ありがとうございます。

制限事項としても明記していますが、例示いただいた式のような
「暗黙の乗算を含む部分式に関する動作は未定義」となります。
ですので、ご指摘のとおり本来なら計算成功ではありませんが、
現状の動作で仕様通りということになります。

現時点での実装では、こういった式をエラーとするような処理は
実装していないため、エラー報告はされずに処理が継続し、
最終的には計算成功扱いとなるという動作になっています。

また、式"2(1+2)"は"2*(1+2)"のように暗黙の乗算を含む式とは解釈されず、
計算できない単一の項"2(1+2)"として扱われそのまま出力されるため、
計算成功扱いにはなりますが期待するような結果は出力されないという動作になります。

  • 書き込み完了後に投稿内容を編集することは出来ません。
  • >>1と入力すると1番へのアンカーになります。
  • 投稿内容はPukiWiki記法で整形されます。
    • 以下のPukiWiki記法が使えます。
      • 引用文
      • 番号付きリスト、番号なしリスト、定義リスト
      • 整形済みテキスト
        • 複数行のコードブロックを書き込むには次のように記入してください。
          #code{{
          int x = 2;
          int y = 3;
          }}
        • 複数行のコマンド出力を書き込むには次のように記入してください。
          #prompt{{
          C:\> echo "foo"
          "foo"
          }}
      • 表組み
      • 見出し
      • 強調・斜体、取り消し線・下線
      • 文字色(&color)、文字サイズ(&size)
      • 注釈
    • URL・メールアドレスは自動的にリンクになります。
    • 詳しくはPukiWikiのFormattingRulesを参照してください。