エレファント・ビジュアライザー調査記録

ビジュアルプログラミングで数式の変形を表すことを考えていくブロクです。

自由モノイドのイテレーター(6)

自由モノイドプログラミング言語の作成(Python版 1)

前回は C# の Sprache を使って書いていたのですが、構文がわかりにくくなってしまったので、いったん Python の lark モジュールを使って書き直してみることにします。このモジュールでは構文を独立して書くので構文が見やすくなります。構文の曖昧なところもチェックできます。同様のものが C# でもあるとは思うのですが、lark モジュールは以前使ったことがあるのでこれを使ってみます。前回はまず C# で書くと言ったのですが、Python も使うことにします。

構文の変更

このプログラミング言語を MonIter と呼ぶことにします。前回のバージョンをバージョン0(MonIter 0)、今回のバージョンをバージョン1(MonIter 1)とします。

MonIter 1 の変更点は以下のようになります。

関数定義には「def」をつける

これは構文が曖昧というわけではないのですが、lark モジュールではエラーになってしまうので変更します。

モノイドの演算子「&」

MonIter 0 では「*」を使っていたのですが、「*」は数値の演算に使うことにして(ただし今は使えません)モノイドの演算子は「&」とします。

モノイドの元を引数とする関数の先頭には「$」をつける

MonIter 0 では関数の名前の先頭の文字が「_」としたのですが、このように変更します。

例(フィボナッチ数列)

引数で渡すバージョン

MonIter 0 では

    fib(x, y) = x * fib(y, x + y);
    fib(0, 1)

というコードが MonIter 1 では

    def fib(x, y) = x & fib(y, x + y);
    fib(0, 1)

となります。

Zip を使うバージョン

MonIter 0 では

    fib(x, y) = x * y * _zipsum(fib(x, y), _tail(fib(x, y)));
    fib(0, 1)

というコードが MonIter 1 では

    def fib(x, y) = x & y & $zipsum(fib(x, y), $tail(fib(x, y)));
    fib(0, 1)

となります。

構文

lark モジュールの書き方で書いた構文は以下のようになります。

?start: program

program: statement_block

statement_block: statement
    | statement ";" statement_block

statement: definition
    | expression_statement

definition: "def" identifier "(" parameter_list ")" "=" expression

parameter_list: identifier
    | identifier "," parameter_list

expression_statement: expression

expression: product

product: term
    | term "&" product

term: domain_expression
    | function
    | domain_function

function: "$" identifier "(" expression_list ")"

expression_list: expression
    | expression "," expression_list

domain_function: identifier "(" domain_expression_list ")"

domain_expression_list: domain_expression
    | domain_expression "," domain_expression_list

domain_expression: domain_sum

domain_sum: domain_term
    | domain_term "+" domain_sum

domain_term: identifier
    | number

identifier: /[A-Za-z_][A-Za-z0-9_]*/
number: /[0-9]+/

%import common.WS
%ignore WS  # 空白を無視

構文木の変換

この構文の構文解析によって得られた構文木を変換するメソッドを書きます。これは構文の各要素に対応するように書く必要があります。構文の各要素をそれに対応するクラスに変換します。

構文の要素 変換するメソッド 構文に対応するクラス
program program Program
statement_block statement_block List[Statement]
statement statement Statement
definition definition Definition
parameter_list parameter_list List[Identifier]
expression_statement expression_statement ExpressionStatement
expression expression Expression
product product List[Term]
term term Term
function function Function
expression_list expression_list List[Expression]
domain_function domain_function DomainFunction
domain_expression_list domain_expression_list List[DomainExpression]
domain_expression domain_expression DomainExpression
domain_sum domain_sum List[DomainTerm]
domain_term domain_term DomainTerm
identifier identifier Identifier
number number Number

構文木を変換するクラス

構文木を変換するクラスは以下のようになります。

class CustomTransformer(Transformer):
    def program(self, item: "List[Statement]") -> "Program":
        return Program(item)

    def statement_block(self, item: "Statement", list: "Optional[List[Statement]]" = None) -> "List[Statement]":
        if list is None:
            return [item]
        else:
            return [item] + list

    def statement(self, item: "Statement") -> "Statement":
        return item

    def definition(self, name: "Identifier", paramlist: "List[Identifier]", exp: "Expression") -> "Statement":
        return Definition(name, paramlist, exp)

    def parameter_list(self, item: "Identifier", list: "Optional[List[Identifier]]" = None) -> "List[Identifier]":
        if list is None:
            return [item]
        else:
            return [item] + list

    def expression_statement(self, exp: "Expression") -> "Statement":
        return ExpressionStatement(exp)

    def expression(self, exp: "List[Term]") -> "Expression":
        return Expression(exp)

    def product(self, item: "Term", list: "Optional[List[Term]]" = None) -> "List[Term]":
        if list is None:
            return [item]
        else:
            return [item] + list

    def term(self, item: "Term") -> "Term":
        return item

    def function(self, name: "Identifier", explist: "List[Expression]") -> "Function":
        return Function(name, explist)

    def expression_list(self, item: "Expression", list: "Optional[List[Expression]]" = None) -> "List[Expression]":
        if list is None:
            return [item]
        else:
            return [item] + list

    def domain_function(self, name: "Identifier", explist: "List[DomainExpression]") -> "DomainFunction":
        return DomainFunction(name, explist)

    def domain_expression_list(self, item: "DomainExpression", list: "Optional[List[DomainExpression]]" = None) -> "List[DomainExpression]":
        if list is None:
            return [item]
        else:
            return [item] + list

    def domain_expression(self, termlist: "List[DomainTerm]") -> "DomainExpression":
        return DomainExpression(termlist)

    def domain_sum(self, item: "DomainTerm", list: "Optional[List[DomainTerm]]" = None) -> "List[DomainTerm]":
        if list is None:
            return [item]
        else:
            return [item] + list

    def domain_term(self, item: "DomainTerm") -> "DomainTerm":
        return item
    
    def identifier(self, name: str) -> "Identifier":
        return Identifier(str(name))
    
    def number(self, name: str) -> "Number":
        return Number(str(name))