NHSP 入門ガイド

HSP風の書きやすさで .NET アセンブリを作ろう

1. NHSP とは?

NHSP (Net HSP) は、HSP に似た文法で .NET のクラスライブラリ (DLL) や実行ファイル (EXE) を作れるプログラミング言語です。

特徴

NHSP (.nhsp)

#assembly "HelloLib"
#class public Greeter
  #func public string Hello, string name
    return "Hello, " + name + "!"
  #endfunc
#endclass

C# (.cs) で同じこと

public class Greeter
{
    public string Hello(string name)
    {
        return "Hello, " + name + "!";
    }
}

2. インストールと最初のプログラム

2.1 コンパイラの準備

nhspc.exeNhspCompiler.Core.dll を同じフォルダに置くだけです。インストール不要。

2.2 最初のプログラム

テキストエディタで hello.nhsp を作成:

; hello.nhsp - 最初のプログラム
#assembly "HelloLib"

#class public Hello
  #func public static string SayHello
    return "Hello from NHSP!"
  #endfunc
#endclass

2.3 コンパイル

nhspc.exe hello.nhsp -o HelloLib.dll
-debug オプションを付けると、デバッグ用の PDB ファイルも生成されます:
nhspc.exe hello.nhsp -o HelloLib.dll -debug

VS Code でデバッグしたい場合: nhspc -debug が出力するのは Windows PDB 形式で、Visual Studio や dnSpy ではそのまま使えますが、 VS Code (.NET) のデバッガは Portable PDB しか読めません。 そのため nhspc には Pdb2PortablePdb.exe という変換ツールが付属しており、 VS Code 拡張 (vscode-nhsp) を使えばコンパイル時に自動で変換されます。 詳細は リファレンスの PDB セクション を参照。

Visual Studio 2022 でデバッグしたい場合: nhspc が出す Windows PDB は VS 2022 のデバッガがネイティブに読めるので、追加変換は不要です。 app.exe を VS で開いて F5 するだけでブレークポイント / Locals / コールスタックが 動きます。シンタックスハイライト用に同梱の nhsp-language.vsix も合わせて インストールするのが推奨です。詳細は リファレンスの Visual Studio 2022 セクション を参照。

2.4 HSP3 から使う

; HSP3 スクリプト
loadnet "HelloLib.dll", 2
newnet p, "HelloLib", "Hello", 1
netres r
mcall p, "SayHello"
mes nettoval(r, 2)   ; => "Hello from NHSP!"

3. 基本の文法

3.1 コメント

; これはコメント(HSP スタイル)
// これもコメント(C スタイル)

3.2 変数

; dim を使った宣言
dim int x = 42
dim string name = "Taro"

; dim 省略形 (C# と同じ書き方)
int y = 100
string msg = "Hello"

; 型推論(最初の代入で型が決まる)
count = 100        ; int 型
text = "abc"       ; string 型

; 配列
dim int arr, 10
arr(0) = 100
arr(1) = 200

; null (空っぽ) を入れる
string s = null      ; C# 形式
string s2 = nullptr  ; C++ 形式 (同じ動作)

; null チェック
string name = s ?? "名無し"  ; s が null なら "名無し"

3.3 コロン区切りと行継続

; HSP と同じ!コロンで1行に複数文が書ける
int a = 1 : int b = 2 : int c = a + b

; 長い行は \ で分割できる(HSP 互換)
#dllfunc public static int MessageBoxW, \
  IntPtr hWnd, string text, \
  string caption, int type
HSP の : (文区切り) と \ (行継続) がそのまま使えます。C# には同じ機能がありません。

3.3 型の一覧

NHSP説明
int32bit 整数42
double倍精度小数3.14
string文字列"Hello"
bool真偽値true / false
int6464bit 整数9999999999

3.4 演算子

; 算術: + - * / %
result = 10 + 3 * 2    ; => 16 (掛け算が先)

; 比較: == != < > <= >=
; HSP互換: = も == と同じ
if x = 5 { ... }       ; x == 5 と同じ

; 論理: && || !
if x > 0 && x < 10 { ... }

; 複合代入: += -= *= /=
x += 5

3.5 型変換

; 数値 → 文字列
s = str(42)            ; => "42"

; 文字列 → 数値
n = int("123")         ; => 123

; 文字列連結 (+ で自動変換)
msg = "値は" + str(x)

4. クラスを作ろう

4.1 シンプルなクラス

#class public Calculator

  #func public int Add, int a, int b
    return a + b
  #endfunc

  #func public int Multiply, int a, int b
    return a * b
  #endfunc

#endclass

4.2 フィールド(データの保持)

#class public Counter
  #field public int Count

  #func public Increment
    Count += 1
  #endfunc

  #func public int GetCount
    return Count
  #endfunc
#endclass

4.3 コンストラクタ(初期化)

#class public Person
  #field public string Name
  #field public int Age

  ; コンストラクタ: オブジェクト作成時に呼ばれる
  #init string name, int age
    Name = name
    Age = age
  #endinit

  #func public string Greet
    return "I'm " + Name + ", age " + str(Age)
  #endfunc
#endclass

4.4 プロパティ

#class public Rectangle
  #field public int Width
  #field public int Height

  #init int w, int h
    Width = w
    Height = h
  #endinit

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

4.5 static メソッド

インスタンスを作らなくても呼べるメソッドです。

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

5. 制御構文

5.1 if / else

if score >= 80 {
  print "優"
} elseif score >= 60 {
  print "良"
} else {
  print "不可"
}

5.2 repeat / loop(HSP スタイル)

; cnt がカウンター(0 から開始)
repeat 5
  print str(cnt)   ; 0, 1, 2, 3, 4
loop

5.3 for / next

for i = 1 to 10
  print str(i)     ; 1, 2, ..., 10
next

; step 指定
for i = 0 to 100 step 10
  print str(i)     ; 0, 10, 20, ..., 100
next

5.4 while / wend

dim int i
i = 10
while i > 0
  print str(i)
  i -= 1
wend

5.5 break / continue

repeat 100
  if cnt = 50 { break }      ; ループを抜ける
  if cnt % 2 = 0 { continue }  ; 偶数はスキップ
  print str(cnt)
loop

6. 配列を使う

; 配列の宣言(10個分)
dim int scores, 10

; 値の代入
scores(0) = 85
scores(1) = 92
scores(2) = 78

; ループで合計
dim int total
total = 0
for i = 0 to 2
  total += scores(i)
next
print "合計: " + str(total)

7. 継承とインターフェース

7.1 継承

#class public Animal
  #field public string Name
  #init string name
    Name = name
  #endinit
  #func public virtual string Speak
    return Name + " says ..."
  #endfunc
#endclass

; Dog は Animal を継承
#class public Dog : Animal
  #init string name
    Name = name
  #endinit
  #func public override string Speak
    return Name + " says Woof!"
  #endfunc
#endclass

7.2 インターフェース

; 「何ができるか」を定義
#interface IDrawable
  #func Draw
#endinterface

; インターフェースを実装
#class public Circle : IDrawable
  #func public Draw
    print "Drawing a circle"
  #endfunc
#endclass

8. .NET API を呼び出す

; String のメソッド
dim string s
s = "Hello World"
print str(s.Length)       ; => 11
print s.ToUpper()         ; => "HELLO WORLD"
print s.Contains("World") ; => True

; Math クラス
dim int max
max = Math.Max(10, 20)    ; => 20
print str(Math.Abs(-42))  ; => 42

9. エラー処理

dim int result
result = 0
try
  result = 10 / 0    ; ゼロ除算!
catch ex
  result = -1        ; エラー時の処理
endtry
print str(result)    ; => -1

10. EXE を作る

; app.nhsp
#assembly "MyApp"

#class public Greeter
  #func public static string Hello, string name
    return "Hello, " + name + "!"
  #endfunc
#endclass

#main
  print Greeter.Hello("World")
#endmain
nhspc.exe app.nhsp -o MyApp.exe

EXE にアイコンとバージョン情報を付ける

作った EXE のエクスプローラー上のアイコンや、右クリック → プロパティの「詳細」タブに出るバージョン情報・会社名・著作権なども ソース内でディレクティブとして指定できます。

#assembly "MyApp"

; --- バージョン情報 (右クリックプロパティに出る) ---
#version      "1.0.0.0"
#fileversion  "1.0.0.0"
#title        "私のアプリ"
#description  "ボタンを押すと挨拶するアプリ"
#company      "ACME 株式会社"
#product      "MyApp"
#copyright    "(c) 2026 ACME"

; --- アイコン (.ico はもちろん、.png/.bmp/.gif/.jpg もそのまま渡せる) ---
#icon "logo.png"

#main
  print "Hello"
#endmain
アイコン: #icon.ico を渡せばそのまま埋め込み、PNG / BMP / GIF / JPEG / TIFF を渡すと nhspc が内部で 16 / 32 / 48 / 256 ピクセルの 4 サイズに変換した .ico を自動生成して埋め込みます。 わざわざ .ico を作る必要はありません。

GUI アプリで黒いコンソール窓を出さない

WinForms などの GUI アプリで EXE をダブルクリックすると、デフォルトでは後ろに黒いコンソール窓が出てしまいます。 これを抑制するには -subsystem windows オプションを使います。

nhspc.exe gui_app.nhsp -o GuiApp.exe -subsystem windows

x86 / x64 を指定する

既定の AnyCPU は実行マシンに合わせて自動的に 32bit / 64bit が選ばれます。 特定のアーキテクチャに固定したい (例: 32bit DLL を P/Invoke する) 場合は -platform を使います。

nhspc.exe app.nhsp -platform x86       REM 32bit 専用
nhspc.exe app.nhsp -platform x64       REM 64bit 専用
nhspc.exe app.nhsp -platform anycpu32  REM AnyCPU + 32bit 優先

11. Win32 API を呼び出す (P/Invoke)

Windows の DLL にある関数を直接呼び出すことができます。HSP の #uselib / #func に相当します。

#class public WinApi
  ; user32.dll の関数を宣言
  #dllimport "user32.dll"
  #dllfunc public static int MessageBoxA, int hWnd, string text, string caption, int type
  #dllfunc public static int MessageBoxW, int hWnd, string text, string caption, int type

  ; kernel32.dll の関数を宣言
  #dllimport "kernel32.dll"
  #dllfunc public static int GetTickCount
  #dllfunc public static int GetCurrentProcessId
#endclass

NHSP

; DLL の関数を宣言
#dllimport "user32.dll"
#dllfunc public static int MessageBoxA, int hWnd, string text, string caption, int type

HSP3 で同じこと

; DLL の関数を宣言
#uselib "user32.dll"
#func MessageBoxA "MessageBoxA" int, sptr, sptr, int
#dllimport で DLL 名を宣言し、#dllfunc で関数を列挙します。 #dllfunc は宣言だけなので #endfunc は不要。 同じ DLL の関数は #dllimport 1回で複数宣言できます。

Unicode や C ランタイム関数の呼び出し

#dllimport にオプションを指定できます。

; Unicode 版の API を呼ぶ
#dllimport "user32.dll", CharSet = Unicode
#dllfunc public static int MessageBoxW, IntPtr hWnd, string text, string caption, int type

; C ランタイム関数は Cdecl で呼ぶ
#dllimport "msvcrt.dll", CallingConvention = Cdecl
#dllfunc public static int puts, string str

引数に [MarshalAs] を付けて文字列の変換方式を指定することもできます。

; 引数の文字列を ANSI 文字として渡す
#dllfunc public static int GetModuleFileName, IntPtr hModule, [LPStr] string buffer, int size

12. COM コンポーネント

COM 相互運用コンポーネントも作れます。#attribute で GUID やインターフェースの種類を指定します。

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

; COM クラス
#attribute Guid, "0D53A3E8-E51A-49C7-944E-E72A2064F938"
#attribute ClassInterface, "None"
#attribute ComVisible, "true"
#class public MyComponent : IMyComponent
  #func public string GetMessage
    return "Hello from NHSP COM!"
  #endfunc
#endclass
作成した DLL は regasm.exe で COM 登録すれば、VBScript や VBA など COM 対応言語から利用できます。

13. HSP3 (IronHSP) から使う

NHSP でコンパイルした DLL は、IronHSP の loadnet で読み込めます。

; HSP3 スクリプト
loadnet "MyLib.dll", 2
newnet p, "MyLib", "Calculator", 0
netres r
mcall p, "Add", 10, 20
mes "結果: " + nettoval(r, 4)   ; => 結果: 30
NHSP の出力は標準的な .NET アセンブリなので、C# や VB.NET からも参照できます。

14. 構造体を使う

構造体は「値型」と呼ばれるデータの入れ物です。Win32 API にデータを渡すときによく使います。HSP の構造体変数に近い考え方です。

基本の構造体

; 2D座標を表す構造体
#struct public Point2D, Sequential
  #field public int X
  #field public int Y
#endstruct

C# では struct キーワードで書くものが、NHSP では #struct ... #endstruct になります。

Win32 API 用の構造体

Windows API で使う構造体はパッキング (アラインメント) の指定が必要な場合があります。

; SYSTEMTIME 構造体 (Pack = 2 でパッキング指定)
#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

; 使い方: P/Invoke と組み合わせる
#class public TimeUtil
  #dllimport "kernel32.dll"
  #dllfunc public static void GetSystemTime, ref SYSTEMTIME st
#endclass
ushort は符号なし16ビット整数。他にも uint, ulong, sbyte, char などの型が使えます。 IntPtr はポインタサイズの整数で、ハンドル (HWND, HANDLE) を渡すときに使います。

共用体 (Union)

同じメモリ位置に違う型でアクセスしたい場合は Explicit レイアウトと [FieldOffset] を使います。

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

文字列を含む構造体

固定長の文字列フィールドには [MarshalAs] で長さを指定します。

#struct public FileInfo, Sequential, CharSet = Unicode
  #field public int Id
  [MarshalAs ByValTStr, 260] #field public string Path
#endstruct

15. ref / out で値を返す

通常メソッドは return で1つの値しか返せませんが、refout を使うと引数経由で複数の値を返せます。

ref (参照渡し)

ref は「呼び出し元の変数を直接書き換える」意味です。

#class public MathUtil
  ; ref で渡した変数を直接変更
  #func public static void AddTen, ref int value
    value = value + 10
  #endfunc

  ; 2つの変数を入れ替え
  #func public static void Swap, ref int a, ref int b
    dim int temp
    temp = a
    a = b
    b = temp
  #endfunc
#endclass

out (出力引数)

out は「メソッド内で必ず値を設定して返す」意味です。TryParse パターンでよく使います。

#class public Parser
  ; 文字列を数値に変換、成功/失敗を return で返し、結果は out で返す
  #func public static bool TryParse, string s, out int result
    result = 42
    return true
  #endfunc
#endclass

HSP3 で同じこと

; HSP では変数を直接参照渡しできないので
; 配列の要素を使ったり、命令で返す
; NHSP の ref/out なら直接書ける

C# で同じこと

public static void AddTen(ref int value)
{
    value = value + 10;
}
public static bool TryParse(
    string s, out int result)
{
    result = 42;
    return true;
}

16. 定数とデリゲート

const (コンパイル時定数)

const をつけたフィールドはコンパイル時に値が確定し、変更できません。

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

readonly (読み取り専用)

readonly はコンストラクタで1回だけ設定でき、その後は変更できません。

#class public Settings
  #field public readonly int MaxSize

  #init int size
    MaxSize = size
  #endinit
#endclass

static フィールド

static フィールドはクラスに1つだけ存在し、全インスタンスで共有されます。

#class public Counter
  #field public static int Count
#endclass

デリゲート (関数の型)

デリゲートは「この形の関数」を表す型です。コールバックやイベントで使います。

; 「int を2つ受け取って int を返す関数」を表すデリゲート型
#delegate public int BinaryOp, int a, int b

; void のデリゲート
#delegate public void EventHandler, object sender, string message
デリゲートは C# の delegate と同じです。HSP にはない概念ですが、 .NET のイベント処理やコールバック関数で必要になることがあります。

17. 列挙型 (enum)

列挙型は、決まった選択肢の中から値を選ぶときに使います。HSP の定数定義に近い使い方ができます。

; 色を表す列挙型
#enum Color
  Red
  Green
  Blue
#endenum

; 値を指定することもできる
#enum Priority
  Low = 1
  Medium = 5
  High
  Critical = 100
#endenum
値を省略すると 0 から順番に自動で番号がつきます。途中で値を指定すると、その次からはその値 +1 になります。

18. switch と三項演算子

switch 文

値で分岐するとき、if / elseif の連続よりも見やすく書けます。

switch x
  case 1
    print "one"
  case 2
    print "two"
  default
    print "other"
endswitch
NHSP の switch は C# と違い、自動的に break が入ります。フォールスルー (次の case に落ちる) は発生しません。

三項演算子

1行で「もし〜なら A、そうでなければ B」を書けます。

; 大きい方を返す
dim int result = a > b ? a : b

; null チェック
dim string name = s ?? "名無し"

NHSP

return a > b ? a : b
return s ?? "default"

C#

return a > b ? a : b;
return s ?? "default";

19. ビット演算と文字列補間

ビット演算子

ハードウェア制御やフラグ操作でよく使います。HSP の &, | と同じ考え方です。

dim int flags = $FF
dim int masked = flags & $0F   ; AND: 下位4ビットだけ残す → $0F
dim int combined = $0F | $F0   ; OR: 合成 → $FF
dim int toggled = flags ^ $0F  ; XOR: 反転 → $F0
dim int inverted = ~flags       ; NOT: 全ビット反転
dim int shifted = 1 << 4        ; 左シフト: 1 → 16
dim int right = 256 >> 3        ; 右シフト: 256 → 32

文字列補間

$"..." の中で {式} を書くと、その部分に値が埋め込まれます。変数名だけでなく、関数呼び出しやプロパティアクセスも使えます。

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
$"..." 内の {} には変数、関数、プロパティ、計算式が書けます。C# の補間文字列とほぼ同じです。 数値は自動的に str() で文字列に変換されます。

typeof

型の情報を取得できます。

dim t = typeof(int)
print t.Name   ; → "Int32"

インクリメント / デクリメント

x++x += 1x--x -= 1 のショートカットです。

dim int i = 0
i++   ; i = 1
i++   ; i = 2
i--   ; i = 1

20. 型チェックとイベント

is / as 演算子

is で型チェック、as で安全なキャストができます。

; is: 型が合っているか調べる
if obj is string
  print "文字列です"
endif

; as: キャストを試みる (失敗したら null)
dim string s = obj as string
dim string result = s ?? "文字列ではない"

イベント

.NET のイベント機能を使えます。ボタンのクリックなど、通知の仕組みに使います。

#class public Button
  ; EventHandler 型のイベントを宣言
  #event public EventHandler Click
#endclass

デフォルト引数

引数にデフォルト値を設定できます。呼び出し時に省略可能になります。

#func public static string Greet, string name = "World", int count = 1
  return name
#endfunc

21. オブジェクトの生成と破棄

static コンストラクタ

クラスが初めて使われるときに1回だけ実行されます。static フィールドの初期値設定に便利です。

#class public Config
  #field public static int MaxRetry

  ; クラスが初めて使われるときに実行
  #init static
    MaxRetry = 3
  #endinit
#endclass

デストラクタ

オブジェクトがメモリから解放されるときに実行されます。ファイルハンドルなどの後片付けに使います。

#class public FileHandler
  #destructor
    ; ファイルを閉じるなどの後処理
  #enddestructor
#endclass
デストラクタは GC のタイミングで呼ばれるので、確実に解放したい場合は using 文を使いましょう。

Nullable 型 (int?)

値型に ? をつけると null を入れられる型になります。

; int? は「整数か null」を表す型
dim int? value
value = 42
if value.HasValue
  print value.Value
endif