⚡

Goで作った自作言語をAWS Lambdaカスタムランタイムで実行する

に公開

はじめに

ふと思い立ってプログラミング言語を自作してみました。「Sugu」といいます。
JavaScriptに近い仕様のインタプリタ言語で、Goを使用して作っています。

Repo

https://github.com/n-yokomachi/sugu

言語仕様など

https://n-yokomachi.github.io/sugu/

自作といいつつ実装は全部Claude Code+Opus 4.5で行い、レビューも基本はClaude Codeにお任せして自分はざっくり見る感じで作っています。
もともと先に作ってからリバースエンジニアリングして勉強するつもりだったので、これからコードを読むターンです。

それはそれとして、AWS Lambda上でも動かせれば面白そうだなと思うのでやってみます。

自作言語「Sugu」の概要

Suguはインタプリタ言語で、REPL/ファイル実行をサポートしています。
👇こんな感じで実行できます。

# REPL(対話モード)を起動
$ sugu
>> mut x = 1 + 2;
>> outln(x);
3

# ファイルを実行
$ sugu hello.sugu

また簡単にSuguの仕様を紹介しておくと、まずデータ型・制御構文は以下の通り

データ型: `number`, `string`, `boolean`, `null`, `array`, `map`
制御構文: `if/else`, `while`, `for`, `switch`

インタプリタのアーキテクチャは以下の通り

入力コード → Lexer → Parser → AST → Evaluator → 結果

書き方はこんな感じです

// 変数宣言
mut x = 10;       // 再代入可能
const PI = 3.14;  // 再代入不可

// 関数定義
func add(a, b) => {
    return a + b;
}

// 出力
outln("Hello, Sugu!");
outln(add(1, 2));  // 3

ざっくりJSと同じ感覚で書ける感じです。
学習目的で作っているので特に思想とかはありません。
また、組み込み関数をはじめいろいろと足りていない要素はあるので、今後もアップデート予定です。

AWS Lambdaのカスタムランタイム

AWS Lambdaで通常サポートされていない言語を実行する場合カスタムラインタイムを使用します。

カスタムランタイムの仕組みについて詳しくはAWSのドキュメントを参照してください。
AWS Lambdaのカスタムラインタイムは2026年1月現在以下がサポートされています。

  • Amazon Linux 2023
  • Amazon Linux 2(2026年6月30日廃止予定)

カスタムラインタイムの場合エントリポイントはbootstrapという実行ファイルになるので、これを自作言語側に実装する必要がありますが、Goの場合はaws-lambda-go SDKがLambda Runtime APIとの通信を抽象化してくれるので、ハンドラー関数を書くだけでOKです。

Lambda対応の実装

自作言語側にAWS Lambda実行のための対応を入れます。
ここではハンドラーの実装、イベントの受け渡しのみをピックアップしていますが、元のSuguがCLI目的のため出力の制御なども別途入れています。

Lambdaハンドラー

main.suguファイルを読み込んで実行するハンドラーを実装します。

lambda/main.go
package main

import (
    "github.com/aws/aws-lambda-go/lambda"
)

func main() {
    lambda.Start(Handler)
}
lambda/handler.go
// main.suguの最後に評価された式の値がそのままLambdaのレスポンスになる
func Handler(ctx context.Context, event json.RawMessage) (interface{}, error) {
    // main.sugu を読み込む
    code, err := os.ReadFile("main.sugu")
    if err != nil {
        return map[string]string{"error": "failed to read main.sugu: " + err.Error()}, nil
    }

    return Execute(string(code), event)
}

イベントの受け渡し

Lambdaのテストイベントをevent変数として渡せるようにしました。
JSONをSuguのオブジェクト(Map, Array, Number, String, Boolean)に変換しています。

lambda/handler.go
// JSON → Sugu Objectの変換
func goValueToSuguObject(v interface{}) object.Object {
    switch val := v.(type) {
    case nil:
        return &object.Null{}
    case bool:
        return &object.Boolean{Value: val}
    case float64:
        return &object.Number{Value: val}
    case string:
        return &object.String{Value: val}
    case []interface{}:
        elements := make([]object.Object, len(val))
        for i, elem := range val {
            elements[i] = goValueToSuguObject(elem)
        }
        return &object.Array{Elements: elements}
    case map[string]interface{}:
        pairs := make(map[object.HashKey]object.HashPair)
        for k, v := range val {
            key := &object.String{Value: k}
            pairs[key.HashKey()] = object.HashPair{
                Key:   key,
                Value: goValueToSuguObject(v),
            }
        }
        return &object.Map{Pairs: pairs}
    default:
        return &object.Null{}
    }
}

ビルド

AWS Lambdaのカスタムラインタイムを使用するため、以下のビルド設定でビルドします。

# Linux/Mac
GOOS=linux GOARCH=amd64 go build -o bootstrap ./lambda
zip sugu-lambda.zip bootstrap

AWS Lambdaの設定・デプロイ

続いてLambda側の設定です。
Lambda関数の作成時にカスタムランタイムを選択して作成します。

Lambdaの作成後、bootstrapをzipにしたものをアップロードし、実行用のmain.suguファイルを作成します。

動作確認

以下のテストイベントを渡してみます。

{
  "name": "yoko"
}

ちゃんと動いてますね

おしまい

ということで自作言語を作ったついでにAWS Lambda上で動かしてみました。
なんとなくで始めた自作言語ですが結構面白いので今後もアップデートできればと思います。
実装している処理系への理解がまだ足りないのでそのあたりはコードを読みつつ勉強します。

GitHubで編集を提案

Discussion