Upgrade to Pro — share decks privately, control downloads, hide ads and more …

軽率にプログラミング言語のシンタックスについて考えてみよう / lets-think-abou...

軽率にプログラミング言語のシンタックスについて考えてみよう / lets-think-about-programming-lang-syntax

Iwaken Lab.プログラミング言語入門座談会で登壇した資料です

Avatar for にー兄さん

にー兄さん

July 05, 2025
Tweet

More Decks by にー兄さん

Other Decks in Programming

Transcript

  1. にー兄さん ソフトウェアエンジニア@HoloLab inc. Babylon.js勉強会運営 Iwaken Lab. Microsoft MVP for Dev

    Tech 好きな言語 TypeScript / C# / Kotlin CoffeeScript as a 思ひ出 アカウント @ninisan_drumath @drumath2237
  2. プログラムが実行されるまで(一例) コンパイラ言語の一例を示す。あくまで一例 1. 字句解析(tokenize) 1. トークン列を生成 2. 構文解析(parse) 1. 抽象構文木を生成

    3. アセンブリや中間表現に変換生成 1. ILやIR、WASMなど 2. ネイティブバイナリ 4. ランタイム環境によって実行 今のインタプリタ言語はJITコンパイラ搭載のものや、 実行前に中間表現に変換してVMで動かすものがあったりするので もしかしたら純粋なインタプリタって少なくなってるのかも
  3. 例)RustからWASMを生成してWebブラウザで実行する 1. Rustのコードを字句解析・構文解析によりASTを得る 1. ここはrustcやwasm-packが担当 2. ASTをWASMに変換する 1. AST ->

    HIR -> MIR -> LLVM IR -> WASMという流れで生成 3. ChromeブラウザのJSエンジンがスタックマシンとして実行(実際はレジスタマシン的な挙動になるらしい けど) 1. V8やSpiderMonkeyなど add関数のWASM(WAT形式) (module (func $add (export "add") (param $a i32) (param $b i32) (result i32) local.get $a local.get $b i32.add ) )
  4. (拡張)BNFによる構文の定義 BNF(バッカス・ナウア記法)と呼ばれる方法がある 言語構文の要素それぞれを再帰的に定義する 拡張BNFによる仮言語の構文の表現(ChatGPTにお願いした) ​ ​ program statement expr literal

    TYPE IDENT NUMBER ::= { statement } ::= let IDENT : TYPE = expr ::= literal ∣ if expr { expr } else { expr } ::= NUMBER ∣ true ∣ false ::= i32 ∣ bool ::= letter { letter ∣ digit ∣ _ } ::= digit { digit } let a: i32 = 1 let b: bool = true let c: i32 = if b { 1 } else { 0 }
  5. 抽象構文木(AST: abstract syntax tree) プログラムは木構造である! let変数宣言文 変数名( "a" ) 型(

    i32 ) 式 リテラル(数値リテラル 1 ) 構文を木構造で表したものを AST(抽象構文木)という Let Ident(a) Colon Type(i32) Equal Number(1)
  6. 例)再帰下降構文解析 手軽な構文解析のアルゴリズム 仮言語はLL(1)言語というカテゴリで 再帰下降構文解析ができる BNFをそのままコードに落とし込んだような 見た目で再帰処理によってASTを構築できる ​ ​ program statement

    expr literal TYPE IDENT NUMBER ::= { statement } ::= let IDENT : TYPE = expr ::= literal ∣ if expr { expr } else { expr } ::= NUMBER ∣ true ∣ false ::= i32 ∣ bool ::= letter { letter ∣ digit ∣ _ } ::= digit { digit }   parseProgram() { const statements = []; while (this.peek().type !== "EOF") { statements.push(this.parseStatement()); } return statements; } parseStatement() { this.consume("Let"); const nameToken = this.consume("Ident"); this.consume("Colon"); const typeToken = this.consume("Type"); this.consume("Equal"); const expr = this.parseExpr(); return { type: "LetStatement", name: nameToken.value, varType: typeToken.value, value: expr, }; } parseExpr(): any { const token = this.peek(); if (token.type === "If") { this consume("If");
  7. let a: i32 = 1 let b: bool = true

    let c: i32 = if b { 1 } else { 0 }
  8. TypeScript Compiler APIによる構文解析 TypeScriptはコンパイラのAPIを提供している ASTの生成からトラバースまで、簡単に使いやすいAPIがあってよい 自前のコード生成などに活用できる   p yp

    p ; const source = `let x = 1 + 2;`; const sourceFile = ts.createSourceFile( "example.ts", source, ts.ScriptTarget.Latest, true // setParentNodes ); // ASTを走査してノードを出力 function visit(node: ts.Node) { console.log( `${ts.SyntaxKind[node.kind]}: "${node.getText(sourceFile)}"` ); ts.forEachChild(node, visit); } visit(sourceFile);
  9. C# Roslyn APIによる構文解析 C#のRoslynコンパイラでもASTの生成が可能 コード生成の場合はSource Generatorも使えるが、 CLIからコード生成をしたい場合などには活用できる   using

    Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; var code = "int x = 1 + 2;"; var tree = CSharpSyntaxTree.ParseText(code); var root = tree.GetRoot(); // ASTのルートノードをたどる foreach (var node in root.DescendantNodes()) { Console.WriteLine($"{node.Kind()}: {node}"); }
  10. 関連・参考 怖くないバッカスナウア記法(BNF)入門 https://qiita.com/h_sakurai/items/3cc328a6db8941ac6336 Wikipedia: 抽象構文木 https://ja.wikipedia.org/wiki/抽象構文木 Using the Compiler API

    https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API The .NET Compiler Platform SDK https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/