NHSP 言語リファレンス

コンパイラ仕様 / 文法定義 / C# 対応表

コンパイラ仕様

項目
コンパイラnhspc.exe (コンソール) / NhspCompiler.Core.dll (ライブラリ)
実装言語C# (.NET Framework 4.8)
ターゲット.NET Framework 4.8 アセンブリ (IL)
出力形式DLL (クラスライブラリ) / EXE (コンソール / Windows GUI)
ターゲットアーキAnyCPU (既定) / x86 / x64 / AnyCPU 32-bit preferred — -platform オプション
IL 生成System.Reflection.Emit (AssemblyBuilder / TypeBuilder / ILGenerator)
デバッグ情報PDB (SequencePoint / DebuggableAttribute) — -debug オプション
Win32 リソースVERSIONINFO (アセンブリメタデータディレクティブから自動生成) / RT_GROUP_ICON+RT_ICON / RT_MANIFEST
ソース拡張子.nhsp

コンパイルパイプライン

Source (.nhsp) → Lexer → Parser → AST → Semantic → IL Emit → Assembly (.dll/.exe)

構文の基本ルール

機能構文説明
1行1文x = 42改行が文の区切り (セミコロン不要)
コロン区切りa = 1 : b = 2HSP と同様、: で1行に複数文を記述可能
行継続a + b + \
  c + d
HSP と同様、\ + 改行で次の行に続く
dim 省略int x = 42dim int x = 42 と同等。C# 風の変数宣言
コメント; コメント / // コメントHSP 形式と C 形式の両方に対応
16進数$FF / 0xFFHSP 形式と C 形式の両方に対応
null リテラルnull / nullptrC# 形式と C++ 形式の両方に対応 (同じ動作)

コロン区切りと行継続の例

NHSP

; コロン区切り: 1行に複数文
int a = 1 : int b = 2 : int c = a + b

; 行継続: 長い行を分割
#dllfunc public static int MessageBoxW \
  "MessageBoxW", \
  IntPtr hWnd, string text, \
  string caption, int type

; 式の途中でも分割可能
int result = 1 + 2 + \
  3 + 4

C#

// C# は ; で文を区切る
int a = 1; int b = 2; int c = a + b;

// C# は改行を無視するので行継続不要
[DllImport("user32.dll")]
static extern int MessageBoxW(
  IntPtr hWnd, string text,
  string caption, int type);

int result = 1 + 2 +
  3 + 4;
HSP との違い: HSP ではコメントに ; を使いますが、NHSP でも同じです。: は HSP と同じく文の区切りです。\ による行継続も HSP 互換です。
C# との違い: C# では文末の ; が必須ですが、NHSP では不要です (改行が区切り)。

ディレクティブ一覧

ディレクティブ説明C# 相当
#assembly "Name" [, exe]出力アセンブリ名・種別[assembly: AssemblyTitle("Name")]
#reference "Lib.dll"参照アセンブリ追加csc /r:Lib.dll
#namespace "NS"名前空間開始namespace NS {
#endnamespace名前空間終了}
#class [修飾子] Name [: Base, IFace]クラス定義開始public class Name : Base, IFace {
#endclassクラス定義終了}
#interface Nameインターフェース定義public interface Name {
#endinterfaceインターフェース終了}
#func [修飾子] [戻型] Name [, params]メソッド定義public RetType Name(params) {
#endfuncメソッド終了}
#init [params]コンストラクタpublic Name(params) {
#endinitコンストラクタ終了}
#field [修飾子] Type Nameフィールドpublic Type Name;
#property Name as Typeプロパティpublic Type Name { get; }
#main / #endmainエントリポイントstatic void Main() {
#dllimport "dll名"DLL 宣言 (以降の #dllfunc に適用)[DllImport("dll名")]
#dllfunc [修飾子] [戻型] Name [, params]P/Invoke 関数宣言 (本体なし)static extern RetType Name(params);
#struct [修飾子] Name [, Layout] [, Pack=N] [, Size=N] [, CharSet=X]構造体 (値型) 定義[StructLayout(...)] public struct Name {
#endstruct構造体終了}
#delegate [修飾子] [戻型] Name [, params]デリゲート型定義public delegate RetType Name(params);
#enum Name ... #endenum列挙型定義public enum Name { ... }
#include "file.nhsp"ファイルのインクルードC# には直接相当なし (partial class 等)
#event [修飾子] DelegateType Nameイベント定義public event EventHandler Name;
#indexer [修飾子] Type [, params]インデクサ定義public Type this[params] { }
#operator RetType Op, params演算子オーバーロードpublic static RetType operator Op(params)
#init static ... #endinit静的コンストラクタstatic ClassName() { }
#destructor ... #enddestructorデストラクタ (ファイナライザ)~ClassName() { }
#attribute Name, "value"カスタム属性付与[Name("value")]
アセンブリメタデータ / Win32 リソース
#version "1.2.3.4"AssemblyVersion (CLR バージョン)[assembly: AssemblyVersion("1.2.3.4")]
#fileversion "1.2.3.4"AssemblyFileVersion (Win32 VERSIONINFO の FileVersion)[assembly: AssemblyFileVersion("1.2.3.4")]
#infoversion "1.2.3-rc1"AssemblyInformationalVersion (Win32 VERSIONINFO の ProductVersion)[assembly: AssemblyInformationalVersion("1.2.3-rc1")]
#title "App Name"AssemblyTitle (FileDescription)[assembly: AssemblyTitle("App Name")]
#description "..."AssemblyDescription[assembly: AssemblyDescription("...")]
#company "ACME"AssemblyCompany (CompanyName)[assembly: AssemblyCompany("ACME")]
#product "MyApp"AssemblyProduct (ProductName)[assembly: AssemblyProduct("MyApp")]
#copyright "(c) 2026"AssemblyCopyright (LegalCopyright)[assembly: AssemblyCopyright("(c) 2026")]
#trademark "..."AssemblyTrademark (LegalTrademarks)[assembly: AssemblyTrademark("...")]
#icon "logo.png"Win32 アプリケーションアイコン (ico/png/bmp/gif/jpg/jpeg/tif/tiff)csc /win32icon:logo.ico
#manifest "app.manifest"Win32 マニフェスト埋め込み (RT_MANIFEST)csc /win32manifest:app.manifest

メタデータディレクティブを 1 つでも書くと nhspc が Win32 VERSIONINFO リソースを自動生成します。 エクスプローラーで EXE/DLL を右クリック → プロパティ → 詳細タブに表示される情報になります。 #icon.ico をそのまま埋め込むか、PNG/BMP/GIF/JPEG/TIFF を渡した場合は内部で 16/32/48/256 サイズの PNG エントリを持つ Vista+ 形式 .ico に自動変換します (要 GDI+)。

型システム

NHSP.NET (CLR)サイズリテラル例
intSystem.Int324 byte42, -1
int64 / longSystem.Int648 byte
doubleSystem.Double8 byte3.14
floatSystem.Single4 byte
stringSystem.String参照"Hello"
boolSystem.Boolean1 bytetrue, false
byteSystem.Byte1 byte
sbyteSystem.SByte1 byte
shortSystem.Int162 byte
ushortSystem.UInt162 byte
uintSystem.UInt324 byte
ulongSystem.UInt648 byte
charSystem.Char2 byte
IntPtrSystem.IntPtr4/8 byteポインタサイズ
UIntPtrSystem.UIntPtr4/8 byteポインタサイズ
object / varSystem.Object参照

変数宣言と型の使い方

NHSP

; dim を使った宣言 (NHSP 従来形式)
dim int x = 42
dim string name = "Hello"
dim double pi = 3.14

; dim 省略形 (C# 風 — 型名から始める)
int x2 = 100
string msg = "World"
double rate = 0.5
bool flag = true

; 型推論 (代入値から自動判定)
dim count = 100        ; int
dim text = "abc"       ; string

; 配列
dim int[] arr, 10      ; int 配列 (要素数 10)
string[] names, 5      ; dim 省略も OK

; ジェネリクス型
List<int> nums = new List<int>()

; Nullable
int? maybe = 42        ; null も入れられる

; 16進数リテラル
int hex1 = 0xFF        ; C 形式
int hex2 = $FF         ; HSP 形式 (同じ値)

; null リテラル (C# / C++ どちらの書き方も OK)
string s1 = null       ; C# 形式
string s2 = nullptr    ; C++ 形式 (同じ動作)
string safe = s1 ?? "default"  ; null なら "default"

C#

// C# の変数宣言
int x = 42;
string name = "Hello";
double pi = 3.14;

int x2 = 100;
string msg = "World";
double rate = 0.5;
bool flag = true;

var count = 100;
var text = "abc";

int[] arr = new int[10];
string[] names = new string[5];

List<int> nums = new List<int>();

int? maybe = 42;

int hex1 = 0xFF;

string s1 = null;
// nullptr は C# にはない (NHSP独自)
string safe = s1 ?? "default";
dim は省略可能。int x = 42dim int x = 42 は同じ意味。
C# 経験者は dim なし、HSP ユーザーは dim 付きが馴染みやすい。どちらの書き方も混在可能。
.NET の型名 (StringBuilder, StreamReader 等) もフルネーム不要で使用可能 (主要な名前空間を自動検索)。
型推論: x = 42ints = "text"string と推論。dim で明示指定も可能。

クラス定義

NHSP

#class public Person
  #field public string Name
  #field private int _age

  #init string name, int age
    Name = name
    _age = age
  #endinit

  #func public string GetInfo
    return Name + " (" + str(_age) + ")"
  #endfunc
#endclass

C#

public class Person
{
    public string Name;
    private int _age;

    public Person(string name, int age)
    {
        Name = name;
        _age = age;
    }

    public string GetInfo()
    {
        return Name + " (" + _age + ")";
    }
}

構造体 (値型)

#struct で値型 (struct) を定義できます。P/Invoke でネイティブ構造体を渡す際に必要です。StructLayoutAttribute の全パラメータに対応。

構文

#struct [修飾子] 名前 [, LayoutKind] [, Pack = N] [, Size = N] [, CharSet = X]
  #field [修飾子] 型 フィールド名
  ...
#endstruct

StructLayout パラメータ

パラメータ説明
LayoutKindSequential (既定), Explicit, Autoフィールドのメモリ配置方式
Pack1, 2, 4, 8, 16, 32, 64, 128パッキングアラインメント (バイト単位)
Size整数構造体の明示的な合計サイズ (バイト)
CharSetAnsi, Unicode, Auto, None文字列マーシャリングの文字セット

例: SYSTEMTIME 構造体 (Pack 指定)

NHSP

#struct public SYSTEMTIME, Sequential, Pack = 2
  #field public ushort wYear
  #field public ushort wMonth
  #field public ushort wDayOfWeek
  #field public ushort wDay
  #field public ushort wHour
  #field public ushort wMinute
  #field public ushort wSecond
  #field public ushort wMilliseconds
#endstruct

C#

[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct SYSTEMTIME
{
    public ushort wYear;
    public ushort wMonth;
    public ushort wDayOfWeek;
    public ushort wDay;
    public ushort wHour;
    public ushort wMinute;
    public ushort wSecond;
    public ushort wMilliseconds;
}

例: 共用体 (Explicit Layout + FieldOffset)

NHSP

#struct public Union, Explicit
  [FieldOffset 0] #field public int IntValue
  [FieldOffset 0] #field public float FloatValue
#endstruct

C#

[StructLayout(LayoutKind.Explicit)]
public struct Union
{
    [FieldOffset(0)] public int IntValue;
    [FieldOffset(0)] public float FloatValue;
}

例: 文字列フィールドの MarshalAs

#struct public WinStruct, Sequential, CharSet = Unicode
  #field public int Id
  [MarshalAs ByValTStr, 256] #field public string Name
#endstruct
構造体はクラスと異なり System.ValueType を継承する値型です。スタック上に配置され、代入時にコピーされます。 P/Invoke で Win32 API にネイティブ構造体を渡す場合に使います。

デリゲート

#delegate でデリゲート型を定義できます。コールバック関数の型や P/Invoke の関数ポインタに使用します。

構文

#delegate [修飾子] [戻り値型] デリゲート名 [, 型 引数名, ...]

NHSP

; 戻り値 int、引数 2つの演算デリゲート
#delegate public int BinaryOp, int a, int b

; void デリゲート (イベントハンドラ)
#delegate public void EventHandler, object sender, string message

C#

public delegate int BinaryOp(int a, int b);

public delegate void EventHandler(
    object sender, string message);
デリゲートは MulticastDelegate を継承し、Invoke, BeginInvoke, EndInvoke メソッドが自動生成されます。

メソッド定義

構文

#func [修飾子...] [戻り値型] メソッド名 [, 型 引数名, 型 引数名 ...]
  ; 本体
#endfunc

修飾子

修飾子説明
public外部から呼び出し可能
privateクラス内部のみ
staticインスタンス不要
virtual派生クラスでオーバーライド可能
override基底クラスのメソッドをオーバーライド

NHSP

#func public static int Max, int a, int b
  if a > b { return a }
  return b
#endfunc

C#

public static int Max(int a, int b)
{
    if (a > b) return a;
    return b;
}

ref / out パラメータ

メソッドの引数に ref (参照渡し) や out (出力引数) を指定できます。呼び出し元の変数を直接変更します。

NHSP

; ref: 呼び出し元の変数を読み書き
#func public static void AddTen, ref int value
  value = value + 10
#endfunc

; out: 呼び出し元に値を返す
#func public static bool TryParse, string s, out int result
  result = 42
  return true
#endfunc

C#

public static void AddTen(ref int value)
{
    value = value + 10;
}

public static bool TryParse(
    string s, out int result)
{
    result = 42;
    return true;
}
ref は IL レベルで Type.MakeByRefType() + Ldarg/Ldind/Stind を使用。 outref と同じ ByRef 型に加えて ParameterAttributes.Out を設定。

フィールド・プロパティ

NHSP

#field public int Width
#field public int Height

#property Area as int, public
  #get
    return Width * Height
  #endget
#endproperty

C#

public int Width;
public int Height;

public int Area
{
    get { return Width * Height; }
}

フィールド修飾子

NHSPC#説明
#field public int Xpublic int X;通常のインスタンスフィールド
#field public static int Countpublic static int Count;静的フィールド (クラスに1つ)
#field public const int MAX = 260public const int MAX = 260;コンパイル時定数 (リテラル値が直接埋め込まれる)
#field public readonly int Idpublic readonly int Id;コンストラクタでのみ代入可能な読み取り専用フィールド

const / readonly の例

NHSP

#class public Config
  #field public const int MAX_PATH = 260
  #field public const string VERSION = "1.0"
  #field public readonly int MaxSize

  #init int size
    MaxSize = size
  #endinit
#endclass

C#

public class Config
{
    public const int MAX_PATH = 260;
    public const string VERSION = "1.0";
    public readonly int MaxSize;

    public Config(int size)
    {
        MaxSize = size;
    }
}
constFieldAttributes.Literal で IL レベルのコンパイル時定数。readonlyFieldAttributes.InitOnly でコンストラクタ内のみ代入可。

コンストラクタ

NHSP

; デフォルト (省略可)
#init
  X = 0
#endinit

; 引数付き
#init int x, int y
  X = x
  Y = y
#endinit

C#

public Point()
{
    X = 0;
}

public Point(int x, int y)
{
    X = x;
    Y = y;
}

継承・インターフェース

NHSP

#interface IShape
  #func int Area
#endinterface

#class public Circle : IShape
  #field public int Radius
  #init int r
    Radius = r
  #endinit
  #func public int Area
    return Radius * Radius * 3
  #endfunc
#endclass

C#

public interface IShape
{
    int Area();
}

public class Circle : IShape
{
    public int Radius;
    public Circle(int r) { Radius = r; }
    public int Area()
    {
        return Radius * Radius * 3;
    }
}

制御構文

NHSPC#備考
if cond { } else { }if (cond) { } else { }条件の括弧不要。===
repeat N ... loopfor(int cnt=0;cnt<N;cnt++){}cnt が自動カウンタ
for i = S to E [step N] ... nextfor(int i=S;i<=E;i+=N){}
while cond ... wendwhile(cond){}
foreach var in collection ... nextforeach(var x in col){}IEnumerable 対応
switch ... case ... endswitchswitch(x){ case N: }自動 break
breakbreak;
continuecontinue;

if / elseif / else

NHSP

; ブレース形式
if x > 10 {
  print "big"
} elseif x > 5 {
  print "medium"
} else {
  print "small"
}

; endif 形式 (HSP 互換)
if x = 1
  print "one"
elseif x = 2
  print "two"
else
  print "other"
endif

C#

if (x > 10)
{
    Console.WriteLine("big");
}
else if (x > 5)
{
    Console.WriteLine("medium");
}
else
{
    Console.WriteLine("small");
}

// HSP互換: = は == として扱われる
// (条件式中のみ)

repeat / loop (HSP 形式ループ)

NHSP

; 5回繰り返し (cnt は自動カウンタ)
repeat 5
  print "cnt = " + str(cnt)
loop
; 出力: cnt = 0, 1, 2, 3, 4

; 無限ループ
repeat
  if cnt > 99
    break
  endif
loop

C#

for (int cnt = 0; cnt < 5; cnt++)
{
    Console.WriteLine($"cnt = {cnt}");
}

// 無限ループ
for (int cnt = 0; ; cnt++)
{
    if (cnt > 99) break;
}

for / next

NHSP

; 1 から 10 まで
for i = 1 to 10
  print str(i)
next

; ステップ指定 (2 刻み)
for i = 0 to 100 step 2
  print str(i)
next

C#

for (int i = 1; i <= 10; i++)
{
    Console.WriteLine(i);
}

for (int i = 0; i <= 100; i += 2)
{
    Console.WriteLine(i);
}

while / wend

NHSP

dim int x = 0
while x < 10
  x++
wend
print str(x) ; => 10

C#

int x = 0;
while (x < 10)
{
    x++;
}
Console.WriteLine(x); // 10

例外処理

基本の try / catch / finally

NHSP

; 基本形
try
  dim int result = a / b
  return result
catch Exception ex
  return -1
finally
  print "cleanup"
#endtry

C#

try
{
    int result = a / b;
    return result;
}
catch (Exception ex)
{
    return -1;
}
finally
{
    Console.WriteLine("cleanup");
}
try/catch 内の return は自動的に IL の Leave 命令を使い、正しく動作します (Phase 17 で修正済み)。

複数 catch (型別の例外処理)

NHSP

try
  dim int result = int("abc")
catch FormatException e
  print "書式エラー: " + e.Message
catch OverflowException e2
  print "オーバーフロー: " + e2.Message
catch Exception e3
  print "その他: " + e3.Message
#endtry

C#

try
{
    int result = int.Parse("abc");
}
catch (FormatException e)
{
    Console.WriteLine("書式エラー: " + e.Message);
}
catch (OverflowException e2)
{
    Console.WriteLine("オーバーフロー: " + e2.Message);
}
catch (Exception e3)
{
    Console.WriteLine("その他: " + e3.Message);
}

throw (例外の送出)

NHSP

; 新しい例外を投げる
throw new ArgumentException("invalid value")

; catch 内で再送出
try
  ; ...
catch Exception e
  throw   ; 同じ例外をそのまま再送出
#endtry

C#

throw new ArgumentException("invalid value");

try { /* ... */ }
catch (Exception e)
{
    throw; // rethrow
}

スレッド・同期

System.Threading の API を利用してマルチスレッドプログラミングが可能です。

sleep (スレッド停止)

NHSP

; 100ミリ秒待機
sleep 100

C#

Thread.Sleep(100);

lock (排他制御)

NHSP

#class public Counter
  #field private int _count
  #field private object _lock

  #init
    _count = 0
    _lock = new object()
  #endinit

  #func public void Increment
    lock _lock
      _count += 1
    endlock
  #endfunc

  #func public int GetCount
    return _count
  #endfunc
#endclass

C#

public class Counter
{
    private int _count;
    private object _lock;

    public Counter()
    {
        _count = 0;
        _lock = new object();
    }

    public void Increment()
    {
        lock (_lock)
        {
            _count += 1;
        }
    }

    public int GetCount() => _count;
}
lock は IL レベルで Monitor.Enter / Monitor.Exittry/finally で囲んで生成。
ロック対象は参照型のオブジェクトが必要 (thisnew object() 等)。

Thread で並列実行

NHSP

; スレッドで並列に処理を実行
#assembly "ThreadDemo", exe
#reference "System.dll"

#class public Worker
  #func public static void DoWork
    print "Worker start"
    sleep 500
    print "Worker done"
  #endfunc
#endclass

#main
  ; ThreadStart デリゲートを作成して Thread に渡す
  dim ThreadStart ts = new ThreadStart(Worker.DoWork)
  dim Thread t = new Thread(ts)
  t.Start()
  print "Main thread"
  t.Join()
  print "All done"
#endmain

C#

using System.Threading;

class Worker
{
    public static void DoWork()
    {
        Console.WriteLine("Worker start");
        Thread.Sleep(500);
        Console.WriteLine("Worker done");
    }
}

class Program
{
    static void Main()
    {
        var t = new Thread(Worker.DoWork);
        t.Start();
        Console.WriteLine("Main thread");
        t.Join();
        Console.WriteLine("All done");
    }
}

Thread のプロパティ・バックグラウンドスレッド

NHSP

#class public ThreadExample
  #func public static void Run
    ; 現在のスレッド情報
    dim Thread current = Thread.CurrentThread
    print "ThreadId: " + str(current.ManagedThreadId)
    print "Name: " + current.Name
    print "IsBackground: " + str(current.IsBackground)

    ; バックグラウンドスレッド作成
    dim ThreadStart ts = new ThreadStart(Worker)
    dim Thread bg = new Thread(ts)
    bg.Name = "MyWorker"
    bg.IsBackground = true   ; メインスレッド終了時に自動停止
    bg.Priority = ThreadPriority.BelowNormal
    bg.Start()
    bg.Join()  ; 完了待ち
  #endfunc

  #func public static void Worker
    dim Thread me = Thread.CurrentThread
    print "Worker on: " + me.Name
    sleep 100
  #endfunc
#endclass

C#

class ThreadExample
{
    static void Run()
    {
        var current = Thread.CurrentThread;
        Console.WriteLine(
            $"ThreadId: {current.ManagedThreadId}");
        Console.WriteLine($"Name: {current.Name}");
        Console.WriteLine(
            $"IsBackground: {current.IsBackground}");

        var bg = new Thread(Worker);
        bg.Name = "MyWorker";
        bg.IsBackground = true;
        bg.Priority = ThreadPriority.BelowNormal;
        bg.Start();
        bg.Join();
    }

    static void Worker()
    {
        var me = Thread.CurrentThread;
        Console.WriteLine($"Worker on: {me.Name}");
        Thread.Sleep(100);
    }
}

ParameterizedThreadStart (引数付きスレッド)

NHSP

#class public ParamThread
  #func public static void PrintN, object arg
    dim int n = int(arg.ToString())
    repeat n
      print "Count: " + str(cnt)
      sleep 50
    loop
  #endfunc

  #func public static void Run
    dim ParameterizedThreadStart pts = new ParameterizedThreadStart(PrintN)
    dim Thread t1 = new Thread(pts)
    t1.Start(5)   ; 引数 5 を渡す

    dim Thread t2 = new Thread(pts)
    t2.Start(3)   ; 引数 3 を渡す

    t1.Join()
    t2.Join()
    print "Both done"
  #endfunc
#endclass

C#

class ParamThread
{
    static void PrintN(object arg)
    {
        int n = (int)arg;
        for (int i = 0; i < n; i++)
        {
            Console.WriteLine($"Count: {i}");
            Thread.Sleep(50);
        }
    }

    static void Run()
    {
        var t1 = new Thread(PrintN);
        t1.Start(5);

        var t2 = new Thread(PrintN);
        t2.Start(3);

        t1.Join();
        t2.Join();
        Console.WriteLine("Both done");
    }
}

ThreadPool (スレッドプール)

NHSP

#class public PoolExample
  #func public static void Work, object state
    dim string name = state.ToString()
    print "Start: " + name
    sleep 200
    print "End: " + name
  #endfunc

  #func public static void Run
    ; スレッドプールにジョブを投入
    dim WaitCallback cb = new WaitCallback(Work)
    ThreadPool.QueueUserWorkItem(cb, "Job1")
    ThreadPool.QueueUserWorkItem(cb, "Job2")
    ThreadPool.QueueUserWorkItem(cb, "Job3")

    ; 全ジョブ完了を待つ (簡易)
    sleep 1000
    print "All jobs done"
  #endfunc
#endclass

C#

class PoolExample
{
    static void Work(object state)
    {
        string name = (string)state;
        Console.WriteLine($"Start: {name}");
        Thread.Sleep(200);
        Console.WriteLine($"End: {name}");
    }

    static void Run()
    {
        ThreadPool.QueueUserWorkItem(Work, "Job1");
        ThreadPool.QueueUserWorkItem(Work, "Job2");
        ThreadPool.QueueUserWorkItem(Work, "Job3");

        Thread.Sleep(1000);
        Console.WriteLine("All jobs done");
    }
}

Task.Run で非同期タスク

NHSP

; Task.Run でバックグラウンド処理
#assembly "TaskDemo", exe
#reference "System.dll"

#class public Downloader
  #func public static string Fetch, string url
    ; 重い処理のシミュレーション
    sleep 1000
    return "Data from " + url
  #endfunc
#endclass

#main
  ; Task.Run で非同期実行
  dim Task task1 = Task.Run(new Action(Downloader.Fetch))

  print "Waiting..."
  task1.Wait()
  print "Done"
#endmain

C#

using System.Threading.Tasks;

class Downloader
{
    public static string Fetch(string url)
    {
        Thread.Sleep(1000);
        return "Data from " + url;
    }
}

class Program
{
    static void Main()
    {
        Task task1 = Task.Run(() =>
            Downloader.Fetch("http://example.com"));

        Console.WriteLine("Waiting...");
        task1.Wait();
        Console.WriteLine("Done");
    }
}
async/await は未対応のため、Task.Wait()Task.Result で同期的に待機する方法を使います。
ラムダ式も未対応のため、Task.Run にはデリゲート / メソッド参照を渡します。

Mutex でプロセス間排他

NHSP

; 二重起動防止
#assembly "MutexDemo", exe
#reference "System.dll"

#main
  dim bool createdNew
  dim Mutex mutex = new Mutex(true, "MyAppMutex")

  ; Mutex.WaitOne で取得を試みる
  if mutex.WaitOne(0)
    print "アプリ起動"
    sleep 3000
    mutex.ReleaseMutex()
  else
    print "既に起動済みです"
  endif
#endmain

C#

using System.Threading;

class Program
{
    static void Main()
    {
        bool createdNew;
        var mutex = new Mutex(true,
            "MyAppMutex", out createdNew);

        if (mutex.WaitOne(0))
        {
            Console.WriteLine("アプリ起動");
            Thread.Sleep(3000);
            mutex.ReleaseMutex();
        }
        else
        {
            Console.WriteLine("既に起動済みです");
        }
    }
}

スレッドセーフなコレクション操作

NHSP

; lock で List への追加を保護
#class public SafeList
  #field private List<int> _items
  #field private object _sync

  #init
    _items = new List<int>()
    _sync = new object()
  #endinit

  #func public void Add, int item
    lock _sync
      _items.Add(item)
    endlock
  #endfunc

  #func public int Count
    lock _sync
      return _items.Count
    endlock
    return 0
  #endfunc
#endclass

C#

public class SafeList
{
    private List<int> _items = new();
    private object _sync = new();

    public void Add(int item)
    {
        lock (_sync)
        {
            _items.Add(item);
        }
    }

    public int Count
    {
        get
        {
            lock (_sync)
            {
                return _items.Count;
            }
        }
    }
}

タイマー (Timer)

NHSP

; 1秒ごとにコールバック
#assembly "TimerDemo", exe
#reference "System.dll"

#class public App
  #func public static void OnTick, object state
    print "Tick: " + DateTime.Now.ToString("HH:mm:ss")
  #endfunc
#endclass

#main
  dim TimerCallback cb = new TimerCallback(App.OnTick)
  dim Timer timer = new Timer(cb, 0, 0, 1000)
  print "Timer started (5秒後に停止)"
  sleep 5000
  timer.Dispose()
  print "Timer stopped"
#endmain

C#

using System.Threading;

class App
{
    static void OnTick(object state)
    {
        Console.WriteLine(
            $"Tick: {DateTime.Now:HH:mm:ss}");
    }
}

class Program
{
    static void Main()
    {
        var timer = new Timer(
            App.OnTick, null, 0, 1000);
        Console.WriteLine(
            "Timer started (5秒後に停止)");
        Thread.Sleep(5000);
        timer.Dispose();
        Console.WriteLine("Timer stopped");
    }
}
System.Threading.Timer はスレッドプールで定期実行。System.Timers.Timer はイベントベースで、#event と組み合わせて使用可能。
コールバックにはデリゲート (TimerCallback, ThreadStart 等) を渡します。

P/Invoke (ネイティブ DLL 呼び出し)

#dllimport ディレクティブで Win32 API やネイティブ DLL の関数を直接呼び出せます。DefinePInvokeMethod で IL レベルの P/Invoke を生成。

NHSP

#class public NativeApi
  #dllimport "user32.dll"
  #dllfunc public static int MessageBoxA, int hWnd, string text, string caption, int type

  #dllimport "kernel32.dll"
  #dllfunc public static int GetTickCount
  #dllfunc public static int GetCurrentProcessId
#endclass

C#

public class NativeApi
{
    [DllImport("user32.dll")]
    public static extern int MessageBoxA(
        int hWnd, string text,
        string caption, int type);

    [DllImport("kernel32.dll")]
    public static extern int GetTickCount();

    [DllImport("kernel32.dll")]
    public static extern int GetCurrentProcessId();
}
#dllimport で DLL 名を宣言し、#dllfunc で関数を列挙します。#dllfunc は本体を持たず、#endfunc は不要です。同じ DLL の関数は #dllimport 1回で複数宣言可能。

#dllimport オプション

#dllimport には文字セットや呼び出し規約などのオプションを指定できます。

#dllimport "DLL名", CharSet = Unicode, SetLastError = true, CallingConvention = Cdecl, ExactSpelling = true
オプション既定値説明
CharSetAnsi, Unicode, Auto, NoneAuto文字列パラメータのマーシャリング方式
CallingConventionStdCall, Cdecl, ThisCall, FastCall, WinapiStdCall呼び出し規約 (C ランタイムは Cdecl)
SetLastErrortrue, falsefalseWin32 の SetLastError を保持する
ExactSpellingtrue, falsefalseA/W サフィックスの自動検索を無効化

例: Unicode + SetLastError

NHSP

#dllimport "user32.dll", CharSet = Unicode, SetLastError = true
#dllfunc public static int MessageBoxW, IntPtr hWnd, string text, string caption, int type

C#

[DllImport("user32.dll",
    CharSet = CharSet.Unicode,
    SetLastError = true)]
public static extern int MessageBoxW(
    IntPtr hWnd, string text,
    string caption, int type);

例: C ランタイム (Cdecl)

#dllimport "msvcrt.dll", CallingConvention = Cdecl
#dllfunc public static int puts, string str

ref / out パラメータと P/Invoke

NHSP

#dllimport "kernel32.dll"
#dllfunc public static void GetSystemTime, ref SYSTEMTIME st

C#

[DllImport("kernel32.dll")]
public static extern void GetSystemTime(
    ref SYSTEMTIME st);

EntryPoint 指定 (関数名の別名・序数)

メソッド名の後に文字列リテラルを書くと、DLL 内の実際のエクスポート名を指定できます。

NHSP

; 関数名の別名指定
#dllimport "user32.dll", CharSet = Unicode
#dllfunc public static int MsgBox "MessageBoxW", IntPtr hWnd, string text, string caption, int type

; 序数指定 (#123 形式)
#dllimport "mydll.dll"
#dllfunc public static int MyFunc "#123"

C#

[DllImport("user32.dll",
    CharSet = CharSet.Unicode,
    EntryPoint = "MessageBoxW")]
public static extern int MsgBox(
    IntPtr hWnd, string text,
    string caption, int type);

[DllImport("mydll.dll",
    EntryPoint = "#123")]
public static extern int MyFunc();
EntryPoint を省略するとメソッド名がそのまま使われます。序数指定 ("#123") は DLL のエクスポート番号で呼び出すレガシー機能です。
HSP の #func MessageBoxA "MessageBoxA" int, sptr, sptr, int と同じ考え方です。

is / as 演算子

実行時の型チェック (is) と安全なキャスト (as) をサポートします。

NHSP

; 型チェック
if obj is string
  print "It's a string"
endif

; 安全なキャスト (失敗時は null)
dim string s = obj as string
dim string result = s ?? "not a string"

C#

if (obj is string)
    Console.WriteLine("It's a string");

string s = obj as string;
string result = s ?? "not a string";
is は IL の Isinst + null チェック。asIsinst のみ (null を返す)。 参照型にのみ使用可能。値型の as は使用不可。

イベント

#event でイベントを定義します。内部的にデリゲートフィールド + add/remove メソッドが自動生成されます。

NHSP

#class public Button
  #event public EventHandler Click
#endclass

C#

public class Button
{
    public event EventHandler Click;
}
IL レベルでは DefineEvent + Delegate.Combine/Delegate.Remove パターンで add/remove メソッドを生成。 イベントの型はデリゲート型 (EventHandler, Action 等) を指定。

インデクサ

#indexer でインデクサ (添字アクセス) を定義します。C# の this[int index] に相当。

NHSP

#indexer public int, int index
  #get
    return index * 10
  #endget
  #set
    ; value でセットされる値を受け取る
  #endset
#endindexer

C#

public int this[int index]
{
    get { return index * 10; }
    set { /* value */ }
}
IL レベルでは DefineProperty("Item", ...) + get_Item/set_Item メソッドを生成。

演算子オーバーロード

#operator で演算子のオーバーロードを定義します。C# の operator + 等に相当。

構文

#operator 戻り値型 演算子記号, 型 引数名 [, 型 引数名]
  ; 本体
#endoperator

NHSP

#class public Vector
  #field public int X
  #field public int Y
  #operator Vector +, Vector a, Vector b
    dim Vector r = new Vector()
    r.X = a.X + b.X
    r.Y = a.Y + b.Y
    return r
  #endoperator
  #operator bool ==, Vector a, Vector b
    return a.X == b.X && a.Y == b.Y
  #endoperator
#endclass

C#

public class Vector {
    public int X, Y;
    public static Vector operator +(
        Vector a, Vector b)
    {
        return new Vector {
            X = a.X + b.X,
            Y = a.Y + b.Y };
    }
    public static bool operator ==(
        Vector a, Vector b)
    {
        return a.X == b.X &&
               a.Y == b.Y;
    }
}

対応する演算子

演算子CLR メソッド名パラメータ数
+op_Addition2
-op_Subtraction2
*op_Multiply2
/op_Division2
%op_Modulus2
==op_Equality2
!=op_Inequality2
< / >op_LessThan / op_GreaterThan2
implicitop_Implicit1 (変換元)
explicitop_Explicit1 (変換元)

ネストクラス

クラスの内部に別のクラスを定義できます。外部からは Outer+Inner (CLR) または Outer.Inner で参照。

NHSP

#class public Outer
  #class public Inner
    #func public static int Value
      return 42
    #endfunc
  #endclass
#endclass

C#

public class Outer
{
    public class Inner
    {
        public static int Value()
            => 42;
    }
}
IL レベルでは TypeBuilder.DefineNestedType で生成。TypeAttributes.NestedPublic が設定されます。

ジェネリクス

.NET の既存のジェネリクス型 (List<T>, Dictionary<K,V> 等) を利用できます。

型名の書き方

; ジェネリクス型の利用
dim List<int> nums = new List<int>()
dim Dictionary<string,string> map = new Dictionary<string,string>()
dim HashSet<int> set = new HashSet<int>()

対応するジェネリクス型

NHSP.NET 型
List<T>System.Collections.Generic.List<T>
Dictionary<K,V>System.Collections.Generic.Dictionary<K,V>
HashSet<T>System.Collections.Generic.HashSet<T>
Queue<T>System.Collections.Generic.Queue<T>
Stack<T>System.Collections.Generic.Stack<T>
Nullable<T>System.Nullable<T>
Action<T>System.Action<T>
Func<T,R>System.Func<T,TResult>
Task<T>System.Threading.Tasks.Task<T>
独自のジェネリクス型の定義 (#class public MyBox<T>) は現在未対応。.NET 標準ライブラリのジェネリクス型の利用のみサポート。
既知の制限: 一部のジェネリクス型操作でスタックオーバーフローが発生する場合があります。

static コンストラクタ

クラスが最初に使われるときに1回だけ実行される初期化コード。static フィールドの初期値設定に使います。

NHSP

#class public Config
  #field public static int DefaultValue

  #init static
    DefaultValue = 42
  #endinit
#endclass

C#

public class Config
{
    public static int DefaultValue;

    static Config()
    {
        DefaultValue = 42;
    }
}
IL レベルでは .cctor (TypeInitializer) として生成。MethodAttributes.Static | RTSpecialName | SpecialName
CLR がクラスへの最初のアクセス時に自動呼び出し。明示的に呼ぶことはできない。

デストラクタ

オブジェクトが GC に回収される前に実行されるクリーンアップコード。

NHSP

#class public Resource
  #destructor
    ; リソース解放処理
  #enddestructor
#endclass

C#

public class Resource
{
    ~Resource()
    {
        // リソース解放処理
    }
}
IL レベルでは Finalize() メソッドの override として生成。try { 本体 } finally { base.Finalize(); } パターン。
GC のタイミングに依存するため、確定的な解放には using 文 + IDisposable を推奨。

Nullable 型

値型に ? を付けて null を許容する型にできます。

NHSP

; int? は Nullable<int> と同等
#func public static bool HasValue, int? val
  return val.HasValue
#endfunc

; double? も使える
dim double? price = 19.99

C#

public static bool HasValue(int? val)
{
    return val.HasValue;
}

double? price = 19.99;
int? は内部的に Nullable<int>System.Nullable`1[System.Int32])に変換。
.HasValue / .Value プロパティ、?? 演算子と組み合わせて使用可能。
ジャグ配列 int[][] (配列の配列) もサポート。

値型のプロパティ・メソッドアクセス

DateTime などの値型 (struct) のプロパティやメソッドも正しく呼び出せます。

NHSP

; DateTime (値型) のプロパティ
dim DateTime now = DateTime.Now
int y = now.Year
int m = now.Month
int d = now.Day

; メソッド呼び出し
string text = now.ToString("yyyy-MM-dd")

; メソッドチェーン
string time = DateTime.Now.ToString("HH:mm:ss")

print $"Date: {str(y)}-{str(m)}-{str(d)}"
print $"Time: {time}"

C#

DateTime now = DateTime.Now;
int y = now.Year;
int m = now.Month;
int d = now.Day;

string text = now.ToString("yyyy-MM-dd");

string time = DateTime.Now.ToString("HH:mm:ss");

Console.WriteLine($"Date: {y}-{m}-{d}");
Console.WriteLine($"Time: {time}");
値型のメンバーアクセスは IL レベルで Ldloca (アドレス取得) + Call を使用。 メソッドチェーン (DateTime.Now.ToString()) は中間結果を一時変数に格納して処理。

MarshalAs 属性

P/Invoke のパラメータや構造体のフィールドに [MarshalAs] 属性を付けて、マネージド型とアンマネージド型の変換方式を指定できます。

パラメータへの指定

; フルネーム: [MarshalAs 型名]
#dllfunc public static int GetModuleFileName, IntPtr hModule, [MarshalAs LPStr] string buffer, int size

; ショートカット: [型名] だけでも MarshalAs として適用される
#dllfunc public static int GetModuleFileName, IntPtr hModule, [LPStr] string buffer, int size

フィールドへの指定

; 固定長文字列: [MarshalAs ByValTStr, サイズ]
[MarshalAs ByValTStr, 260] #field public string Path

; 固定長配列: [MarshalAs ByValArray, サイズ]
[MarshalAs ByValArray, 4] #field public byte Reserved

対応する UnmanagedType 一覧

名前UnmanagedType説明
BoolUnmanagedType.BoolWin32 BOOL (4 byte)
I1 / U1.I1 / .U1符号付/符号なし 1 byte
I2 / U2.I2 / .U2符号付/符号なし 2 byte
I4 / U4.I4 / .U4符号付/符号なし 4 byte
I8 / U8.I8 / .U8符号付/符号なし 8 byte
R4 / R8.R4 / .R8float / double
LPStr.LPStrANSI char* (null終端)
LPWStr.LPWStrUnicode wchar_t* (null終端)
LPTStr.LPTStrTCHAR* (プラットフォーム依存)
BStr.BStrCOM BSTR
ByValTStr.ByValTStr構造体内の固定長文字列 (SizeConst 必須)
ByValArray.ByValArray構造体内の固定長配列 (SizeConst 必須)
LPStruct.LPStruct構造体ポインタ
Struct.Struct構造体 (値渡し)
Interface.InterfaceCOM インターフェースポインタ
IUnknown.IUnknownIUnknown*
IDispatch.IDispatchIDispatch*
FunctionPtr.FunctionPtr関数ポインタ (デリゲート → native)
SysInt / SysUInt.SysInt / .SysUIntネイティブ整数 (IntPtr)
SafeArray.SafeArrayCOM SAFEARRAY
AsAny.AsAny実行時型推論
Error.ErrorHRESULT

実践例: Win32 API と MarshalAs の組み合わせ

NHSP

; GetWindowText の宣言
; バッファに LPStr で文字列を受け取る
#struct public RECT, Sequential
  #field public int Left
  #field public int Top
  #field public int Right
  #field public int Bottom
#endstruct

#class public WinApi
  #dllimport "user32.dll", CharSet = Unicode
  #dllfunc public static int GetWindowTextW, IntPtr hWnd, [LPWStr] string buffer, int maxCount
  #dllfunc public static int GetWindowRect, IntPtr hWnd, ref RECT rect

  #dllimport "kernel32.dll"
  #dllfunc public static int GetModuleFileNameA "GetModuleFileNameA", IntPtr hModule, [LPStr] string filename, int size
#endclass

C#

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left, Top, Right, Bottom;
}

public class WinApi
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int GetWindowTextW(
        IntPtr hWnd,
        [MarshalAs(UnmanagedType.LPWStr)] string buffer,
        int maxCount);

    [DllImport("user32.dll")]
    public static extern int GetWindowRect(
        IntPtr hWnd, ref RECT rect);

    [DllImport("kernel32.dll", EntryPoint = "GetModuleFileNameA")]
    public static extern int GetModuleFileNameA(
        IntPtr hModule,
        [MarshalAs(UnmanagedType.LPStr)] string filename,
        int size);
}

COM 属性

#attribute ディレクティブで .NET の COM 関連カスタム属性を付与できます。COM 相互運用コンポーネントを NHSP で作成可能。

NHSPC# 相当説明
#attribute Guid, "..."[Guid("...")]COM GUID の設定
#attribute ComVisible, "true"[ComVisible(true)]COM から可視にする
#attribute ClassInterface, "None"[ClassInterface(ClassInterfaceType.None)]クラスインターフェースの種別
#attribute InterfaceType, "InterfaceIsIDispatch"[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]インターフェースの COM 種別
#attribute ComSourceInterfaces, "型名"[ComSourceInterfaces(typeof(型))]イベントソースインターフェース

COM コンポーネントの完全な例

NHSP

#assembly "MyComLib"

; COM インターフェース
#attribute Guid, "EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F"
#interface ComClass1Interface
  #func string Hello
#endinterface

; イベントインターフェース
#attribute Guid, "7BD20046-DF8C-44A6-8F6B-687FAA26FA71"
#attribute InterfaceType, "InterfaceIsIDispatch"
#interface ComClass1Events
#endinterface

; COM クラス本体
#attribute Guid, "0D53A3E8-E51A-49C7-944E-E72A2064F938"
#attribute ClassInterface, "None"
#attribute ComVisible, "true"
#class public ComClass1 : ComClass1Interface
  #func public string Hello
    return "Hello COM!"
  #endfunc
#endclass

C#

using System.Runtime.InteropServices;

[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface ComClass1Interface
{
    string Hello();
}

[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
 InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ComClass1Events
{
}

[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"),
 ClassInterface(ClassInterfaceType.None),
 ComVisible(true)]
public class ComClass1 : ComClass1Interface
{
    public string Hello()
    {
        return "Hello COM!";
    }
}

enum 定義

#enum で列挙型を定義します。値の指定がない場合は自動的にインクリメントされます。

構文

#enum [修飾子] 列挙名
  メンバ名 [= 値]
  メンバ名 [= 値]
  ...
#endenum

NHSP

#enum Color
  Red
  Green
  Blue
#endenum

#enum Priority
  Low = 1
  Medium = 5
  High
  Critical = 100
#endenum

C#

public enum Color
{
    Red,    // 0
    Green,  // 1
    Blue    // 2
}

public enum Priority
{
    Low = 1,
    Medium = 5,
    High,       // 6 (自動)
    Critical = 100
}
値を省略すると 0 から自動インクリメント。途中で値を指定すると、以降はその値+1 から再開。 IL レベルでは ModuleBuilder.DefineEnum + DefineLiteral で生成。

switch / case

値による分岐処理。C# の switch 文に相当します。

構文

switch 式
  case 値1
    ; 処理
  case 値2
    ; 処理
  default
    ; どれにも一致しない場合
endswitch

NHSP

switch x
  case 1
    return "one"
  case 2
    return "two"
  default
    return "other"
endswitch

C#

switch (x)
{
    case 1: return "one";
    case 2: return "two";
    default: return "other";
}
NHSP の switch は自動的に break が入ります (C# のフォールスルーは発生しません)。
IL レベルでは各 case を Ceq + Brfalse の if-else チェーンで生成。

foreach

IEnumerable を実装するコレクションを反復処理します。

構文

foreach [型] 変数名 in コレクション式
  ; 本体
next

NHSP

foreach string item in list
  print item
next

C#

foreach (string item in list)
{
    Console.WriteLine(item);
}
IL レベルでは GetEnumerator()MoveNext() / Current のパターンを生成。 IDisposable を実装する列挙子は自動的に Dispose() が呼ばれます。
型名は省略可能 (その場合は Current プロパティの型から推論)。

using 文 (IDisposable)

using 文でリソースのスコープを管理します。ブロック終了時に自動的に Dispose() が呼ばれます。

構文

using 変数名 = 初期化式
  ; 本体
endusing

NHSP

using reader = new StreamReader("file.txt")
  dim string line = reader.ReadLine()
  print line
endusing

C#

using (var reader =
    new StreamReader("file.txt"))
{
    string line = reader.ReadLine();
    Console.WriteLine(line);
}
IL レベルでは try { 本体 } finally { if (var != null) var.Dispose(); } パターンを生成。

演算子一覧

演算子の優先順位 (上が高優先)

優先度種別演算子
1単項- ! ~ (ビット反転)
2乗除余* / %
3加減+ -
4シフト<< (左シフト) >> (右シフト)
5比較< > <= >=
6等値== != (= も == と同等)
7ビット AND&
8ビット XOR^
9ビット OR|
10論理 AND&& / and (短絡評価)
11論理 OR|| / or (短絡評価)
12null 合体??
13三項条件 ? 真 : 偽

代入演算子

演算子説明IL
=代入Stloc / Stfld
+= -= *= /=算術複合代入Add / Sub / Mul / Div
&= |= ^=ビット複合代入And / Or / Xor
++ --インクリメント / デクリメントLdloc + Ldc_I4_1 + Add/Sub + Stloc

特殊演算子

演算子説明
??null 合体 (左が null なら右を返す)s ?? "default"
? :三項演算子 (条件分岐式)a > b ? a : b
typeof(型)型の Type オブジェクトを取得typeof(int)System.Type
$"...{式}..."文字列補間 (変数・関数・式 全対応)$"x={str(x)}, len={s.Length}"

演算子の使用例

NHSP

; 算術
dim int x = 10 + 3 * 2    ; 16 (*が先)
dim int mod = 10 % 3       ; 1

; ビット演算
dim int flags = $FF & $0F  ; 0x0F (AND)
dim int mask = 1 << 4      ; 16 (左シフト)
dim int inv = ~$FF         ; ビット反転

; 比較 (= は == と同等)
if x = 16
  print "equal"
endif

; 三項演算子
dim int bigger = a > b ? a : b

; null 合体
dim string name = input ?? "anonymous"

; 論理演算 (短絡評価)
if x > 0 && y > 0
  print "both positive"
endif

; is / as
if obj is string
  dim string s = obj as string
endif

; 文字列補間
print $"x = {x}, name = {name}"

C#

int x = 10 + 3 * 2;   // 16
int mod = 10 % 3;      // 1

int flags = 0xFF & 0x0F;  // AND
int mask = 1 << 4;        // 16
int inv = ~0xFF;           // NOT

if (x == 16)
    Console.WriteLine("equal");

int bigger = a > b ? a : b;

string name = input ?? "anonymous";

if (x > 0 && y > 0)
    Console.WriteLine("both positive");

if (obj is string)
{
    string s = obj as string;
}

Console.WriteLine($"x = {x}, name = {name}");

ビルトイン関数・文

名前説明C# 相当
print exprコンソール出力Console.WriteLine(expr)
str(x)文字列変換x.ToString()
int(s)整数パースint.Parse(s)
double(s)実数パースdouble.Parse(s)
sleep msスレッド停止Thread.Sleep(ms)
new var, Type(args)オブジェクト生成 (文形式)var x = new Type(args)
dim var = new Type()オブジェクト生成 (dim 形式)var x = new Type()
typeof(型)Type オブジェクト取得typeof(int)
$"...{式}..."文字列補間 (変数・関数呼び出し・プロパティ・式 全対応)$"...{expr}..."
x++ / x--インクリメント / デクリメントx++; / x--;

使用例

NHSP

; 型変換
dim string s = str(42)       ; "42"
dim int n = int("123")       ; 123
dim double d = double("3.14") ; 3.14

; コンソール出力
print "Hello"              ; Hello
print 42                   ; 42
print "x = " + str(x)     ; x = 10

; 文字列補間 (変数・関数・プロパティ・式に対応)
dim string name = "World"
int x = 42
print $"Hello {name}!"            ; Hello World!
print $"x = {str(x)}"             ; x = 42
print $"len = {name.Length}"       ; len = 5
print $"{str(x)} * 2 = {str(x*2)}" ; 42 * 2 = 84

; オブジェクト生成 (2つの書き方)
new sb, StringBuilder()          ; 文形式
dim StringBuilder sb2 = new StringBuilder()  ; dim形式

; typeof
dim Type t = typeof(int)
print t.FullName           ; System.Int32

C#

string s = 42.ToString();
int n = int.Parse("123");
double d = double.Parse("3.14");

Console.WriteLine("Hello");
Console.WriteLine(42);
Console.WriteLine("x = " + x);

string name = "World";
Console.WriteLine($"Hello {name}!");

var sb = new StringBuilder();
var sb2 = new StringBuilder();

Type t = typeof(int);
Console.WriteLine(t.FullName);

ファイル分割 (#include)

#include ディレクティブでソースファイルを分割できます。指定されたファイルの内容がその位置に挿入されます。

構文

#include "ファイル名"

main.nhsp

#assembly "MyApp", exe
#include "classes.nhsp"
#include "utils.nhsp"

#main
  new calc, Calculator
  print calc.Add(1, 2)
#endmain

classes.nhsp

#class public Calculator
  #func public int Add, int a, int b
    return a + b
  #endfunc
#endclass
#include はコンパイル前のプリプロセス段階で処理されます。指定パスはソースファイルからの相対パスです。 ネストした #include は現在サポートされていません。

.NET API アクセス

全ての .NET Framework 4.8 API にアクセス可能。静的メソッドは ClassName.Method()、インスタンスメソッドは obj.Method() で呼び出し。

String (文字列操作)

NHSP

dim string s = "Hello World"
dim int len = s.Length            ; 11
dim string upper = s.ToUpper()    ; "HELLO WORLD"
dim bool has = s.Contains("World") ; true
dim string sub = s.Substring(0, 5) ; "Hello"
dim string rep = s.Replace("World", "NHSP")
dim string[] parts = s.Split(" ")

C#

string s = "Hello World";
int len = s.Length;
string upper = s.ToUpper();
bool has = s.Contains("World");
string sub = s.Substring(0, 5);
string rep = s.Replace("World", "NHSP");
string[] parts = s.Split(' ');

Math (数学関数)

NHSP

dim int bigger = Math.Max(10, 20)   ; 20
dim int abs = Math.Abs(-5)          ; 5
dim double sqrt = Math.Sqrt(2.0)    ; 1.414...
dim double pi = Math.PI             ; 3.14159...

C#

int bigger = Math.Max(10, 20);
int abs = Math.Abs(-5);
double sqrt = Math.Sqrt(2.0);
double pi = Math.PI;

Console (コンソール入出力)

NHSP

; 出力 (print は Console.WriteLine のショートカット)
print "Hello"
Console.Write("no newline")

; 入力
dim string input = Console.ReadLine()
dim int key = Console.Read()

C#

Console.WriteLine("Hello");
Console.Write("no newline");

string input = Console.ReadLine();
int key = Console.Read();

DateTime (日付・時刻)

NHSP

dim DateTime now = DateTime.Now
print now.ToString("yyyy-MM-dd HH:mm:ss")
dim int year = now.Year
dim int month = now.Month

C#

var now = DateTime.Now;
Console.WriteLine(
    now.ToString("yyyy-MM-dd HH:mm:ss"));
int year = now.Year;
int month = now.Month;

File / Path (ファイル操作)

NHSP

#reference "System.dll"

; ファイル読み書き
File.WriteAllText("test.txt", "Hello")
dim string content = File.ReadAllText("test.txt")
dim bool exists = File.Exists("test.txt")

; パス操作
dim string dir = Path.GetDirectoryName("C:\\temp\\file.txt")
dim string ext = Path.GetExtension("image.png")

C#

File.WriteAllText("test.txt", "Hello");
string content = File.ReadAllText("test.txt");
bool exists = File.Exists("test.txt");

string dir = Path.GetDirectoryName(
    @"C:\temp\file.txt");
string ext = Path.GetExtension("image.png");

List<T> / Dictionary<K,V> (コレクション)

NHSP

; リスト
dim List<int> nums = new List<int>()
nums.Add(1)
nums.Add(2)
nums.Add(3)
print str(nums.Count)    ; 3

; ディクショナリ
dim Dictionary<string,int> map = new Dictionary<string,int>()
map.Add("apple", 100)
map.Add("banana", 200)

C#

var nums = new List<int>();
nums.Add(1);
nums.Add(2);
nums.Add(3);
Console.WriteLine(nums.Count);

var map = new Dictionary<string, int>();
map.Add("apple", 100);
map.Add("banana", 200);

Environment / Process (システム情報)

NHSP

; 環境情報
print Environment.MachineName
print Environment.OSVersion.ToString()
print Environment.CurrentDirectory

; プロセス起動
Process.Start("notepad.exe")

C#

Console.WriteLine(Environment.MachineName);
Console.WriteLine(Environment.OSVersion);
Console.WriteLine(Environment.CurrentDirectory);

Process.Start("notepad.exe");

CLI オプション

nhspc.exe <input.nhsp> [options]

  <input.nhsp>          : ソースファイル
  -o <file>             : 出力ファイルパス (省略時: アセンブリ名.exe / .dll)
  -debug                : PDB デバッグ情報を生成
  -platform <plat>      : ターゲットプラットフォーム
                            anycpu    (既定) — AnyCPU
                            x86       — 32bit 専用
                            x64       — 64bit 専用
                            anycpu32  — AnyCPU + 32bit 優先
  -target <kind>        : 出力種別を強制
                            exe — 強制的に EXE
                            dll — 強制的に DLL
                            (省略時は #main の有無で自動判定)
  -subsystem <sub>      : EXE のサブシステム
                            console   (既定) — コンソール窓を表示
                            windows   — GUI アプリ用 (コンソール窓なし)
  -r <assembly>         : 参照アセンブリを追加 (複数指定可)
  -reference <assembly> : -r の別名
  -win32icon <file>     : アプリケーションアイコンを埋め込む
                          ico/png/bmp/gif/jpg/jpeg/tif/tiff に対応
                          (ico 以外は内部で .ico に自動変換)

使用例

REM AnyCPU 既定 + メタデータはソース側 (#version 等) で指定
nhspc.exe app.nhsp

REM x64 専用 + デバッグ情報
nhspc.exe app.nhsp -platform x64 -debug

REM WinForms GUI アプリ (コンソール窓なし)
nhspc.exe gui.nhsp -subsystem windows -win32icon logo.png

REM ライブラリ参照を CLI から追加
nhspc.exe app.nhsp -r System.Xml.dll -r System.Drawing.dll

以下の設定はソース側ディレクティブ (#icon / #reference / #manifest 等) と CLI オプション (-win32icon / -r) のどちらでも指定できます。両方指定された場合、ソース側が優先 (CLI は不足分のみ追加) される ものと、CLI が上書きするものがあるので注意してください。
- #icon が指定されている場合: -win32icon は無視
- #reference-r: 両方の集合がマージされる
- -target: #main 自動判定や #assembly "...", exe を上書きする

デバッグ情報 (PDB / Portable PDB)

nhspc -debug を付けてビルドすると、生成 EXE/DLL と並んで .pdb ファイルが出力されます。これは Windows PDB 形式 (フル PDB) で、 System.Reflection.Emit がデフォルトで生成する形式です。

Visual Studio や dnSpy はこの Windows PDB をそのまま読めますが、 VS Code (.NET ランタイム coreclr デバッガ) や Rider のクロスプラット フォームデバッガは Portable PDB 形式しか受け付けません。 そのため nhspc には変換ツールが同梱されています。

Pdb2PortablePdb ツール

Pdb2PortablePdb <assembly.exe|dll> [-o <output.pdb>]

  <assembly>     : 対象アセンブリ (隣接する同名 .pdb を読み込む)
  -o <output>    : 出力先 .pdb (省略時は元の .pdb を上書き)

内部的には Microsoft.DiaSymReader.ToolsPdbConverter を使って Windows PDB → Portable PDB の変換を行います。アセンブリ本体は変更されません (MVID と PDB の対応関係はそのまま維持されます)。

VS Code 拡張 (vscode-nhsp) との連携

VS Code 拡張から NHSP Debug を起動すると、コンパイル直後に自動的に Pdb2PortablePdb.exe が呼ばれ、生成された Windows PDB がその場で Portable PDB に変換されてから、coreclr デバッガが起動されます。手動で変換する必要は ありません。ブレークポイント、ステップ実行、変数ウォッチがそのまま使えます。

変換が必要な場面・不要な場面

シナリオ必要な PDB 形式nhspc から見た手順
Visual Studio (Windows) でデバッグWindows PDBnhspc -debug のみで OK (変換不要)
dnSpy / dnSpyEx でデバッグWindows PDBnhspc -debug のみで OK
VS Code (C# / coreclr) でデバッグPortable PDBvscode-nhsp 拡張が自動変換 (手動なら Pdb2PortablePdb)
Rider (.NET) でデバッグPortable PDB 推奨Pdb2PortablePdb で変換
本番リリース (デバッグなし)不要-debug を付けない
なぜ nhspc 自体が Portable PDB を直接吐かないのか: System.Reflection.EmitDefineDocument / ISymbolWriter 系 API は Windows PDB 専用です。Portable PDB を直接生成するには System.Reflection.Metadata.MetadataBuilder を使った別系統の IL/メタデータビルダーが必要で、AssemblyBuilder ベースの現行 コードからは差し替えコストが大きいため、現状は「フル PDB を出して必要なら 変換」という二段構えにしています。

Visual Studio 2022 で使う

nhspc のリポジトリには Visual Studio 2022 用の TextMate ベース構文ハイライト拡張 nhsp-language.vsix が同梱されています。インストールすると .nhsp ファイルが NHSP 用のシンタックスハイライトで開けるようになり、デバッグも追加設定なしで使えます。

ビルドとインストール

もっとも簡単な手順は install.bat を実行することです。VSIX が未ビルドなら自動でビルドします。 ABC 全フェーズを 1 コマンドでセットアップできます:

install.bat [/q] [/home [path]] [/openfolder <project-dir>] [/noinstall]
フラグ説明
/qサイレント (VSIXInstaller の GUI 確認ダイアログを出さない / 既存テンプレも問答無用で上書き)
/home [path]Phase B 用に NHSPC_HOME ユーザー環境変数を setx で永続設定。
パス省略時は nhspc\bin\Releasedist 等を自動検出
/openfolder <dir>Phase B テンプレ (launch.vs.json / tasks.vs.json) を <dir>\.vs\ にコピー
/noinstallVSIXInstaller 呼び出しをスキップ。VSIX は既に入っていて /home/openfolder だけ実行したい時に便利
REM Phase A + C のみ (VSIX をインストール、確認ダイアログあり)
install.bat

REM Phase A + C のみ (サイレント)
install.bat /q

REM Phase A + B + C 全部入り (NHSPC_HOME を自動検出し、project に templates 配置)
install.bat /home /openfolder C:\projects\myapp

REM サイレント + 明示パス指定
install.bat /q /home C:\tools\nhspc /openfolder C:\projects\myapp

REM 既に VSIX 入れた後で別プロジェクトに Open Folder テンプレだけ追加
install.bat /noinstall /openfolder C:\projects\anotherapp

REM アンインストール
nhspc\vs2022-nhsp\uninstall.bat

REM サイレントアンインストール
nhspc\vs2022-nhsp\uninstall.bat /q

install.batvswhere.exe で VS 2022 を検出し、VSIXInstaller.exe を呼び出します。 インストール時は VS 2022 を 事前に終了させておく 必要があります (起動中だとインストールがキューイングされて 次回 VS 起動時まで反映されません)。

手動でビルドだけしたい場合や、ZIP の中身を見たい場合は build.bat を直接呼んでください:

nhspc\vs2022-nhsp\build.bat        REM nhsp-language.vsix を生成のみ

VSIXInstaller がはじいた場合は、nhsp-language.vsix の中身 (zip として開く) を次の場所に手動で展開して VS を再起動するフォールバックもあります (17.0_xxxx のサフィックスはインスタンスごとに異なります):

%LocalAppData%\Microsoft\VisualStudio\17.0_xxxx\Extensions\IronHSP\NhspLanguage\

デバッグ手順 (追加設定不要)

nhspc が出力する Windows PDB は VS 2022 のデバッガが第一級でサポートする形式なので、 拡張機能を入れなくてもデバッグ自体は動きます。手順:

  1. nhspc app.nhsp -o app.exe -debug で EXE と PDB を生成
  2. VS 2022 を起動 → File → Open → Project/Solutionapp.exe を選択
  3. ソリューション エクスプローラーに表示された app.exe を右クリック → Set as Startup Project
  4. app.nhsp を VS 内で開いて行頭マージンをクリックしてブレークポイントを置く
  5. F5 で起動 → BP で停止 → ステップ実行 (F10 / F11) / Locals ウィンドウ / Call Stack / 変数ホバー評価 が C# と同じように動く
なぜ追加設定なしで動くのか: nhspc は PDB の DefineDocument.nhsp ファイルの絶対パスを書き込んでいるため、VS デバッガはソース行情報から 直接 .nhsp を開けます。VS 2022 が .nhsp という拡張子を「言語」として知らなくても、 デバッガはソース行のマップに拡張子を見ないので問題なく動きます。Locals ウィンドウに 出る変数名・型は nhspc が PDB の LocalScope に書き込んだものがそのまま表示されます。

Open Folder モード (F5 で自動コンパイル → デバッグ)

プロジェクトファイルなしで .nhsp を直接編集 → F5 でビルド → デバッグしたい場合は、 VS 2022 の Open Folder モードに tasks.vs.jsonlaunch.vs.json を置く方法があります (VS Code の launch.json / tasks.json と同じノリ)。

nhspc/vs2022-nhsp/openfolder-templates/ にテンプレートを同梱しています。次の手順でセットアップしてください:

  1. nhspc.exe があるフォルダのパスを環境変数 NHSPC_HOME に設定 (例: setx NHSPC_HOME C:\tools\nhspc)
  2. 作業フォルダ (例: C:\projects\myapp) に .vs サブフォルダを作る
  3. テンプレ 2 つを .vs\ にコピー: tasks.vs.json / launch.vs.json
  4. VS 2022 → File → Open → Folder でその作業フォルダを開く
  5. F5 → 初回のみ EXE 名を聞かれる (例: app.exe) → 入力するとビルドタスクが走り、デバッガが起動

テンプレが提供するタスク:

taskLabel説明
NHSP: Build (Debug)nhspc -debug で AnyCPU ビルド (BP / Locals 用 PDB 付き)
NHSP: Build (Release)-debug なしの AnyCPU ビルド
NHSP: Build x64 (Debug)-platform x64 -debug で 64bit 専用ビルド

タスクは Solution Explorer の .nhsp ファイルを右クリック → 任意のタスクを実行で個別に呼ぶこともできます。 ビルドだけしたい (デバッガを立ち上げたくない) ときに便利です。

カスタマイズ: プラットフォーム固定 / アイコン埋め込み / 参照アセンブリ追加など プロジェクト固有のオプションを使いたい場合は、tasks.vs.jsonargs 配列を直接編集してください。${file} / ${fileDirname} / ${fileBasenameNoExtension} / ${workspaceRoot} / ${env.X} などの プレースホルダが使えます。

VSIX 拡張で何が増えるか

機能VSIX なしVSIX あり
シンタックスハイライト白黒キーワード/型/文字列/コメントが色分け
ファイル拡張子の関連付け「不明な拡張子」扱いNHSP として開く
デバッグ (BP / Locals / Step)動く動く (変わらず)
IntelliSense / 補完なしなし (LSP の補完機能は将来追加予定)
エラー波線 (リアルタイム)なしVS Code 経由で nhspls.exe を使えば可 (下記 LSP セクション参照)

Language Server (nhspls.exe)

nhspls.exe は nhspc のレキサー / パーサーをそのまま流用した Language Server Protocol 実装です。エディタが文書の変更を通知してくると、 nhspls がリアルタイムで構文解析を回して publishDiagnostics で赤波線を返します。 コンパイル (IL Emit) は走らないので軽量で、保存しなくてもタイプ中にエラー位置が出ます。

サポート機能

LSP メソッド動作
initialize / initializedCapabilities ハンドシェイク (textDocumentSync = Full)
textDocument/didOpen / didChange / didClose文書ストアに lex+parse 結果をキャッシュ → 各機能で共有
textDocument/publishDiagnosticsパースで集めた DiagnosticBag を LSP 形式で送出 (リアルタイム赤波線)
textDocument/documentSymbolアウトライン: クラス → メソッド/フィールド/プロパティ/イベント の階層、インターフェース、enum、delegate
textDocument/completion# 後はディレクティブ一覧、それ以外はキーワード + 型エイリアス + ユーザー定義クラス/インターフェース
textDocument/hoverカーソル下のトークン種別を判定: 型エイリアスは CLR 名、キーワードは説明、識別子はクラス/メソッド/フィールドのシグネチャ
textDocument/definitionカーソル下の識別子をユニット内のクラス/メソッド/フィールド/プロパティ/インターフェース/enum/delegate と照合してジャンプ先を返す
shutdown / exitクリーン終了

未実装 (将来予定):

VS Code から使う

vscode-nhsp 拡張は起動時に nhspls.exe を子プロセスとして spawn し、開いている .nhsp すべてに対して didOpen/didChange を流します。設定:

設定キー既定値説明
nhsp.languageServer.enabledtrueLSP を有効にするか。false にすると compile-on-save の診断のみになる
nhsp.languageServerPath(空)nhspls.exe のパス。空なら拡張同梱版 → ワークスペース内のビルド出力 の順に自動検出

VS 2022 から使う

nhsp-language.vsix には MEF コンポーネント NhspVsLanguageClient.dll が同梱されており、VS 2022 で .nhsp ファイルを開くと 自動的に nhspls.exe が子プロセスとして spawn されます。VS 2022 の Microsoft.VisualStudio.LanguageServer.Client API が LSP プロトコルを駆動するので、以下が動きます:

機能VS 2022 での見え方
診断 (publishDiagnostics)エラー リスト + コード ウィンドウの赤波線
documentSymbolナビゲーションバー (上部のドロップダウン) と「コード マップ」
completionCtrl+Space で候補表示、# でディレクティブ補完
hover識別子にマウスホバーで型/シグネチャ ツールチップ
definitionF12 / Go to Definition で #class / #func へジャンプ

ビルド方法: nhspc\vs2022-nhsp\build.bat を実行すると NhspVsLanguageClientNhspLanguageServer をリリースビルドし、必要な DLL/EXE を全部 zip して nhsp-language.vsix を作ります。 出来上がった VSIX をダブルクリックでインストール → VS 2022 を再起動 → .nhsp ファイルを開けば動き出します。

VS 2022 LSP の制限: Microsoft.VisualStudio.LanguageServer.Client API はシンプルさを優先しているため、 カスタムコマンドや CodeLens など一部の高度な LSP 機能は VS 側でサポートされていません。 診断 / 補完 / hover / definition / documentSymbol の5つは公式にサポート済みなので nhspls との 組み合わせでフル機能が動きます。

手動でテストする

nhspls.exe は単体で起動して JSON-RPC で対話できます。デバッグ用にスクリプトから叩く例:

; Python から (Content-Length ヘッダ + JSON ボディで送る)
import subprocess, json
def msg(m):
    body = json.dumps(m).encode()
    return f"Content-Length: {len(body)}\r\n\r\n".encode() + body

p = subprocess.Popen([r"...\nhspls.exe"],
    stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write(msg({"jsonrpc":"2.0","id":1,"method":"initialize",
    "params":{"processId":0,"rootUri":None,"capabilities":{}}}))
p.stdin.flush()
; ... read framed response from p.stdout

既知の制限・未実装機能

既知の制限

制限回避策
メソッド名に HSP 予約語 (repeat, double 等) は使えない別の名前を使用 (例: Double → Twice)
複数クラスの new で一部 AccessViolation同一クラス内からの参照、または1対1のクロス参照のみ
ジェネリクス型の利用で一部 StackOverflow複雑なネストを避ける
catch when (例外フィルタ) の IL 生成未実装構文は認識されるが、when 条件は実行時に無視される。catch 内で if 分岐で代替

未実装の C# 機能

中優先 (実装可能だが工数が必要)

機能C# 例難易度回避策
ジェネリクス型定義class Box<T> { T Value; }既存の .NET ジェネリクス型 (List<int> 等) の利用は対応済み。独自ジェネリクスクラスの定義のみ未対応。C# ヘルパー DLL で定義して参照する方法で代替可能
yield return (イテレータ)yield return x;ステートマシンクラスの自動生成が必要。List<T> を構築して返すことで代替
多次元配列int[,] matrixジャグ配列 int[][] は対応済み。真の多次元配列は Array.CreateInstance で代替

低優先 (C# コンパイラレベルの実装が必要)

機能C# 例難易度回避策
async / awaitasync Task<int> M() { await ...; }超難ステートマシン + AsyncTaskMethodBuilder の完全生成が必要。Thread / Task.Run で代替
ラムダ式(x) => x * 2クロージャクラスの自動生成 + キャプチャ変数の管理が必要。#delegate 型定義は対応済み。通常のメソッド参照で代替
LINQ クエリ構文from x in list where x > 5 select xメソッドチェーン (.Where().Select()) への構文変換が必要。直接メソッドチェーンを記述して代替
パターンマッチングx is int i and > 0C# 7+ の高度なパターン。is / as + if 分岐で代替
拡張メソッドstatic void M(this string s)メソッド解決の仕組み変更が必要。通常の static メソッドとして定義・呼び出しで代替
レコード型record Person(string Name)Equals/GetHashCode/ToString の自動生成が必要。通常のクラスで手動実装して代替
タプル(int, string) pair = (1, "a")ValueTuple<T1,T2> + 分解代入が必要。クラスやフィールドで代替
dynamic 型dynamic obj = ...;超難DLR (CallSite / Binder) が必要。var (Object) + リフレクションで代替
unsafe / ポインタint* p = &x;P/Invoke + IntPtr で代替

対応済み機能一覧 (Phase 1-18)

カテゴリ機能
型定義class, interface, struct, enum, delegate, sealed, abstract, nested class
メンバーfield (const/readonly/static), method (virtual/override/abstract), constructor, static constructor, destructor, property, event, indexer, operator overload
パラメータref, out, params, default value, [MarshalAs], [In]/[Out]
制御構文if/elseif/else, repeat/loop, while/wend, for/next, foreach/next, switch/case, break, continue
例外処理try/catch (複数)/finally, throw, catch with type filter, Leave 自動対応
演算子算術 (+,-,*,/,%), 比較 (==,!=,<,>,<=,>=), 論理 (&&,||,!), ビット (&,|,^,~,<<,>>), 三項 (?:), null合体 (??), is, as, typeof, ++/--
int, int64, double, float, string, bool, byte, sbyte, short, ushort, uint, ulong, char, IntPtr, UIntPtr, int? (Nullable), int[] (配列), int[][] (ジャグ配列), List<T> (ジェネリクス利用)
dim, new, print, sleep, lock, using, return, throw
その他$"補間文字列 (変数・関数・式対応)", 16進数 ($FF/0xFF), #namespace, #include, #dllimport/#dllfunc (EntryPoint/CharSet/CallingConvention/SetLastError), COM属性, PDB デバッグ情報 (Windows PDB + Pdb2PortablePdb で Portable PDB へ変換可), コロン区切り (:), 行継続 (\), dim 省略形, null/nullptr, 値型メンバーアクセス (DateTime 等)