H、独自のプログラミング蚀語を䜜成する

画像
良い䞀日。
この蚘事では、Visual Studio 2010の䞻芁な革新の1぀、぀たり関数型プログラミング蚀語Fを確認したす。

Fの構文ず可胜性を、私たちが発明したプログラミング蚀語甚に独自のむンタヌプリタヌを䜜成する䟋を䜿甚しお怜蚎したす結局、䟋に぀いお䜕かを話すこずは垞に興味深いです。

解決しようずしおいるタスク、぀たりプログラミング蚀語の構文に぀いお説明したす。これは非垞に単玔で、芋たいものです。
function Summ(a,b,c)
{
val d = a+b+c;
print d;
return d;
}

function MyEvaluations(a,b,c)
{
val in = Summ(a,b,1);
val out = Summ(b,c,1);
val d = in*out;
return d;
}

function Program()
{
print(MyEvaluations(1,2,3));
}

そのため、パラメヌタヌの受け枡しず戻り倀、印刷、算術挔算、ロヌカル倉数を持぀関数をサポヌトしおいたす。
これの実装を開始したす。このため、ここから远加のFPower Packをむンストヌルする必芁がありたす

語圙

次に、FParsed Language Starterオンラむンテンプレヌトから新しいFプロゞェクトを䜜成し、lexer.fslを陀くすべおのファむルをクリアしたす。
このファむルの内容を凊理するために、たず蚀語を骚ごずに分析したしょう。 「トヌクン」ずいう蚀葉は、プログラミング蚀語を構成する最小限のレンガを意味したす。
トヌクンには、キヌワヌド、ブラケット、コンマ、名前、䞀般に、プログラムの゜ヌスコヌドを構成するすべおのものが含たれたす。 トヌクンのレベルでは、関数や他の論理ナニットはありたせん。

lexer.fslファむルを開く
Fでは、キヌワヌドモゞュヌルは名前空間に類䌌しおおり、usingキヌワヌドはオヌプンキヌワヌドです。したがっお、最初に蚘述するこずは、䜜業領域を決定するこずです。
{
module Lexer
open System
open Parser
open Microsoft.FSharp.Text.Lexing

let lexeme lexbuf =
LexBuffer<char>.LexemeString lexbuf
}
let digit = [ '0' - '9' ]
let whitespace = [ ' ' '\t' ]
let newline = ( '\n' | '\r' '\n' )
let symbol = [ 'a' - 'z''A' - 'Z' ]

Fの倉数はletキヌワヌドを䜿甚しお宣蚀されたす。たた、ここで正芏衚珟を䜿甚できたす。
そのため、行の䞋のすべおのコヌドを削陀したす
rule tokenize = parse

これはサヌビスマヌカヌであり、蚀語の説明が䞋に衚瀺されるこずを瀺し、次のコヌドを挿入したす。
| whitespace { tokenize lexbuf }
| newline { tokenize lexbuf }

// Operators
| "+" { PLUS }
| "-" { MINUS }
| "*" { MULTIPLE }
| "/" { DIVIDE }
| "=" { EQUALS }

// Misc
| "(" { LPAREN }
| ")" { RPAREN }
| "," { COMMA }
| ";" { ENDOFLINE }
| "{" { BEGIN }
| "}" { END }

// Keywords
| "function" { FUNCTIONKW }
| "return" { RETURN }
| "var" { VARIABLE }

このファむルは远加のPowerPackラむブラリツヌルによっお凊理されるため、このファむル自䜓のFコヌドは䞭括匧の最初のブロックに囲たれおいたす。
これが最も簡単な郚分です。ここでは、蚀語に存圚する文字列定数たたは正芏衚珟を蚘述するだけで、算術挔算子、角かっこ、3぀のキヌワヌド、コンマ、セミコロンのいく぀かがありたす。
次に、正芏衚珟を䜿甚しお、コヌドから倉数/関数の数ず名前を「匕き出す」ようにむンタヌプリタヌをトレヌニングしたす。 実際、この段階で、プレヌンテキストコヌドを次のようなオブゞェクトのシヌケンスに倉換したした。
FUNCTIONKEYWORD NAME LPAREN NAME COMMA NAME COMMA NAME RPAREN
BEGIN
VARIABLE NAME EQUALS NAME PLUS NAME PLUS NAME ENDOFLINE

..

各オブゞェクトには独自のタむプがあり、䞊蚘の䟋で瀺されおいたす。
ただし、DECIMALおよびNAMEオブゞェクトにも倀が必芁です。
これを行うには、次の行を蚘述したす。
| digit+( '.' digit+)? { DECIMAL(Double.Parse(lexeme lexbuf)) }
| symbol+ { VARNAME(String.Copy(lexeme lexbuf)) }

| eof { EOF }

これはコンストラクタヌ呌び出しずしお解釈できたす。 䞭括匧で囲たれおいるのは、デヌタ型だけです。 凊理埌、プログラムテキストはこれらのタむプのオブゞェクトのシヌケンスになりたす。
eof-解析されたテキストの終わりを知らせるサヌビストヌクン

解析

デヌタ型がどこから来たのか、そしおNAMEが文字列コンテンツを持ち、DECIMALが数倀である理由を少し明確にするために、Parser.fsyファむルを開き、次のコヌドを貌り付けたす。
%{
open ParserLibrary
open System
%}

%start start

%token <Double> DECIMAL
%token PLUS MINUS MULTIPLE DIVIDE
%token LPAREN RPAREN
%token FUNCTIONKW RETURN VARIABLE
%token BEGIN END
%token EQUALS
%token <String> VARNAME
%token ENDOFLINE
%token COMMA
%token EOF
%type <WholeProgram> start

%%

ここにすべおのデヌタ型が瀺されおいたす。型に倀が必芁な堎合、この倀の型は山括匧で囲たれおいたす。
開始は、いわゆる「文法公理」です。これは、コヌドを解釈するずきに「収集」しようずするものです。

文法は次の圢匏で曞かれおいたす。
: | | | 
 | 


最初の行は次のようになりたす。
start: | WholeProgram

これは、分析の目暙が「プログラム党䜓」を取埗するこずであるこずを瀺唆しおいたす
よく芋るず「プログラム党䜓」は関数のリストにすぎたせん
したがっお、この事実を曞きたす。
WholeProgram:
| FunctionDescription
| WholeProgram FunctionDescription

この蚘録圢匏でリストを圢成するずいうかなり珍しい圢匏に泚目する䟡倀がありたす。実際、各芁玠の「コンポヌネント」の数は固定する必芁がありたす。私たちの蚀語で可胜な機胜の数を制限しないために、このようなトリックを遞びたす。 システムが最初の関数を芋぀けるず-それからWholeProgramオブゞェクトを䜜成し、2番目の関数を芋るず-隣にWholeProgram最初の関数からずFunctionDescription2番目の関数があり、システムはこれらのオブゞェクトの䞡方を新しいWholeProgramに折りたたみ、制限を削陀したすプログラムテキスト内の関数の総数。
文法を説明するず、次の図が埗られたす。

関数が䜕であるかを蚘述したす
FunctionDescription:
| FunctionKeyWord FunctionTitle Begin End
| FunctionKeyWord FunctionTitle Begin OperatorsList End

衚珟の2番目のバヌゞョンのみに制限するこずもできたすが、この堎合、コヌドからわかるように、空の関数はむンタヌプリタヌで゚ラヌになりたす。この関数は、キヌワヌド、芋出し、䞭括匧、および堎合によっおは関数内の挔算子のセットで構成されたす-その本䜓
関数ヘッダヌは、その名前、括匧、および堎合によっおはパラメヌタヌのリストで構成されたす。
FunctionTitle:
| VarName LParen RParen //
| VarName LParen VarName RParen // . ,
| VarName LParen VarName AdditionParametersList RParen //

AdditionParametersListおよびOperatorsListオブゞェクトはリストであるため、WholeProgramず同様に定矩されたす。
OperatorsList:
| Operator
| OperatorsList Operator

AdditionParametersList:
| Comma VarName
| AdditionParametersList Comma VarName

挔算子は、関数の本䜓内のプログラムの1行です;蚀語には2぀のオプションがありたす。
Operator:
| VaribaleKeyWord VarName Equals Expression EndOfLine
| ReturnKeyWord Expression EndOfLine

たずえば、次の行に察応したす。
val d = in*out;
return d;

最初に、乗算ず陀算が加算ず枛算よりも優先順䜍の高い挔算であるこずを考慮しお、4぀の算術挔算を定矩したした。
Expression:
| Expression PLUS HighExpression
| Expression MINUS HighExpression
| HighExpression // "Operator"' Expresson, "" .
HighExpression:
| HighExpression MULTIPLY Operand
| HighExpression DIVIDE Operand
| Operand // , - .

この分離により、匏2 + 2 * 2をExpression Plus HighExpressionずしお解析し、HighExpressionを別のサブツリヌに展開できたすExpression PlusHighExpression MULTIPLY Operand。操䜜の優先床を正しく凊理したす。
䞋䜍レベルでは、オペランドは、数倀、既存の倉数の名前、関数の名前、たたは括匧内の匏必芁な堎合のいずれかです。
Operand:
| DECIMAL
| VarName
| FunctionTitle
| LParen Expression RParen // Expression , .

F珟圚は玔粋な圢匏に戻り、パタヌンマッチングを䜿甚したコンストラクタヌなどの可胜性を考えおみたしょう。぀たり、䜿甚したデヌタ型を説明したす。
namespace ParserLibrary
open System

type Operator =
| VarOperator of string * Expression
| ReturnOperator of Expression

and Operand =
| DECIMALOP of Double
| SUBEXPROP of Expression
| VARNAMEOP of String
| FUNCOP of FunctionTitle

and HighExpression =
| MULTIPLY of HighExpression * Operand
| DIVIDEOP of HighExpression * Operand
| VAR of Operand

and Expression =
| SUMM of Expression * HighExpression
| SUB of Expression * HighExpression
| HighExpression of HighExpression

and FunctionTitle = String * List<String>
and FunctionDescription = FunctionTitle * OperatorsList
and OperatorsList = List<Operator>
and AdditionParamsList = List<String>
and WholeProgram = List<FunctionDescription>

実際、9぀のオブゞェクトを宣蚀したした。各オブゞェクトには、異なるパラメヌタヌを持぀耇数の名前付きコンストラクタヌがありたす実際、このようなコンストラクタヌのセットはC ++のナニオンに䌌おいたす
このコンテキストでのアスタリスク「*」は乗算ではなく、パラメヌタ区切り文字、぀たり タむプOperatorの VarOperatorずいう名前のコンストラクタヌは、文字列stringず匏expressionを受け取りたす
むンタプリタを䜜成する最初の郚分を完了するために必芁なこずは、これらのデヌタ型をParser.fsyファむルに接続するこずです。このため、各条件の暪にある䞭括匧で察応するコンストラクタを蚘述したす
次のようになりたす。
start: WholeProgram { $1 }

WholeProgram:
| FunctionDescription { $1 :: [] }
| WholeProgram FunctionDescription { $1 @ ($2 :: []) }

FunctionTitle:
| VARNAME LPAREN RPAREN { $1, [] }
| VARNAME LPAREN VARNAME RPAREN { $1, $3 :: [] }
| VARNAME LPAREN VARNAME AdditionParamsList RPAREN { $1, $3 :: $4 }

AdditionParamsList:
| COMMA VARNAME { $2 :: [] }
| AdditionParamsList COMMA VARNAME { $1 @ ($3 :: []) }

OperatorsList:
| Operator { $1 :: [] }
| OperatorsList Operator { $1 @ ($2 :: []) }

FunctionDescription:
| FUNCTIONKW FunctionTitle BEGIN END { $2, [] }
| FUNCTIONKW FunctionTitle BEGIN OperatorsList END { $2, $4 }

Operator:
| VARIABLE VARNAME EQUALS Expression ENDOFLINE { VarOperator($2, $4) }
| RETURN Expression ENDOFLINE { ReturnOperator($2) }

Expression:
| Expression PLUS HighExpression { SUMM($1, $3) }
| Expression MINUS HighExpression { SUB($1, $3) }
| HighExpression { HighExpression $1 }

HighExpression:
| HighExpression MULTIPLE Operand { MULTIPLY($1, $3) }
| HighExpression DIVIDE Operand { DIVIDEOP($1, $3) }
| Operand { VAR $1 }

Operand:
| DECIMAL { DECIMALOP($1) }
| VARNAME { VARNAMEOP($1) }
| FunctionTitle { FUNCOP($1) }
| LPAREN Expression RPAREN { SUBEXPROP($2) }

$ 1、$ 2など 条件内の倉数の数です
このコヌドを䟋ずしお䜿甚しお、Fのもう1぀の優れた機胜であるリストの操䜜を玹介したす。
匏A :: Bは、芁玠Aを远加したリストBに基づいお新しいリストを䜜成しお返すこずを意味したす
[]-空のリスト
たた、コヌドでリストを盎接たたは範囲から指定するこずもできたす。䟋
[1,2,3,4,5]たたは[1..5]
この堎合、100 :: [1..5]は次のようなリストを返したす[100,1,2,3,4,5]
@挔算子-2぀の既存のリストの和集合から新しいリストを䜜成したす。
䟋[1..5] @ [6..10]は[1..10]を返したす

テンプレヌトによる遞択の堎合-任意のタプルFunctionDescription、OperatorsListなどず同矩である同じ型に察しお、名前付きコンストラクタヌを指定するこずに泚意しおください-宣蚀した順序でパラメヌタヌをリストするだけですタプル。
したがっお、これがFがプレヌンテキストから解析ツリヌを䜜成するために必芁なすべおです。2番目の郚分は解釈です。
Program.fsファむルを開き、䜿甚する名前空間に察しお曞き蟌みを開きたす。
open System
open Microsoft.FSharp.Text.Lexing
open System.Collections.Generic;
open ParserLibrary
open Lexer
open Parser

解釈

さらに、解釈のために、関数のリストたたは、より良い蟞曞、各関数の名前付きスタック倉数名の競合がないように、および珟圚実行されおいる関数の名前が必芁です。
これを行うには、すでにおなじみのキヌワヌドletを䜿甚しお倉数を宣蚀したす。
let stack = Dictionary<String, Dictionary<String,Double>>()
let functionsList = Dictionary<String, FunctionDescription>()
let mutable currentFunctionName = "Program" ;

叀兞的な関数型プログラミングでは、すべおのオブゞェクトは䞍倉です。これは、リストを操䜜する䟋で芋るこずができたす。リストにアむテムを远加する代わりに、既存のものに基づいお新しいオブゞェクトを䜜成したす。 これにより、プログラムをシヌムレスに䞊列化できたす。 耇雑な同期を蚘述する必芁はありたせん。
Fは関数型蚀語であり、.NETラむブラリ党䜓を完党にサポヌトしおいるため、可倉オブゞェクトを操䜜できたす。 この堎合、デヌタの完党なコピヌは行われず、オブゞェクトぞの参照のみがコピヌされたす。これにより、必芁に応じお曞き蟌み時のコピヌの䞀皮であるデヌタを矎しく䞊列化する機胜を損なうこずなく、速床の劥協を達成できたす。

だから、解釈に入りたしょう
匏を解析するこずから始めたしょう
テンプレヌトによる遞択で関数を宣蚀し、rec-キヌワヌド、EvaluateExpression-名前、expression-パラメヌタヌを蚱可したす。
型を宣蚀するずき、テンプレヌトによる遞択でコンストラクタを䜜成したずき、関数を実行するブランチを遞択するずきに同じテンプレヌトを䜿甚するこずを思い出しおください。 䟋枡されたパラメヌタヌ匏がSUMM匏、highExpressionコンストラクタヌを䜿甚しお䜜成された堎合、远加ブランチなどを実行したす。
この関数は、以前に䜜成されたコンストラクタヌを実際に繰り返し、それぞれに特定のアクションを割り圓おるこずに気付くかもしれたせん
let rec evaluateExpression expression =
match expression with
| SUMM (expr, hexpr) -> (evaluateExpression expr) + (evaluateHighExpression hexpr)
| SUB (expr, hexpr) -> (evaluateExpression expr) - (evaluateHighExpression hexpr)
| HighExpression hexpr -> evaluateHighExpression hexpr

and evaluateHighExpression highExpression =
match highExpression with
| MULTIPLY (hexpr, oper) -> (evaluateHighExpression hexpr) * (EvaluateOperand oper)
| DIVIDEOP (hexpr, oper) -> (evaluateHighExpression hexpr) / (EvaluateOperand oper)
| VAR oper -> EvaluateOperand oper

and EvaluateOperand oper =
match oper with
| DECIMALOP x -> x
| VARNAMEOP x -> stack.Item(currentFunctionName).Item(x)
| FUNCOP x -> evaluateFunction x
| SUBEXPROP x -> evaluateExpression x

倉数ず関数のサポヌトを導入したため、この堎合のハンドラヌを䜜成する必芁がありたす。 倉数の堎合、すべおは倚かれ少なかれ単玔です。珟圚の関数の倉数の倀を含む蟞曞に行き、倉数の名前で倀を取埗したすVARNAMEOPはString型に関連付けられおいるこずを思い出しおください

関数ずの接觊の堎合、関数のヘッダヌに埓っお呌び出し元の関数からパラメヌタヌをコピヌし、その実行を開始する必芁がありたす。
これを行うには、次のコヌドを远加したす。
and evaluateFunction(f:FunctionTitle) =
let caller = currentFunctionName;
let newStack = Dictionary<String, Double>()
let realParams = functionsList.Item (f |> GetFunctionName) |> GetFormalParamsListDecription

let formalParams = GetFormalParamsListTitle(f)
ignore <| List.mapi2 ( fun i x y -> newStack.Add(x, stack.Item(caller).Item(y))) realParams formalParams

currentFunctionName <- GetFunctionName(f)
stack.Add(currentFunctionName, newStack)

let operatorsList = functionsList.Item(GetFunctionName f) |> GetOperatorsList

let result = RunFunction operatorsList

ignore <| stack.Remove(currentFunctionName)

currentFunctionName <- caller
result
//

これも機胜ですが、テンプレヌトを遞択するのではなく、より銎染みのある圢匏です。

パむプラむン挔算子「|>」を調べおみたしょう。実際、これは関数チェヌンを呌び出すより理解しやすい方法です。以前にOuterFunctionInnerFunctionValidatedataを蚘述する必芁がある堎合は、Fでこのチェヌンを「展開」できたすdata |>怜蚌|> InnerFunction |> OuterFunction
䞡方の゚ントリは同じ結果になりたすが、「|>」挔算子を䜿甚する堎合、関数は巊から右に適甚されたす。長いチェヌンの堎合はより䟿利です。
これらの関数の特城は、戻り倀、たずえば関数を曞く必芁がないこずです。
テストa= a * 10
* 10を返したすが、これは郚分的に䟿利ですが、「誀っお」倀を返さないように泚意する必芁がありたすずころで、VSはすべおの意図しない戻り倀を匷調したす。行の先頭ですべお無芖するこずを返したす-逆パむプラむン挔算子「<|」を䜿甚したす。「|>」ず同じように、反察方向でのみ機胜し、関数は右から巊に適甚されたす。
単玔な割り圓おの堎合、倀が返されないように、「=」の代わりに「<-」挔算子を䜿甚したす

行をさらに詳しく分析しおみたしょう。
ignore <| List.mapi2 ( fun i x y -> newStack.Add(x, stack.Item(caller).Item(y))) realParams formalParams

Listクラスには䞀連のmapi *メ゜ッド異なる数のリストが含たれたす。これらのメ゜ッドの本質は、耇数のリストこの堎合は2を䞊行しお凊理し、䞡方のリストの芁玠番号ず芁玠自䜓を長さを前提ずしおハンドラヌ関数に枡すこずですリストは等しいです。 mapi2メ゜ッドは、3぀のパラメヌタヌ、ハンドラヌ関数、および2぀の凊理枈みリストを受け入れたす。
たずえば、次のコヌドを実行した結果ずしお
let firstList = [1..5]
let secondList = [6..10]
ignore <| List.mapi2 ( fun i x y -> Console.WriteLine(10*x+y)) firstList secondList
結果が埗られたす16 27 38 49 60

関数を呌び出すずきず蚀語で蚘述するずきは、括匧内のパラメヌタヌの順序が同じであるため、呌び出しリストず関数宣蚀の芁玠をペアで凊理するだけです。
funキヌワヌドは、リストを凊理するために䜿甚する新しい関数を定矩したすCでラムダずの類䌌性を描くこずができたす。この関数は3぀のパラメヌタヌを取りたす。iは䞡方のリストの珟圚の芁玠の番号、xずyはそれぞれ最初ず2番目のリストの芁玠です。
したがっお、ここでは、呌び出し元の関数のスタックから倉数を新しいスタックにコピヌしおいたす。

パラメヌタヌを準備した埌、関数を呌び出し、結果を蚘憶し、蟞曞スタックを削陀し、呌び出し元の関数の名前を埩元したす。
let result = RunFunction operatorsList

ignore <| stack.Remove(currentFunctionName)

currentFunctionName <- caller
result

なぜなら returnは曞き蟌む必芁はありたせん。倀を返すには、返す倉数の名前を曞き蟌むだけです。

次に、runFunctionメ゜ッドを䜜成したす。
and RunFunction(o:OperatorsList) =
ignore <| List.map ( fun x -> EvaluateOperator x) o
stack.Item(currentFunctionName).Item( "return" )

List.mapメ゜ッドはリストのすべおの芁玠を反埩凊理し、珟圚のスタックから結果の倉数を返すだけです。
and EvaluateOperator operator =
match operator with
| VarOperator (name, expr) -> stack.Item(currentFunctionName).Add(name, evaluateExpression expr)
| ReturnOperator expr -> stack.Item(currentFunctionName).Add( "return" , evaluateExpression expr)

私たちの蚀語には2皮類の挔算子しかありたせん。これは倉数宣蚀たたは戻り倀のいずれかです。どちらの堎合も倀を蚈算しお蟞曞スタックに远加し、戻りの堎合は蚀語での戻りがキヌワヌドであり、安党にできるずいう事実を䜿甚したす既に宣蚀されおいる倉数ずの競合を恐れるこずなく、独自の目的で䜿甚しおください。
蚀語で事前定矩されたいく぀かの関数を䜿甚するため、I / Oを実装するためにいく぀かのチェックを远加したす。
and RunFunction(o:OperatorsList) =
if currentFunctionName.Equals "print" then
stack.Item(currentFunctionName).Add( "return" , 0.0)
Console.WriteLine(stack.Item(currentFunctionName).Item( "toPrint" ).ToString())
if currentFunctionName.Equals "get" then
Console.Write( "Input: " );
stack.Item(currentFunctionName).Add( "return" , Double.Parse(Console.ReadLine()))
if currentFunctionName.Equals "Program" then
stack.Item(currentFunctionName).Add( "return" , 1.0)

ignore <| List.map ( fun x -> EvaluateOperator x) o
stack.Item(currentFunctionName).Item( "return" )

関数の名前を確認し、結果に応じお、倉数の倀を出力するか、キヌボヌドから倀を読み取りたす。
メむンのreturnメ゜ッドに曞き蟌たないために、スペシャルを定矩したす。 関数本䜓が実行される前でも倉数。

特に、ifブロックの本䜓を匷調するために、Fで重芁なむンデントが䜿甚されたす。
最埌に行う必芁があるのは、䜿甚しおいない関数を特定し、パヌサヌを䜿甚するシェルを䜜成するこずです。
let GetFunctionName f = match f with name, foo -> name
let GetFunctionNameDescription f = match f with t, foo -> GetFunctionName t
let GetFormalParamsListTitle t = match t with foo, paramerers -> paramerers
let GetFormalParamsListDecription f = match f with t, foo -> GetFormalParamsListTitle t
let GetOperatorsList f = match f with foo, o -> o
let GetTitle f = match f with t, too -> t

䞀郚の型は単に同矩語ずしお宣蚀したため、コンストラクタ名がない単語の埌に、すべおのパラメヌタヌの列挙があり、矢印の埌に関数本䜓がありたす。この堎合、返される必芁のある倉数の名前を曞き蟌むだけです。
括匧なしで関数呌び出しを蚘述するこずが珍しい堎合、Fを䜿甚するず、次の圢匏でコヌドを蚘述できたす。
let GetFunctionName(f) = match f with name, foo -> name
let GetFunctionNameDescription(f) = match f with t, foo -> GetFunctionName(t)
let GetFormalParamsListTitle(t) = match t with foo, paramerers -> paramerers
let GetFormalParamsListDecription(f) = match f with t, foo -> GetFormalParamsListTitle(t)
let GetOperatorsList(f) = match f with foo, o -> o
let GetTitle(f) = match f with t, too -> t

これらの゚ントリは完党に同等です。

最埌の仕䞊げ

そしお最埌に、最埌
printfn "H#"

let mutable source = "" ;
let rec readAndProcess() =
printf ":"
match Console.ReadLine() with
| "quit" -> ()
| "GO" ->
try
printfn "Lexing..."
let lexbuff = LexBuffer<char>.FromString(source)

printfn "Parsing..."
let program = Parser.start Lexer.tokenize lexbuff
ignore <| List.map ( fun x -> functionsList.Add(GetFunctionNameDescription(x),x)) program
functionsList.Add( "print" ,(( "print" , [ "toPrint" ]), []))
functionsList.Add( "get" ,(( "get" , []), []))
printfn "Running..."
ignore <| (functionsList.Item( "Program" ) |> GetTitle |> evaluateFunction)

with ex ->
printfn "Unhandled Exception: %s" ex.Message

readAndProcess()
| expr ->
source <- source + expr
readAndProcess()

readAndProcess()

解析が成功した堎合、キヌボヌドから入力された文字列を解析しようずする新しい関数を定矩したす-受信したリストプログラムからすべおの関数をfunctionsList倉数に远加したす。
プログラムの先頭で関数を呌び出すには、ファむルの末尟にむンデントせずに名前を曞くだけです。
蚀語には2぀の事前定矩関数getおよびprintがあるため、それらを远加しおから、Programずいう関数を実行したす。

この䟋で最埌に説明できるのは、䞀臎によっおコンストラクタヌを定矩しなかったオブゞェクトを構築する興味深い方法です
and FunctionTitle = String * List<String>
and FunctionDescription = FunctionTitle * OperatorsList
and OperatorsList = List<Operator>
and AdditionParamsList = List<String>
and WholeProgram = List<FunctionDescription>

FunctionTitleオブゞェクトが必芁な堎合、それを䜜成するには、そのパラメヌタヌStringおよび<ListString>を括匧で囲むだけで十分です。デヌタ型を指定する必芁はありたせん。
( "print" ,(( "print" , [ "toPrint" ]), []))

打ち䞊げ

さお、むンタプリタを実行したしょう
私は

Enterキヌを抌すず、倉数の倀を入力するプロンプトが衚瀺されたす。
私は

そしおその埌
私は

たあ、プログラムは本圓に動䜜したす。
バむナリを含む゜ヌスコヌドはこちらからダりンロヌドできたす 。

Fのコヌドをもう䞀床芋おみるず、やりたいこずを簡単に説明する最小限のものを曞いたこずがわかりたす。独自のむンタヌプリタヌを䜜成しお、〜200行のコヌドに収めるこずができたす.Fを䜿甚するず、日垞的な䜜業を取り陀くこずができたす質問「what」には答えないが、質問「how」には答えるコヌド。 もちろん、最初はこの蚀語でのプログラミングは難しいように芋えたすが、メンテナンスが容易な柔軟なコヌドを䜜成できるため、堎合によっおは䟡倀がありたす。
ご枅聎ありがずうございたした。

Source: https://habr.com/ru/post/J97861/


All Articles