スクリプトむンタヌプリタヌずスタックマシンの䜜成



この蚘事では、かなり珍しいプロゞェクトに焊点を圓おたす。 あるスクリプト蚀語の独自のむンタプリタずそれを実行するマシンを曞きたいずいう願望に私が蚪れたこずがありたす。 内郚でどのように機胜するかを確認するだけです。 この目暙はさほど気高くありたせん。私は長い箱でアむデアを先送りしたした。 もっず䟿利な蚀葉遣いが欲しかった。

か぀お、私の友人がWSHで自動化スクリプトを曞く必芁があるず䞍満を蚀っおいたしたが、圌はVBScriptずJavascriptのどちらも知りたせんでした。 それから「高貎な」定匏化はそれ自身で生じた あなたは友人を助ける必芁がある。 その結果、コンパむラず実行マシンが䜜成され、VBScriptずJSに頌らずにWindows Script Hostのスクリプトを実行できるようになりたした。 カットの䞋-プロゞェクトずその内郚構造ずプログラミング蚀語自䜓の簡単な背景。



簡単な背景



玳士、私はあなたにひどい秘密を教えたす。 そのような眪を告癜するこずは非垞に恥ずかしいこずですが、それにもかかわらず、私は告癜したす。 私はワン゚スニックです。 はい、はい、はい、私は黄色のフレヌムワヌクを䜿甚しお倧小の決定を曞きたすが、私の良心は私を悩たせたせん。



1Cに加えお、私は他の蚀語を知っおいたすが、私は若いので、䜙裕がありたす。 反察に、序論で蚀及した私の知り合いは、軍の退職者であり、優れた電子技術者であり、圌が民間生掻に入ったずきにプログラマヌずしお再蚓緎されたした。 有胜な技術者にずっお、そのような再蚓緎は倧したこずではなく、圌らは1Cの分野でうたくいき、優秀な専門家はたったく良いです。 しかし、幎霢ずずもに、新しいテクノロゞヌを远いかけるこずはたすたす難しくなり、急速に倉化するITの䞖界をフォロヌするこずはたすたす難しくなっおいたす。 䌚話が行われたずき、圌は次のようなこずを蚀いたした。「はい、新しいスクリプトを1Cでしか曞けないのにコン゜ヌルで実行できるようになるず、新しい蚀語を習埗するには長い時間がかかりたす...」むンタプリタは、玔粋に研究から完党に適甚されたオリ゚ンテヌションを受け取りたした-WSHむンフラストラクチャで動䜜するが、1C蚀語のスクリプトの実行。



プログラミング蚀語に぀いお少し



1C蚀語に぀いおは、promtによっお翻蚳されたVisual Basicであるず圌らは蚀いたす。 本圓にそうです。 粟神的には、蚀語はVBに非垞に近い-それは匱い型付け、OOPなし、ラムダ匏およびクロヌゞャを持っおいたす。 「音節」構文ではなく「蚀語」構文がありたす。 さらに、すべおのキヌワヌドには察応する英語があるため、英語の甚語で曞かれた1Cコヌドを䞀芋しおVBず区別するこずはほずんど䞍可胜です。 それはコメント蚘号です-BASICのようにアポストロフィではなく、2぀のスラッシュです。

この蚀語は、プロシヌゞャず関数を区別し、叀兞的な「For」ルヌプず「While」ルヌプ、および反埩子ルヌプ「For Each ... In」を備えおいたす。 オブゞェクトのプロパティずメ゜ッドぞのアクセスは「ポむントを介しお」実行され、配列ぞのアクセスは角括匧で囲たれたす。 各ステヌトメントはセミコロンで終わりたす。



「スタックマシン」ずは䜕ですか



私の知る限り、スタックドマシンが最も䞀般的です。 たずえば、.NET CLRおよびJVM仮想マシンはスタックされたす。 habrには、このビゞネスに関する別の蚘事もありたす。 それでも、リンクを介しお読者を远いかけないために、ここで圌らの仕事の原理を説明する䟡倀があるず思いたす。 スタックマシンは、スタックずしお線成されたデヌタに察しおすべおの操䜜を実行したす。 各操䜜は、必芁な数のオペランドをスタックからフェッチし、それらに察しおアクションを実行し、結果をスタックにプッシュしたす。 このアプロヌチにより、最小限のコマンドで軜量のバむトコヌドを䜜成できたす。 さらに、非垞に高速に動䜜したす。

バむトコヌドは、マシンが実行する呜什のセットです。



各呜什は、0〜255のシングルバむトのオペレヌションコヌドで、その埌にレゞスタやメモリアドレス Wikipedia などのパラメヌタヌが続きたす。


私たちのマシンでは、チヌムの長さは固定されおおり、操䜜コヌドず操䜜匕数の2぀の数字で構成されおいたす。 各コマンドは、スタック内のデヌタに察する䞀連のアクションを意味したす。

たずえば、加算挔算「1 + 2」の擬䌌コヌドは次のようになりたす。



Push 1 Push 2 Add
      
      





最初の2぀のチヌムは条件をスタックに配眮し、3番目のチヌムは远加を実行しお結果をスタックに配眮したす。 この堎合、割り圓お操䜜「A = 1 + 2」は次のようになりたす。



 Push 1 ;    "1" Push 2 ;    "2" Add ;    "1"  "2",      . LoadVar A ;         ""
      
      





PushコマンドずLoadVarコマンドには匕数があり、Addコマンドには匕数が必芁ないこずがわかりたす。

スタックコンピュヌティングの利点は、操䜜が実行される順序で実行されるこずであり、操䜜の優先順䜍に泚意を払う必芁はありたせん。 曞かれたものが満たされたす。 操䜜を実行する前に、そのオペランドがスタックにプッシュされたす。 この匏の蚘述方法は、「 逆ポヌランド蚘法 」ず呌ばれたす



スタックされたマシンを䜜成するタスクは、このマシンが理解する必芁な䞀連の呜什を「発明する」こずです。



コンパむラデバむス



コンパむラのタスクは、指定された蚀語のコヌドを、それを実行するマシンのバむトコヌドに倉換するこずです。 クラシックコンパむラには3぀のコンポヌネントがありたす。



字句アナラむザは、入力文字の無秩序なストリヌムをトヌクンに分割したす。 圌は、゜ヌステキストから単語、数字、文字列定数、操䜜の兆候を抜出し、それらをアトミック゚ンティティに倉換したす。これは、今埌の䜜業に䟿利です。



パヌサヌは、字句解析プログラムが付䞎したトヌクンのセットが意味のあるものであるこず、プログラムがあり、意味のない文字のセットではないこずを確認したす。



アナラむザヌは、 抜象構文ツリヌ ASTを構築し、コヌドゞェネレヌタヌの入力に䟛絊したす。

コヌドゞェネレヌタヌは、構文ツリヌのノヌドをバむパスしおバむトコヌドを䜜成したす。



同じ3リンクスキヌムを䜿甚しおコンパむラの蚘述を開始したしたが、このタスクの䞀環ずしお䞍必芁に修正し、䞍必芁に思われたした。 そのため、ASTを攟棄し、解析ずコヌド生成を組み合わせたした。

ルヌプ内のコンパむラは、パヌサヌに次のトヌクンを芁求し、それを芋お、コヌドを生成したす。 途䞭で、このコンテキストでトヌクンが蚱可されおいるこずを確認したす。 このスキヌムのASTは冗長です。



ステヌトマシン


パヌサヌずコンパむラヌをステヌトマシンずしお実装する方が䟿利です。 次の文字を抜出するずき、この文字を解釈するために切り替える必芁があるモヌドに぀いお結論が䞋されたす。 たずえば、文字が入力に衚瀺された堎合、単語読み取りモヌドに切り替えたす。数字が衚瀺された堎合、数字モヌドで読み取りたす。 各状態は、有効な入力文字のセットを定矩したす。 トヌクンを抜出した埌、マシンは前の状態に移行したす。 コヌド生成あり-同様に。 入力IFに衚瀺された埌、条件生成モヌドに切り替え、最埌に前の状態に戻りたす。



仮想マシンデバむス



可芖性コンテキスト
すべおの倉数ずメ゜ッドは垞に䜕らかのコンテキストに属したす。 メ゜ッド内には、グロヌバルコンテキスト、モゞュヌルコンテキスト、およびロヌカルコンテキストがありたす。 実行時にアクセスできる利甚可胜な可芖の名前のスタックが刀明したす。 このスタックは、コンパむルおよび実行時にアクティブに動䜜しおいたす。

さらに、アむデアを開発し、 contextsのむンスタンスを䜜成できるず想定するず、モゞュヌル性が自然に発生したす。 メ゜ッドや倉数のセットなど、特定のモゞュヌルがある堎合、このモゞュヌルのすべおのコヌドが機胜するコンテキストがありたす。 ここで、同じモゞュヌルの2぀のむンスタンスを䜜成する堎合、実際には同じクラスの2぀のむンスタンスを䜜成したす。 それぞれが独自のコンテキストを参照し、独自の状態を持ちたす。

1Cのドキュメントではこれを盎接述べおいたせんが、芳察された動䜜から、1Cランタむムでのモゞュヌルの実行はこのように機胜するず結論付けるこずができたす。 ランタむムはオブゞェクトのむンスタンスでは動䜜したせんが、必芁に応じお仮想マシンに「接続」するコンテキストでは動䜜したす。



蚘憶
マシンの条件付き「メモリ」は、マシンに接続されおいるコンテキストのリストです。 各コンテキストには、独自の番号ず状態倉数の特定の倀がありたす。 倉数を読み曞きするコマンドには、コンテキストテヌブルにセル番号の圢匏の匕数がありたす。 衚の各゚ントリは、アクションが実行されるコンテキスト内のコンテキスト番号ず倉数番号を説明しおいたす。

玢匕 コンテキスト番号 可倉数
0 0 0
1 0 1
2 1 3


たずえば、コマンドPushVar 1が受信された堎合、むンデックス1のテヌブルでは、コンテキスト0から倉数番号1を取埗するずいうレコヌドが遞択されたす。

倉数は、接続された各コンテキストがマシンに䌝える単玔な配列です。 マシンはこの配列からデヌタを読み曞きし、接続されたコンテキストの状態を倉曎したす。 どのようなコンテキストが接続されおいるかは関係ありたせん-䜕らかのクラスたたはグロヌバルコンテキストのむンスタンスです。 マシンは倉数を倉曎できたすが、それは重芁ではない倉数の皮類です。



呌び出し履歎


コヌド実行は、いわゆる「フレヌム」を䜿甚しお線成されたす。各「フレヌム」は、ロヌカル倉数のセットず珟圚の呜什ぞのポむンタヌです。 メ゜ッドが呌び出されるず、珟圚のフレヌムがスタックにプッシュされたす。 したがっお、呌び出し時のマシンの状態が保存されたす。



次に、ロヌカル倉数の空の配列ず呌び出されたメ゜ッドの先頭に等しい呜什番号で新しいフレヌムが圢成されたす。 この新しいフレヌムは珟圚のものになり、そこからコマンド実行のサむクルが続きたす。

Returnコマンドに到達するず、珟圚のフレヌムは範囲倖になったロヌカル倉数の倀ずずもに砎棄され、以前の状態で保存されたフレヌムがコヌルスタックから取埗され、コマンド実行サむクルがコヌルポむントから続行されたす。



メ゜ッド呌び出しを䌎う擬䌌コヌドのフラグメント。

 0: Push 1 1: Push 2 2: Add 3: Return 4: Nop 5: Nop 6: Call 0 7: LoadVar
      
      





珟圚の呜什が6番であるずしたす。これはアドレス0ぞの呌び出しです。ロ​​ヌカル倉数ず珟圚の呜什番号は呌び出しスタックに栌玍されたす。 次に、制埡はアドレス0に転送され、次にアドレス6に戻り、次の呜什にさらに移行したす。



チヌムの実装


コマンドの実装はすべおMachineInstanceクラスのメ゜ッドであり、これらのメ゜ッドぞのポむンタヌはオペレヌションコヌドの配列に配眮されたす。 次のコマンドを配列からの番号で取埗するずき、実装ぞのポむンタヌが抜出され、この実装が実行されたす。 途䞭で、䟋倖凊理が実行されたす。

コマンド実行のメむンサむクル
 private void ExecuteCode() { while (true) { try { MainCommandLoop(); break; } catch (RuntimeException exc) { if (_exceptionsStack.Count == 0) throw; var handler = _exceptionsStack.Pop(); SetFrame(handler.handlerFrame); _currentFrame.InstructionPointer = handler.handlerAddress; _lastException = exc; } } } private void MainCommandLoop() { try { while (_currentFrame.InstructionPointer >= 0 && _currentFrame.InstructionPointer < _module.Code.Length) { var command = _module.Code[_currentFrame.InstructionPointer]; _commands[(int)command.Code](command.Argument); } } catch (RuntimeException) { throw; } catch (Exception exc) { throw new ExternalSystemException(exc); } }
      
      









実行可胜ナニットデバむス



このマシンは、1぀の数倀匕数を持぀コマンドを䜿甚したす。 各コマンドは、操䜜コヌドず匕数によっお決定され、その解釈はコマンド自䜓に䟝存したす。 コマンドのシステム党䜓ず実行可胜モゞュヌルの構造は、この原則に基づいおいたす。

最初に、1Cの゜ヌスモゞュヌルの構成を怜蚎したす。

3぀の異なるセクションがありたす。





モゞュヌルの本䜓は本質的に名前のないメ゜ッドであるため、倉数の宣蚀ず実行可胜コヌドずいう2皮類の異皮構造しか存圚しないず蚀えたす。



定数


明らかに、すべおの操䜜はいく぀かの倀で実行されたす。 次に、倀は倉数ず定数で衚されたす。 定数には、数字、文字列、日付、キヌワヌド



、



および



リテラルが含たれたす。

2ず2をコンピュヌタヌに远加するには、「2」ずは䜕か、どこで入手できるかを説明する必芁がありたす。 この目的のために、コヌドで䜿甚されるすべおの定数の説明は、コンパむルされたモゞュヌルに含たれおいたす。 コヌドに「 = ""



」ず衚瀺されおいる堎合、この単語「Hello」は定数のリストに含たれおいるはずです。



倉数


倉数名は、スコヌプを蚱可するためにコンパむル時にのみ重芁です。 実行時には、倉数名は必芁ありたせん。 倉数の配列にメモリを割り圓おるには、それらの数のみを知るだけで十分です。 マシンが実行されるず、倉数の配列が䜜成され、番号でアクセスされたす。 コンパむルされたモゞュヌルでは、倉数の数のみが瀺されたす。

ただし、倉数を䜿甚するず、少し耇雑になりたす。 実際には、モゞュヌル倉数に加えお、モゞュヌル倖のどこかで宣蚀されおいるグロヌバル倉数もありたす。 䞊蚘のように-目に芋える名前のスタックがあり、コンパむラは、ある名前に䌚うず、宣蚀された名前のスタックでそれを探したす。 理論的には、いく぀かの独立したラむブラリを独自のプロパティず関数で接続できたす。これらはクラむアントスクリプトに衚瀺されたす。



コヌドで倉数が䜿甚されおいる堎合、マシンはその倉数がどのコンテキストに属しおいるかを知る必芁がありたす。 チヌムには数倀匕数が1぀しかないため、この倉数が宣蚀されるコンテキストを1぀の匕数を䜿甚しお決定する必芁がありたす。 この目的のために、 倉数のテヌブルがモゞュヌルに導入されたした。 テヌブルの各゚ントリには、コンテキスト内のコンテキスト番号ず倉数番号が含たれたす。 コマンド匕数は、倉数テヌブル内のレコヌド番号ずしお扱われたす「メモリ」セクションを参照。

スタック倉数操䜜
 private void PushVar(int arg) { var vm = _module.VariableRefs[arg]; var scope = _scopes[vm.ContextIndex]; _operationStack.Push(scope.Variables[vm.CodeIndex]); NextInstruction(); } private void LoadVar(int arg) { var vm = _module.VariableRefs[arg]; var scope = _scopes[vm.ContextIndex]; scope.Variables[vm.CodeIndex].Value = BreakVariableLink(_operationStack.Pop()); NextInstruction(); }
      
      





方法


メ゜ッドはプロシヌゞャずファンクションに分けられたす 。 埌者は倀を返す堎合がありたす。 デフォルトでは、パラメヌタヌは参照によっおメ゜ッドに枡されたす。 倀で枡すには、メ゜ッドパラメヌタをキヌワヌド「Znach」で瀺す必芁がありたすBASICのByValに類䌌。

コンパむルされたモゞュヌルには、メ゜ッドのパラメヌタヌ、それらのバむンディング、戻り倀の存圚などに関する情報が含たれおいたす。 各メ゜ッドには独自の番号がありたす。 さらに、各メ゜ッドに぀いお、このメ゜ッドのロヌカル倉数の数が瀺されたす。

倉数などのメ゜ッドは、モゞュヌルの倖郚にある぀たり、グロヌバルに宣蚀されおいるこずができたす。 メ゜ッドぞのアクセスは、倉数の堎合ず同じ方法で線成されたす-呌び出されたメ゜ッドが眮かれおいるコンテキストを定矩する察応衚を通じお。



モゞュヌルの最終構造


䞊蚘のすべおを組み合わせお、コンパむル枈みモゞュヌルの次の構造を取埗したす。



コンパむル埌、モゞュヌルは次を含む構造になりたす。

  1. モゞュヌルレベル倉数の数
  2. 定数のリスト
  3. メ゜ッド眲名リスト
  4. モゞュヌルのバむトコヌド
  5. 可倉マップ
  6. メ゜ッドマップ
  7. モゞュヌルモゞュヌル本䜓ぞの゚ントリポむントであるメ゜ッドの番号




倀を操䜜する



1C蚀語には厳密な型指定はありたせん。 倉数は、倀が割り圓おられるず型を取埗したす。 任意の倀は、本質的にナニバヌサルタむプVARIANTです。 操䜜を実行するずき、ナニバヌサル倀は目的のタむプで䞎えられたす。 たずえば、算術挔算が数倀にキャストされるずき、およびブヌル挔算-ブヌル倀にキャストされるずき。

次の基本的な倀タむプが存圚したす。

  1. 未定矩
  2. ひも
  3. 数
  4. 日付
  5. ブヌル倀
  6. 察象
  7. 皮類


埌者は、型を操䜜するためのプリミティブ型です.NETのSystem.Type



アナログ

ナニバヌサル倀は、 IValue



むンタヌフェむスで衚されたす。

 interface IValue : IComparable<IValue>, IEquatable<IValue> { DataType DataType { get; } TypeDescriptor SystemType { get; } double AsNumber(); DateTime AsDate(); bool AsBoolean(); string AsString(); TypeDescriptor AsType(); IRuntimeContextInstance AsObject(); }
      
      





このむンタヌフェむスを䜿甚するず、倀の実際のタむプを確認したり、基本的なタむプにキャストしたりできたす。 このような削枛は、たずえば算術挔算を実行するために必芁です。 特定のタむプの倀を実装するクラス自䜓は、その倀を各基本タむプにキャストしようずしたす。

匏を評䟡する堎合、最終結果のタむプは、最初のオペランドのタむプによっお決たりたす。 したがっお、匏"12345" + 10



は文字列の結果を生成するはずです。 加算を実行するず、2番目の匕数が文字列にキャストされ、連結が実行されたす。

察照的に、操䜜10 + "12345"



は、文字列 "12345"を数倀にキャストしようずしたす。 このキャストが䞍可胜な堎合、䟋倖「Number型ぞのキャスト゚ラヌ」が発生したす。

䞊蚘の䟋では、Number型を実装するクラスでAsStringメ゜ッドが呌び出され、String型を実装するクラスでAsNumberメ゜ッドが呌び出されたす。



加算操䜜
 private void Add(int arg) { var op2 = _operationStack.Pop(); var op1 = _operationStack.Pop(); var type1 = op1.DataType; if (type1 == DataType.String) { var result = op1.AsString() + op2.AsString(); _operationStack.Push(ValueFactory.Create(result)); } else if (type1 == DataType.Date && op2.DataType == DataType.Number) { var date = op1.AsDate(); var result = date.AddSeconds(op2.AsNumber()); _operationStack.Push(ValueFactory.Create(result)); } else { //    . var result = op1.AsNumber() + op2.AsNumber(); _operationStack.Push(ValueFactory.Create(result)); } NextInstruction(); }
      
      







オブゞェクトのプロパティずメ゜ッドぞのアクセス


各オブゞェクトには、「ポむントを介しお」アクセスできるプロパティずメ゜ッドを含めるこずができたす。 呌び出しは名前で実行されたす。 名前でアクセスする仕組みは、オブゞェクトに必芁なメンバヌがあり、それらにアクセスできるかどうかを確認できる特別なむンタヌフェむスで衚されたす。

 interface IRuntimeContextInstance { bool IsIndexed { get; } IValue GetIndexedValue(IValue index); void SetIndexedValue(IValue index, IValue val); int FindProperty(string name); bool IsPropReadable(int propNum); bool IsPropWritable(int propNum); IValue GetPropValue(int propNum); void SetPropValue(int propNum, IValue newVal); int FindMethod(string name); MethodInfo GetMethodInfo(int methodNumber); void CallAsProcedure(int methodNumber, IValue[] arguments); void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue); }
      
      





プロパティたたはメ゜ッドにアクセスするずき、オブゞェクトはこのプロパティ/メ゜ッドの番号を求められたす。 次に、この番号によっお、プロパティの可読性ず可読性、メ゜ッドパラメヌタの数、戻り倀の存圚などに぀いおリク゚ストが行われたす。 この情報を芋぀けた埌、呌び出しが行われたす。

プロパティの堎合、これは倀の蚭定たたは読み取りです。 メ゜ッドの堎合、プロシヌゞャずしおの呌び出したたは関数ずしおの呌び出し。 埌者の堎合、返された倀は仮想マシンスタックにプッシュされたす。



コンテキストずしおオブゞェクトを操䜜する、たたはその逆


䞊蚘で、 コンテキストのむンスタンスずしおモゞュヌル性ずオブゞェクトの䜜成に蚀及したした。 意味の詳现ず、「ポむントを介した」オブゞェクトぞのアピヌルがどのように構成されおいるかを詳しく芋おみたしょう。



関数の特定のラむブラリがあるず想像しおください。 このラむブラリから関数を呌び出しお、結果を楜しむこずができたす。 ここで、ラむブラリがパブリックメ゜ッドずプロパティのセットを持぀クラスのむンスタンスであるず想像しおください。 このむンスタンスは、知芚できないようにスコヌプに「埋め蟌たれ」おいるため、むンスタンスメ゜ッドをグロヌバル関数であるかのように盎接呌び出したす。



" MathLibrary



"クラスのむンスタンスがスコヌプスタックに接続されおいる堎合、Sin、Cos、およびSqrt関数を盎接呌び出すこずができたす。 これらは、ラむブラリレベルのどこかで宣蚀された通垞のメ゜ッドずしお衚瀺されたす。



  = Sin(X); //  Sin   ,      ,       .
      
      







次に、逆の操䜜を行いたしょう。 䜕らかのスクリプトを蚘述した堎合、それ自䜓がコンテキストです。 コヌドが機胜する範囲を提䟛したす。 しかし、このスクリプトのむンスタンスを䜜成しお倉数に曞き蟌むずどうなりたすか このスクリプトは独自のプロパティずメ゜ッドを持぀クラスであり、オブゞェクトのように操䜜できるこずがわかりたした。 倖郚スクリプトファむルを接続むンポヌトする機胜は、この原則に基づいお構築されおおり、オブゞェクトの堎合ず同様に動䜜したす-むンスタンスの䜜成、メ゜ッドの呌び出しなど。

同時に、スクリプトAがスクリプトBを呌び出すず、Bが唯䞀のスクリプトであるかのようにコヌドBのコンテキストず実行が進行するため、スクリプトBがマシンのメモリに接続されたす。 モゞュヌルBから戻るず、切断され、実行はスクリプトAに進みたす。



バむトコヌドの䟋



以䞋は、バむトコヌド内の特定の操䜜の実行がどのように線成されるかの䟋です。



远加ず割り圓お
  = 1;  = 2;  =  + ;
      
      





 .constants 0 :type: Number, val: 1 1 :type: Number, val: 2 .code 0 :(PushConst 0) ;   0   1 :(LoadLoc 0) ;       0 2 :(PushConst 1) ;   1   3 :(LoadLoc 1) ;       1 4 :(PushLoc 0) ;     0 5 :(PushLoc 1) ;     1 6 :(Add 0) ;       7 :(LoadLoc 2) ;       2
      
      





前提条件ルヌプ実行䞭
  = 1;   < 5   =  + 1; ;
      
      





 .constants 0 :type: Number, val: 1 1 :type: Number, val: 5 .code 0 :(PushConst 0) 1 :(LoadLoc 0) ;      2 :(PushLoc 0) ;   ( ) 3 :(PushConst 1) 4 :(Less 0) ;   ""   №1 ( 5) 5 :(JmpFalse 11) ;    - ,      ( 11) 6 :(PushLoc 0) ;   =  + 1 7 :(PushConst 0) 8 :(Add 0) 9 :(LoadLoc 0) 10 :(Jmp 2) ;        11 :(Nop 0) ;  
      
      





状態
  1 > 2   = 1;   = 0; ;
      
      





 .constants 0 :type: Number, val: 1 1 :type: Number, val: 2 2 :type: Number, val: 0 .code 0 :(PushConst 0) 1 :(PushConst 1) 2 :(Greater 0) ;   "" 3 :(JmpFalse 7) ;    -    "" 4 :(PushConst 0) ;    5 :(LoadLoc 0) 6 :(Jmp 9) ;   ,     7 :(PushConst 2) ;    8 :(LoadLoc 0) 9 :(Nop 0);  
      
      





メ゜ッド呌び出し
 ("");
      
      





 .constants 0 :type: String, val:  .code 0 :(PushConst 0) ;     1 :(ArgNum 1) ;      2 :(CallProc 1) ;  ,        1 .procmap 0 :(1,2) 1 :(0,4) ;  1    0     4.
      
      







WSHに぀いおはどうでしたか



WSHスクリプトむンフラストラクチャは、倚数のCOMオブゞェクトによっお衚されたす。 これらのオブゞェクトは、IDispatchを䜿甚しお名前でメンバヌにアクセスするこずにより、スクリプト蚀語からアクセスできたす。 䜕にも䌌おいたせんか

IDispatchの小さなラッパヌを䜜成するだけで十分です。これにより、マシンは前述のIRuntimeContextInstanceむンタヌフェむスを介しおこれらのCOMオブゞェクトを操䜜できたす。



図は、スクリプトのデバッグに䜿甚したテストアプリケヌションりィンドりを瀺しおいたす。Scripting.FileSystemObjectオブゞェクトを䜿甚しおドラむブラベルを䞀芧衚瀺するスクリプトを実行したす





もちろん、珟時点では、すべおのWSH機胜が完党に実装されおいるわけではなく、この゚ンゞンを完党に眮き換えるこずはできたせん。たずえば、リモヌトマシンでWSHオブゞェクトを䜜成する方法はありたせん。さらに深く掘り䞋げるず、さらに倚くのこずができたす。それにもかかわらず、ほずんどのロヌカルタスクでは、この゚ンゞンは非垞に適しおいるように思えたす。



゜ヌスずもの



プロゞェクト党䜓は、2぀のアプリケヌションず、車の゚ンゞンを備えたdllで構成されおいたす。

TestAppアプリケヌションは、GUIベヌスの補助ツヌルであり、゚ンゞンを詊し、テストスクリプトを蚘述し、実行を実行できたす。

oscriptコン゜ヌルアプリケヌションは、メむンのコマンドラむンツヌルです。

パラメヌタなしで実行するず、サポヌトされおいるコマンドoscriptが衚瀺されたす。

たた、スクリプトず実行䞭のアプリケヌションを1぀のモゞュヌルに結合しお、個別のexeファむルを取埗するずいう考え方もありたす。しかしそれたで、圌の手は届きたせんでした。

゜ヌスコヌドはbitbucketで入手できたす。そこには、利甚可胜な蚀語ツヌルに関する短いりィキがありたす。

動䜜を確認したいが、゜ヌスからコンパむルしたくない人-setup.exe



短い結論



プロゞェクトのフレヌムワヌク内で、1C蚀語のスクリプトのむンタヌプリタヌが開発されたした。これには、1Cのスクリプトずトランスレヌタヌを実行しお仮想マシンのバむトコヌドに倉換するスタック仮想マシンが含たれたす。

パフォヌマンスは元のものずほが同等です。これが膝の䞊で䜜られたアマチュアのプロゞェクトであるず考えるなら、結果は良いず考えるこずができるず思いたす。私は速床に぀いおの真剣な研究は行いたせんでした。興味のために、数孊ず倚数の速床を比范するこずができたすが、これはすでに少し別のトピックです。

この䜜品が皆さんにずっお興味深いものであったこずを願っおいたす。プロゞェクトを分岐し、批刀しおください、私は喜んでいたす。がんばっお。



PS 1Cに関する蚘事ぞのコメントには、「ロシア語で挔算子を曞くにはどうすればよいですか」ずいうメッセヌゞがありたす。



All Articles