はじめに
ITパスポート・基本情報技術者試験の擬似言語をPythonに変換するプログラム(トランスコンパイラ)の紹介です。Python3.10で動作を確認しています。
Pythonに変換することで、Python用のデバッガが使えるようになるため、擬似言語の理解の助けになるのではないでしょうか。
使い方
ページ末尾のソースコードを「pesudo.py」という名前で保存したという前提で説明します。
そのまま実行した場合は、標準入力から擬似言語を読み込み、Pythonに変換して標準出力に出力します。
$ python3 pseudo.py
123を出力
Ctrl-D
print(123)
コマンドライン引数にファイル名を指定すると、そのファイルから擬似言語を読み込みます(test.txtの中身は「123を出力」)。
$ python3 pseudo.py test.txt
print(123)
「-o ファイル名」と指定すると、そのファイルにPythonコードが保存されます。
$ python3 pseudo.py test.txt -o test.py
「-e」を指定すると、Pythonコードに変換せず、その場で擬似言語を実行します。
$ python3 pseudo.py test.txt -e
123
注意
- 不完全な仕様をもとに実装しているため、擬似言語として正しいコードなのに変換できない場合があります
- エラーチェックが甘いので、擬似言語として正しくないコードを変換できてしまう場合があります
仕様
公開されている仕様との差分(独自に追加した仕様)のみ説明します。
型
基本型は「整数型」「実数型」「文字列型」「文字型」「論理型」の5種類です。
基本型の後ろに「の配列」をつけると一次元配列に、「配列の配列」もしくは「の二次元配列」をつけると二次元配列になります。
リテラル
「123」のように数字を並べたものは整数リテラル、「123.45」のように数字の間に小数点が入ると実数リテラルとなります。
「"ABC"」のようにダブルクォートで囲まれた文字列は文字列リテラルとなります。エスケープ機能がないため、ダブルクオートを含む文字列を作成する方法はありません。
手続きまたは関数の記法
手続き、関数の本体は、手続き・関数宣言後のインデントされた範囲とします。
○手続き名(型名1: 引数名1, 型名2: 引数名2, ...)
処理1
処理2
...
処理n
○戻り値型名: 関数名(型名1: 引数名1, 型名2: 引数名2, ...)
処理1
処理2
...
処理n
return
「return 値」と記述することで、指定した値を戻り値として関数を抜けます。
変数宣言
以下のように、コンマで区切って並べることで、同じ型の複数の変数をまとめて宣言することができます。また、変数宣言と同時に値を代入することもできます。
型名: 変数名1 ← 値1, 変数名2, ...
大域変数宣言
手続きまたは関数の外で宣言された変数を中から参照したい場合、その変数を大域変数として宣言する必要があります。大域変数として宣言するには、変数宣言の前に「大域:」と記述します。
大域: 型名: 変数名1 ← 値1, 変数名2, ...
for文の制御記述
以下の3種類が使用できます。
for (iを1から3まで1ずつ増やす)
for (iを3から1まで1ずつ減らす)
for (orderにordersの要素を順に代入する)
演算子と優先順位
演算子の種類 | 演算子 | 優先度 |
---|---|---|
式 | () . [] | 高 |
単項演算子 | not + - | ↑ |
日本語演算 | 「~の要素数」など | │ |
乗除 | mod × ÷「~÷~の商」「~÷~の余り」 | │ |
加減 | + - | │ |
論理シフト | << >> | │ |
ビット論理積 | ∧ | │ |
ビット論理和 | ∨ | │ |
関係 | ≠ ≦ ≧ < = > | │ |
日本語関係 | 「~が~以下」など | │ |
論理積 | and | ↓ |
論理和 | or | 低 |
配列の特殊記法
以下のように記述すると、サイズが5で、全要素が-1で初期化された一次元配列を生成できます。
{5個の -1}
以下のように記述すると、サイズrow行col列で、全要素が「未定義の値」で初期化された二次元配列を生成できます。
{row行col列の 未定義の値}
日本語機能一覧
読めばわかる説明は省略します。
日本語処理
値を返さない手続き的な処理です。
処理 | 説明 |
---|---|
(a)を出力 | |
(a)と(b)をこの順にコンマ区切りで出力する | 「コンマ区切り」を「空白区切り」に変更可 |
(a)の全ての要素 を先頭から順にコンマ区切りで出力する | 「コンマ区切り」を「空白区切り」に変更可 |
(a)の末尾 に (b) を追加する | 配列、文字列に使用可能 |
(a)と(b)の値を入れ替える | |
繰返し処理を終了する | 他言語のbreak |
(a)の値を(b)増やす |
日本語比較
比較演算です(一部例外あり)。
処理 | 説明 |
---|---|
(a) が (b) で割り切れる | |
(a) が (b) と等しい | |
(a) のいずれかの要素の値が (b) と等しい | |
(a) が (b) と等しくない | |
(a) が (b) より大きい | |
(a) が (b) より小さい | |
(a) が (b) 以上 | |
(a) が (b) 以下 | |
(a) が 未定義 | |
(a) が 未定義ではない | |
(a) の要素の和 | 一次元、二次元両対応 |
日本語計算
値を返す関数的な処理です。
処理 | 説明 |
---|---|
(a)の要素数 | |
(a)の小数点以下を切り上げた値 | |
(a)の小数点以下を切り捨てた値 | |
(a)の正の平方根 | |
(a)の整数部分 | |
(a)の(b)乗 | |
(a)の1文字だけから成る文字列 | 文字型から文字列型への変換 |
(a)の(b)文字目の文字 | |
(a)の末尾から(b)文字目の文字 | |
(a)を整数型に変換した値 |
ソースコード
#!/usr/bin/env python3
import re
import ast
import sys
import copy
import argparse
from dataclasses import dataclass
from collections import ChainMap
def parse(i):
return ast.fix_missing_locations(program(peekable(i)))
class peekable:
def __init__(self, it):
self.it = it
self.val = []
def peek(self, pos = 0):
while len(self.val) <= pos:
self.val.append(next(self.it))
return self.val[pos]
def __next__(self):
ret = self.peek()
self.val.pop(0)
return ret
global_variables = None
global_imports = None
global_environments = None
def program(s):
global global_variables, global_imports, global_environments
global_variables = set()
global_imports = set()
global_environments = ChainMap()
stmts = []
while not accept(s, 'EOF'):
stmts += statement(s)
if global_imports:
stmts.insert(0, ast.Import([ast.alias(i) for i in global_imports]))
return ast.Module(stmts, [])
def statement(s):
global global_environments
if accept(s, '○'):
global_environments = global_environments.new_child()
if isinstance(s.peek(), Ident) and s.peek(1) == '(':
rt = None
else:
rt, _ = japanese_inner(s), expect(s, ':')
n, _ = expect_t(s, Ident), expect(s, '(')
args = comma_separated(s, ')', arg_with_type)
body = statements_until(s, 'ENDDEF')
expect(s, 'ENDDEF')
global_environments = global_environments.parents
store_global = global_variables & set(collect_store(body))
if store_global:
body.insert(0, ast.Global([*store_global]))
yield ast.FunctionDef(n.id, ast.arguments([], args, None, [], [], None, []), body, [], rt)
elif accept(s, 'return'):
yield ast.Return(None if s.peek() in ('elseif', 'else', 'endif') else bool_or(s))
elif accept(s, 'if'):
yield if_statement(s)
expect(s, 'endif')
elif accept(s, 'for'):
_, i, _, it, _ = expect(s, '('), expect_t(s, Ident), expect_t(s, Japanese), bool_or(s), expect(s, ')')
yield ast.For(ast.Name(i.id, ast.Store()), it, statements_until(s, 'endfor'), [])
expect(s, 'endfor')
elif accept(s, 'while'):
_, c, _ = expect(s, '('), bool_or(s), expect(s, ')')
yield ast.While(c, statements_until(s, 'endwhile'), [])
expect(s, 'endwhile')
elif accept(s, 'do'):
body = statements_until(s, 'while')
_, _, c, _ = expect(s, 'while'), expect(s, '('), bool_or(s), expect(s, ')')
body.append(ast.If(ast.UnaryOp(ast.Not(), c), [ast.Break()], []))
yield ast.While(ast.Constant(True), body, [])
elif accept(s, '大域'):
_, t, _ = expect(s, ':'), japanese_inner(s), expect(s, ':')
while True:
i = expect_t(s, Ident)
global_variables.add(i.id)
global_environments[i.id] = t
e = bool_or(s) if accept(s, '←') else None
yield ast.AnnAssign(ast.Name(i.id, ast.Store()), copy.deepcopy(t), e, 1)
if not accept(s, ','):
break
elif j := accept_t(s, Japanese):
yield japanese_statement_commands[j.id]([])
else:
t = compare(s)
if accept(s, ':'):
while True:
i = expect_t(s, Ident)
global_environments[i.id] = t
e = bool_or(s) if accept(s, '←') else None
yield ast.AnnAssign(ast.Name(i.id, ast.Store()), copy.deepcopy(t), e, 1)
if not accept(s, ','):
break
elif accept(s, '←'):
t.ctx = ast.Store()
yield ast.Assign([t], bool_or(s))
elif isinstance(s.peek(), Japanese):
stack = [t]
while True:
if not (j := accept_t(s, Japanese)):
stack.append(compare(s))
elif j.id in japanese_statement_commands:
yield japanese_statement_commands[j.id](stack)
break
else:
stack.append(j)
else:
yield ast.Expr(t)
def arg_with_type(s):
t, _, a = japanese_inner(s), expect(s, ':'), expect_t(s, Ident)
global_environments[a.id] = t
return ast.arg(a.id, t)
def collect_store(exprs):
for expr in exprs:
for node in ast.walk(expr):
if isinstance(node, ast.Name) and isinstance(getattr(node, 'ctx', None), ast.Store):
yield node.id
def print_(stack):
args = [s for s in stack if not isinstance(s, Japanese)]
if Japanese('の全ての要素') in stack:
args = [ast.Starred(a) for a in args]
sep = [ast.keyword('sep', ast.Constant(','))] if Japanese('コンマ区切りで') in stack else []
return ast.Expr(ast.Call(ast.Name('print'), args, sep))
def add_last(stack):
e1, _, _, e2 = stack
type_ = get_type(e1)
if isinstance(type_, ast.Name) and type_.id == 'str':
e1.ctx = ast.Store()
return ast.AugAssign(e1, ast.Add(), e2)
else:
return ast.Expr(ast.Call(ast.Attribute(e1, 'append'), [e2], []))
def swap(stack):
a, b = copy.deepcopy(stack[0]), copy.deepcopy(stack[2])
a.ctx = ast.Store()
b.ctx = ast.Store()
return ast.Assign([ast.Tuple([a, b], ast.Store())], ast.Tuple([stack[2], stack[0]]))
def break_(_):
return ast.Break()
def increment(stack):
return ast.AugAssign(stack[0], ast.Add(), stack[2])
japanese_statement_commands = {
'出力': print_,
'を追加': add_last,
'の値を入れ替え': swap,
'繰返し処理を終了': break_,
'増やす': increment,
}
def get_type(var):
nest = 0
while isinstance(var, ast.Subscript):
var = var.value
nest += 1
if not isinstance(var, ast.Name) or var.id not in global_environments:
return None
type_ = global_environments[var.id]
for _ in range(nest):
if not isinstance(type_, ast.Subscript):
return None
type_ = type_.slice
return type_
def if_statement(s):
_, c, _ = expect(s, '('), bool_or(s), expect(s, ')')
t = statements_until(s, 'elseif', 'else', 'endif')
if accept(s, 'elseif'):
e = [if_statement(s)]
elif accept(s, 'else'):
e = statements_until(s, 'endif')
else:
e = []
return ast.If(c, t, e)
def statements_until(s, *ss):
body = []
while s.peek() not in ss:
body += statement(s)
return body
def bool_or(s):
values = [bool_and(s)]
while accept(s, 'or'):
values.append(bool_and(s))
if len(values) == 1:
return values[0]
else:
return ast.BoolOp(ast.Or(), values)
def bool_and(s):
values = [japanese_outer(s)]
while accept(s, 'and'):
values.append(japanese_outer(s))
if len(values) == 1:
return values[0]
else:
return ast.BoolOp(ast.And(), values)
def japanese_outer(s):
e = compare(s)
if not isinstance(s.peek(), Japanese):
return e
stack = [e]
while True:
if not (j := accept_t(s, Japanese)):
stack.append(compare(s))
elif j.id in japanese_outer_commands:
return japanese_outer_commands[j.id](stack)
else:
stack.append(j)
def divisible(stack):
e = stack.pop(0)
mods = [ast.BinOp(copy.deepcopy(e), ast.Mod(), s) for s in stack if not isinstance(s, Japanese)]
return ast.Compare(mods[0], [ast.Eq() for _ in range(len(mods))], mods[1:] + [ast.Constant(0)])
def equals(stack):
if len(stack) == 3 and isinstance(stack[1], Japanese) and stack[1].id == 'のいずれかの要素の値が':
return ast.Compare(stack[2], [ast.In()], [stack[0]])
else:
return ast.Compare(stack[0], [ast.Eq() for _ in range(len(stack) - 1)], [s for s in stack[1:] if not isinstance(s, Japanese)])
def not_equals(stack):
return ast.Compare(stack[0], [ast.NotEq()], [stack[2]])
def greater(stack):
return ast.Compare(stack[0], [ast.Gt()], [stack[2]])
def less(stack):
return ast.Compare(stack[0], [ast.Lt()], [stack[2]])
def greater_equal(stack):
return ast.Compare(stack[0], [ast.GtE()], [stack[2]])
def less_equal(stack):
return ast.Compare(stack[0], [ast.LtE()], [stack[2]])
def upto(stack):
b, _, e, _, step = stack
return ast.Call(ast.Name('range'), [b, ast.BinOp(e, ast.Add(), ast.Constant(1)), step], [])
def downto(stack):
b, _, e, _, step = stack
return ast.Call(ast.Name('range'), [b, ast.BinOp(e, ast.Sub(), ast.Constant(1)), ast.UnaryOp(ast.USub(), step)], [])
def is_not_none(stack):
return ast.Compare(stack[0], [ast.IsNot()], [ast.Constant(None)])
def is_none(stack):
return ast.Compare(stack[0], [ast.Is()], [ast.Constant(None)])
def sum_(stack):
if len(stack) == 3:
if stack[1].id == 'の列番号':
return ast.Call(ast.Name('sum'),
[ast.GeneratorExp(ast.Subscript(ast.Name('i_'), ast.BinOp(stack[2], ast.Sub(), ast.Constant(1))),
[ast.comprehension(ast.Name('i_', ast.Store()), stack[0], [], 0)])], [])
else:
return ast.Call(ast.Name('sum'), [ast.Subscript(stack[0], ast.BinOp(stack[2], ast.Sub(), ast.Constant(1)))], [])
type_ = get_type(stack[0])
if isinstance(type_, ast.Subscript) and isinstance(type_.slice, ast.Subscript):
return ast.Call(ast.Name('sum'),
[ast.GeneratorExp(ast.Call(ast.Name('sum'), [ast.Name('i_')], []),
[ast.comprehension(ast.Name('i_', ast.Store()), stack[0], [], 0)])], [])
else:
return ast.Call(ast.Name('sum'), [stack[0]], [])
def sort_uniq(stack):
return ast.Call(ast.Name('sorted'),
[ast.Call(ast.Name('set'),
[ast.Call(ast.Name('sum'), [stack[0]], [ast.keyword('start', ast.List([]))])], [])], [])
def remove(stack):
return ast.ListComp(ast.Name('i_'),
[ast.comprehension(ast.Name('i_', ast.Store()), stack[0],
[ast.Compare(ast.Name('i_'), [ast.NotEq()], [stack[2]])], 0)])
japanese_outer_commands = {
'で割り切れる': divisible,
'と等しい': equals,
'と等しくない': not_equals,
'より大きい': greater,
'より小さい': less,
'以上': greater_equal,
'以下': less_equal,
'ずつ増やす': upto,
'ずつ減らす': downto,
'未定義ではない': is_not_none,
'未定義': is_none,
'の要素の和': sum_,
'重複なく辞書順に格納した配列': sort_uniq,
'要素を除いた配列': remove,
}
def compare(s):
left = bit_or(s)
os, cs = [], []
while o := accept(s, '=', '≠', '<', '≦', '>', '≧'):
os.append({'=': ast.Eq, '≠': ast.NotEq, '<': ast.Lt, '≦': ast.LtE, '>': ast.Gt, '≧': ast.GtE}[o]())
cs.append(bit_or(s))
if os:
return ast.Compare(left, os, cs)
else:
return left
def bit_or(s):
ret = bit_and(s)
while accept(s, '∨'):
ret = ast.BinOp(ret, ast.BitOr(), bit_and(s))
return ret
def bit_and(s):
ret = shift(s)
while accept(s, '∧'):
ret = ast.BinOp(ret, ast.BitAnd(), shift(s))
return ret
def shift(s):
ret = expression(s)
while o := accept(s, '>>', '<<'):
ret = ast.BinOp(ret, {'>>': ast.RShift, '<<': ast.LShift}[o](), expression(s))
return ret
def expression(s):
ret = term(s)
while o := accept(s, '+', '-'):
ret = ast.BinOp(ret, {'+': ast.Add, '-': ast.Sub}[o](), term(s))
return ret
def term(s):
ret = japanese_inner(s)
while o := accept(s, 'mod', '×', '÷'):
rhs = japanese_inner(s)
if o == 'mod':
ret = ast.BinOp(ret, ast.Mod(), rhs)
elif o == '×':
ret = ast.BinOp(ret, ast.Mult(), rhs)
elif accept(s, 'の商'):
ret = ast.BinOp(ret, ast.FloorDiv(), rhs)
elif accept(s, 'の余り'):
ret = ast.BinOp(ret, ast.Mod(), rhs)
else:
ret = ast.BinOp(ret, ast.Div(), rhs)
return ret
def japanese_inner(s):
stack = [unaryop(s)]
if not (isinstance(s.peek(), Japanese) and s.peek().id in japanese_inner_tokens):
return stack[0]
while True:
if not (j := accept_t(s, Japanese)):
stack.append(unaryop(s))
elif j.id in japanese_inner_commands:
stack = [japanese_inner_commands[j.id](stack)]
if not (isinstance(s.peek(), Japanese) and s.peek().id in japanese_inner_tokens):
return stack[0]
else:
stack.append(j)
def array2d(stack):
return ast.Subscript(ast.Name('list'), ast.Subscript(ast.Name('list'), stack[0]))
def array(stack):
return ast.Subscript(ast.Name('list'), stack[0])
def length(stack):
return ast.Call(ast.Name('len'), [stack[0]], [])
def length_col(stack):
return ast.Call(ast.Name('len'), [ast.Subscript(stack[0], ast.Constant(0))], [])
def ceil(stack):
global_imports.add('math')
return ast.Call(ast.Attribute(ast.Name('math'), 'ceil'), [stack[0]], [])
def floor(stack):
global_imports.add('math')
return ast.Call(ast.Attribute(ast.Name('math'), 'floor'), [stack[0]], [])
def bit_type(_):
return ast.Name('int')
def sqrt(stack):
global_imports.add('math')
return ast.Call(ast.Attribute(ast.Name('math'), 'sqrt'), [stack[0]], [])
def spow(stack):
return ast.BinOp(stack[0], ast.Pow(), stack[2])
def const(stack):
return stack[0]
def subscript(stack):
if stack[1] == Japanese('の末尾から'):
return ast.Subscript(stack[0], ast.UnaryOp(ast.USub(), stack[2]))
else:
return ast.Subscript(stack[0], ast.BinOp(stack[2], ast.Sub(), ast.Constant(1)))
def int_(stack):
return ast.Call(ast.Name('int'), [stack[0]], [])
japanese_inner_commands = {
'の二次元配列': array2d,
'の配列': array,
'の要素数': length,
'の列数': length_col,
'の小数点以下を切り上げた値': ceil,
'の小数点以下を切り捨てた値': floor,
'ビット型': bit_type,
'の正の平方根': sqrt,
'乗': spow,
'文字だけから成る文字列': const,
'文字目の文字': subscript,
'を整数型に変換した値': int_,
}
japanese_inner_tokens = {'の', 'の末尾から'}.union(japanese_inner_commands)
def unaryop(s):
stack = []
while o := accept(s, 'not', '+', '-'):
stack.append({'not': ast.Not, '+': ast.UAdd, '-': ast.USub}[o]())
ret = factor(s)
while True:
if accept(s, '.'):
ret = ast.Attribute(ret, expect_t(s, Ident).id)
elif accept(s, '('):
ret = ast.Call(ret, comma_separated(s, ')', bool_or), [])
elif accept(s, '['):
for i in comma_separated(s, ']', bool_or):
ret = ast.Subscript(ret, ast.BinOp(i, ast.Sub(), ast.Constant(1)))
else:
break
for s in stack[::-1]:
ret = ast.UnaryOp(s, ret)
return ret
def comma_separated(s, end, func):
if accept(s, end):
return []
ret = [func(s)]
while accept(s, ','):
ret.append(func(s))
expect(s, end)
return ret
def factor(s):
if i := accept_t(s, Ident):
return ast.Name(i.id)
elif (n := accept_t(s, int)) is not None:
return ast.Constant(n)
elif (f := accept_t(s, float)) is not None:
return ast.Constant(f)
elif string := accept_t(s, String):
return ast.Constant(string.val)
elif accept(s, '('):
ret, _ = bool_or(s), expect(s, ')')
return ret
elif accept(s, '{'):
if accept(s, '}'):
return ast.List([])
elts = [bool_or(s)]
if accept(s, '個の'):
v, _ = bool_or(s), expect(s, '}')
return ast.BinOp(ast.List([v]), ast.Mult(), elts[0])
elif accept(s, '行'):
n, _, v, _ = bool_or(s), expect(s, '列の'), bool_or(s), expect(s, '}')
return ast.ListComp(ast.BinOp(ast.List([v]), ast.Mult(), n),
[ast.comprehension(ast.Name('_', ast.Store()), ast.Call(ast.Name('range'), [elts[0]], []), [], 0)])
while accept(s, ','):
elts.append(bool_or(s))
expect(s, '}')
return ast.List(elts)
elif accept(s, '未定義の値'):
return ast.Constant(None)
elif accept(s, 'true'):
return ast.Constant(True)
elif accept(s, 'false'):
return ast.Constant(False)
elif accept(s, '∞'):
global_imports.add('math')
return ast.Attribute(ast.Name('math'), 'inf')
else:
raise SyntaxError(s.peek())
def accept_t(s, t):
if isinstance(s.peek(), t):
return next(s)
return None
def expect_t(s, t):
if (a := accept_t(s, t)) is not None:
return a
raise SyntaxError(s.peek())
def accept(s, *ss):
if s.peek() in ss:
return next(s)
return None
def expect(s, *ss):
if a := accept(s, *ss):
return a
raise SyntaxError(s.peek())
def scan(text):
space = re.compile(r'(?:\s|/\*.*?\*/|//[^\r\n]*)*', re.DOTALL)
pattern = re.compile(r'''
((?:if|elseif|else|endif|while|endwhile|do|for|endfor|not|mod|and|or|true|false|return)\b|
[][().{}+-×÷≠≦≧<=>∧∨∞○←,@&|=*/+-]|>[>=]?|<[<=]?|!=|:=?)|
([0-9]+(?:\.[0-9]*)?)|
"([^"]*)"|
([\u3005\u4E00-\u9FCFぁ-んァ-ヶー\u2460-\u24FF\u2776-\u277F\u3251-\u32BF]+)|
((?:(?![\u3005\u4E00-\u9FCFぁ-んァ-ヶー\u2460-\u24FF\u2776-\u277F\u3251-\u32BF])\w)+)''', re.VERBOSE)
re_japanese_tokens = re.compile('|'.join(f'({t})' for t, _ in japanese_tokens))
pos, func_def = space.match(text).end(), False
while pos < len(text):
m = pattern.match(text, pos)
if not m:
raise SyntaxError(text[pos:pos+10])
pos = m.end()
if m[1]:
v = convert_token[m[1]] if m[1] in convert_token else m[1]
if v == '○':
func_def = True
yield v
elif m[2]:
if '.' in m[2]:
yield float(m[2])
else:
yield int(m[2])
elif m[4]:
for mm in re_japanese_tokens.finditer(m[4]):
i = [p[1][1] for p in zip(mm.groups(), japanese_tokens) if p[0]][0]
if i is ...:
yield Japanese(mm[0])
elif i is not None:
yield i
elif m[5]:
i = m[5]
if i in python_keywords:
i += '_'
yield Ident(i)
else:
yield String(m[3])
m = space.match(text, pos)
if func_def and m[0] and m[0][-1] in '\r\n':
func_def = False
yield 'ENDDEF'
pos = m.end()
if func_def:
yield "ENDDEF"
yield 'EOF'
@dataclass
class Ident:
id: str
@dataclass
class String:
val: str
@dataclass
class Japanese:
id: str
convert_token = {
'@': '○',
':=': '←',
'+': '+',
'-': '-',
'*': '×',
'/': '÷',
'=': '=',
'!=': '≠',
'<': '<',
'>': '>',
'<=': '≦',
'>=': '≧',
'&': '∧',
'|': '∨',
}
japanese_tokens = (
('重複なく辞書順に格納した配列', ...),
('の小数点以下を切り上げた値', ...),
('の小数点以下を切り捨てた値', ...),
('文字だけから成る文字列', ...),
('のいずれかの要素の値が', ...),
('を整数型に変換した値', ...),
('の要素を順に代入する', None),
('に含まれる文字列を', ...),
('繰返し処理を終了', ...),
('を超えない範囲で', ...),
('要素を除いた配列', ...),
('の値を入れ替え', ...),
('未定義ではない', ...),
('の行から始まる', ...),
('要素番号の順に', Japanese('先頭から順に')),
('コンマ区切りで', ...),
('の複製から値が', ...),
('で割り切れる', ...),
('と等しくない', ...),
('の二次元配列', ...),
('の正の平方根', ...),
('文字目の文字', ...),
('の全ての要素', ...),
('の全要素の値', Japanese('の全ての要素')),
('空白区切りで', ...),
('先頭から順に', ...),
('ずつ増やす', ...),
('ずつ減らす', ...),
('より大きい', ...),
('より小さい', ...),
('未定義の値', '未定義の値'),
('の整数部分', Japanese('を整数型に変換した値')),
('から始めて', ...),
('番目の文字', Japanese('文字目の文字')),
('の末尾から', ...),
('の要素の和', ...),
('と等しい', ...),
('文字列型', Ident('str')),
('の要素数', ...),
('の文字数', Japanese('の要素数')),
('ビット型', ...),
('の戻り値', None),
('この順に', Japanese('先頭から順に')),
('の行番号', ...),
('の列番号', ...),
('文字型', Ident('str')),
('整数型', Ident('int')),
('実数型', Ident('float')),
('論理型', Ident('bool')),
('の余り', 'の余り'),
('の行数', Japanese('の要素数')),
('の列数', ...),
('の末尾', ...),
('の結果', None),
('未定義', ...),
('でない', Japanese('と等しくない')),
('の配列', ...),
('を追加', ...),
('増やす', ...),
('出力', ...),
('以上', ...),
('以下', ...),
('の商', 'の商'),
('の値', None),
('大域', '大域'),
('から', ...),
('まで', ...),
('配列', Japanese('の配列')),
('個の', '個の'),
('列の', '列の'),
('に', ...),
('が', ...),
('と', ...),
('を', ...),
('の', ...),
('乗', ...),
('行', '行'),
)
python_keywords = {
'False', 'await', 'else', 'import', 'pass',
'None', 'break', 'except', 'in', 'raise',
'True', 'class', 'finally', 'is', 'return',
'and', 'continue', 'for', 'lambda', 'try',
'as', 'def', 'from', 'nonlocal', 'while',
'assert', 'del', 'global', 'not', 'with',
'async', 'elif', 'if', 'or', 'yield',
}
argparser = argparse.ArgumentParser()
argparser.add_argument('infile', nargs='?', default=None)
argparser.add_argument('-o', '--outfile')
argparser.add_argument('-e', '--execute', action='store_true')
args = argparser.parse_args()
if args.infile:
with open(args.infile) as f:
text = f.read()
else:
text = sys.stdin.read()
if args.execute:
exec(ast.unparse(parse(scan(text))), globals={})
elif args.outfile:
with open(args.outfile, 'w') as f:
f.write(ast.unparse(parse(scan(text))))
else:
print(ast.unparse(parse(scan(text))))