サンプルプログラム
$\sum_{i = 1}^W A_{1, i}$ と $\sum_{i = 1}^W B_{1, i}$ のうち、小さい方を変数 $1$ に、大きい方を変数 $2$ に格納し、それらを出力するプログラムの一例です。
$0 = 1
while($0 <= W)
$1 = $1 + A[1,$0]
$2 = $2 + B[1,$0]
$0 = $0 + 1
end
if($1 > $2)
$3 = $1
$1 = $2
$2 = $3
end
return($1, $2)
Simplang の BNF 表記
Simplang プログラムは、以下の BNF の <program>
である必要があります。
<program> ::= <statements> "return" <result> <endlines>
<result> ::= "(" <expression> "," <expression> ")"
<statements> ::= <statement> | <statements> <endlines> <statement>
<statement> ::= <assignment> | <if_statement> | <while_statement>
<assignments> ::= <assignment> | <assignments> <endlines> <assignment>
<assignment> ::= <variable> "=" <expression>
<if_statement> ::= "if(" <condition> ")" <endlines> <assignments> <endlines> "end"
<while_statement> ::= "while(" <condition> ")" <endlines> <assignments> <endlines> "end"
<condition> ::= <statement> <compare_operator> <statement>
<compare_operator> ::= "<" | "<=" | ">" | ">=" | "==" | "!="
<expression> ::= <term> | <expression> "+" <term> | <expression> "-" <term>
<term> ::= <value> | <term> "*" <value> | <term> "/" <value> | <term> "%" <value>
<value> ::= <variable> | <constant> | <number> | <expression>
<variable> ::= "$" <number>
<constant> ::= "H" | "W" | "A[" <expression> "," <expression> "]" | "B[" <expression> "," <expression> "]"
<number> ::= 0 以上 10の100乗 未満の整数
<endlines> ::= "\n" | <endlines> "\n"
Simplang のインタプリタ実装(Python)
Python3
または PyPy3
で実行することができます。
標準入力から作成した Simplang プログラムを与えることで、実行されます。
ただし、仕様を満たさないプログラムを与えた場合、実行時エラーになります。
仕様は yukicoder: Simple Programming Language を参照してください。
from __future__ import annotations
from dataclasses import dataclass
from typing import List, Optional, Tuple, Union
import sys
# 実行行数の上限定数
LIMIT_TEXT_LENGTH = 100
LIMIT_ROW_LENGTH = 100
LIMIT_EXEC_ROW = 10**5
LIMIT_VALUE = 10**100
##############################
# ASTクラスの定義
##############################
class ASTNode:
pass
# <program> ::= <statements> "return" <result> <endlines>
# <result> ::= "(" <expression> "," <expression> ")"
@dataclass
class Program(ASTNode):
statements: List[Statement]
result: Tuple[Expression, Expression]
class Statement(ASTNode):
pass
# <assignment> ::= <variable> "=" <expression>
@dataclass
class Assignment(Statement):
variable: Variable
expression: Expression
# <if_statement> ::= "if(" <condition> ")" <endlines> <assignments> <endlines> "end"
@dataclass
class IfStatement(Statement):
condition: Condition
assignments: List[Assignment]
# <while_statement> ::= "while(" <condition> ")" <endlines> <assignments> <endlines> "end"
@dataclass
class WhileStatement(Statement):
condition: Condition
assignments: List[Assignment]
# <condition> ::= <statement> <compare_operator> <statement>
@dataclass
class Condition(ASTNode):
left: Expression
operator: str # "<", "<=", ">", ">=", "==", "!="
right: Expression
class Expression(ASTNode):
pass
# <expression> ::= <expression> ("+"|"-") <term> | <term>
@dataclass
class BinOp(Expression):
left: Expression
op: str # '+', '-', '*', '/', '%'
right: Expression
# 数値リテラル
@dataclass
class Num(Expression):
value: int
# <variable> ::= "$" <number>
@dataclass
class Variable(Expression):
index: int
# <constant> ::= "H" | "W" | "A[" <expression> "," <expression> "]" | "B[" <expression> "," <expression> "]"
@dataclass
class Constant(Expression):
name: str # "H", "W", "A", "B"
args: Optional[Tuple[Expression, Expression]] = None
##############################
# Lexer: 字句解析器
##############################
@dataclass
class Token:
type: str
value: Optional[str] = None
# トークンの種類
TT_NEWLINE = "NEWLINE"
TT_IF = "IF"
TT_WHILE = "WHILE"
TT_END = "END"
TT_RETURN = "RETURN"
TT_NUMBER = "NUMBER"
TT_DOLLAR = "DOLLAR"
TT_IDENTIFIER = "IDENTIFIER"
TT_PLUS = "PLUS"
TT_MINUS = "MINUS"
TT_MUL = "MUL"
TT_DIV = "DIV"
TT_MOD = "MOD"
TT_LPAREN = "LPAREN"
TT_RPAREN = "RPAREN"
TT_LBRACKET = "LBRACKET"
TT_RBRACKET = "RBRACKET"
TT_COMMA = "COMMA"
TT_EQUAL = "EQUAL" # '='
TT_LT = "LT"
TT_LE = "LE"
TT_GT = "GT"
TT_GE = "GE"
TT_EQ = "EQ" # "=="
TT_NE = "NE" # "!="
TT_EOF = "EOF"
class Lexer:
def __init__(self, text: str):
self.text = text
self.pos = 0
self.current_char: Optional[str] = self.text[self.pos] if self.text else None
def advance(self):
self.pos += 1
if self.pos < len(self.text):
self.current_char = self.text[self.pos]
else:
self.current_char = None
def peek(self):
peek_pos = self.pos + 1
if peek_pos < len(self.text):
return self.text[peek_pos]
return None
def skip_whitespace(self):
while self.current_char is not None and self.current_char in " \t":
self.advance()
def number(self) -> Token:
result = ''
while self.current_char is not None and self.current_char.isdigit():
result += self.current_char
self.advance()
return Token(TT_NUMBER, result)
def identifier(self) -> Token:
result = ''
while self.current_char is not None and self.current_char.isalpha():
result += self.current_char
self.advance()
if result == "if":
return Token(TT_IF, result)
elif result == "while":
return Token(TT_WHILE, result)
elif result == "return":
return Token(TT_RETURN, result)
elif result == "end":
return Token(TT_END, result)
else:
return Token(TT_IDENTIFIER, result)
def tokenize(self) -> List[Token]:
tokens: List[Token] = []
while self.current_char is not None:
if self.current_char in " \t":
self.skip_whitespace()
continue
if self.current_char == "\n":
tokens.append(Token(TT_NEWLINE, "\n"))
self.advance()
continue
if self.current_char == '<':
self.advance()
if self.current_char == '=':
tokens.append(Token(TT_LE, "<="))
self.advance()
else:
tokens.append(Token(TT_LT, "<"))
continue
if self.current_char == '>':
self.advance()
if self.current_char == '=':
tokens.append(Token(TT_GE, ">="))
self.advance()
else:
tokens.append(Token(TT_GT, ">"))
continue
if self.current_char == '=':
self.advance()
if self.current_char == '=':
tokens.append(Token(TT_EQ, "=="))
self.advance()
else:
tokens.append(Token(TT_EQUAL, "="))
continue
if self.current_char == '!':
self.advance()
if self.current_char == '=':
tokens.append(Token(TT_NE, "!="))
self.advance()
else:
raise Exception("予期しない文字 '!'")
continue
if self.current_char == '+':
tokens.append(Token(TT_PLUS, "+"))
self.advance()
continue
if self.current_char == '-':
tokens.append(Token(TT_MINUS, "-"))
self.advance()
continue
if self.current_char == '*':
tokens.append(Token(TT_MUL, "*"))
self.advance()
continue
if self.current_char == '/':
tokens.append(Token(TT_DIV, "/"))
self.advance()
continue
if self.current_char == '%':
tokens.append(Token(TT_MOD, "%"))
self.advance()
continue
if self.current_char == '(':
tokens.append(Token(TT_LPAREN, "("))
self.advance()
continue
if self.current_char == ')':
tokens.append(Token(TT_RPAREN, ")"))
self.advance()
continue
if self.current_char == '[':
tokens.append(Token(TT_LBRACKET, "["))
self.advance()
continue
if self.current_char == ']':
tokens.append(Token(TT_RBRACKET, "]"))
self.advance()
continue
if self.current_char == ',':
tokens.append(Token(TT_COMMA, ","))
self.advance()
continue
if self.current_char == '$':
tokens.append(Token(TT_DOLLAR, "$"))
self.advance()
continue
if self.current_char.isdigit():
tokens.append(self.number())
continue
if self.current_char.isalpha():
tokens.append(self.identifier())
continue
raise Exception(f"不明な文字: {self.current_char}")
tokens.append(Token(TT_EOF, None))
return tokens
##############################
# Parser: 再帰下降パーサー
##############################
class Parser:
def __init__(self, tokens: List[Token]):
self.tokens = tokens
self.pos = 0
def current_token(self) -> Token:
return self.tokens[self.pos]
def eat(self, token_type: str, token_value: Optional[str] = None):
token = self.current_token()
if token.type == token_type and (token_value is None or token.value == token_value):
self.pos += 1
else:
raise Exception(f"期待されたトークン {token_type}({token_value}) ですが、得られたのは {token.type}({token.value})")
def parse(self) -> Program:
stmts = self.parse_statements()
self.eat(TT_RETURN)
result = self.parse_result()
while self.current_token().type == TT_NEWLINE:
self.eat(TT_NEWLINE)
self.eat(TT_EOF)
return Program(statements=stmts, result=result)
def parse_result(self) -> Tuple[Expression, Expression]:
self.eat(TT_LPAREN)
left = self.parse_expression()
self.eat(TT_COMMA)
right = self.parse_expression()
self.eat(TT_RPAREN)
return (left, right)
def parse_statements(self) -> List[Statement]:
stmts = [self.parse_statement()]
while self.current_token().type == TT_NEWLINE:
while self.current_token().type == TT_NEWLINE:
self.eat(TT_NEWLINE)
if self.current_token().type in (TT_RETURN, TT_EOF, TT_END):
break
stmts.append(self.parse_statement())
return stmts
def parse_statement(self) -> Statement:
token = self.current_token()
if token.type == TT_IF:
return self.parse_if_statement()
elif token.type == TT_WHILE:
return self.parse_while_statement()
else:
return self.parse_assignment()
def parse_assignment(self) -> Assignment:
var = self.parse_variable()
self.eat(TT_EQUAL)
expr = self.parse_expression()
return Assignment(variable=var, expression=expr)
def parse_if_statement(self) -> IfStatement:
self.eat(TT_IF)
self.eat(TT_LPAREN)
cond = self.parse_condition()
self.eat(TT_RPAREN)
while self.current_token().type == TT_NEWLINE:
self.eat(TT_NEWLINE)
assigns = self.parse_assignments()
while self.current_token().type == TT_NEWLINE:
self.eat(TT_NEWLINE)
self.eat(TT_END)
return IfStatement(condition=cond, assignments=assigns)
def parse_while_statement(self) -> WhileStatement:
self.eat(TT_WHILE)
self.eat(TT_LPAREN)
cond = self.parse_condition()
self.eat(TT_RPAREN)
while self.current_token().type == TT_NEWLINE:
self.eat(TT_NEWLINE)
assigns = self.parse_assignments()
while self.current_token().type == TT_NEWLINE:
self.eat(TT_NEWLINE)
self.eat(TT_END)
return WhileStatement(condition=cond, assignments=assigns)
def parse_assignments(self) -> List[Assignment]:
assigns = [self.parse_assignment()]
while self.current_token().type == TT_NEWLINE:
self.eat(TT_NEWLINE)
if self.current_token().type in (TT_END, TT_RETURN, TT_EOF):
break
assigns.append(self.parse_assignment())
return assigns
def parse_condition(self) -> Condition:
left_expr = self.parse_expression()
op_token = self.current_token()
if op_token.type in (TT_LT, TT_LE, TT_GT, TT_GE, TT_EQ, TT_NE):
self.eat(op_token.type)
else:
raise Exception("比較演算子が見つかりません")
right_expr = self.parse_expression()
return Condition(left=left_expr, operator=op_token.value, right=right_expr)
def parse_expression(self) -> Expression:
expr = self.parse_term()
while self.current_token().type in (TT_PLUS, TT_MINUS):
op_token = self.current_token()
self.eat(op_token.type)
right = self.parse_term()
expr = BinOp(left=expr, op=op_token.value, right=right)
return expr
def parse_term(self) -> Expression:
term = self.parse_value()
while self.current_token().type in (TT_MUL, TT_DIV, TT_MOD):
op_token = self.current_token()
self.eat(op_token.type)
right = self.parse_value()
term = BinOp(left=term, op=op_token.value, right=right)
return term
def parse_value(self) -> Expression:
token = self.current_token()
if token.type == TT_LPAREN:
self.eat(TT_LPAREN)
expr = self.parse_expression()
self.eat(TT_RPAREN)
return expr
elif token.type == TT_DOLLAR:
return self.parse_variable()
elif token.type == TT_NUMBER:
self.eat(TT_NUMBER)
return Num(int(token.value))
elif token.type == TT_IDENTIFIER:
ident = token.value
self.eat(TT_IDENTIFIER)
if ident in ("H", "W"):
return Constant(name=ident)
elif ident in ("A", "B"):
if self.current_token().type == TT_LBRACKET:
self.eat(TT_LBRACKET)
expr1 = self.parse_expression()
self.eat(TT_COMMA)
expr2 = self.parse_expression()
self.eat(TT_RBRACKET)
return Constant(name=ident, args=(expr1, expr2))
else:
return Constant(name=ident)
else:
raise Exception(f"未知の識別子: {ident}")
else:
raise Exception(f"予期しないトークン: {token.type}({token.value})")
def parse_variable(self) -> Variable:
self.eat(TT_DOLLAR)
token = self.current_token()
if token.type != TT_NUMBER:
raise Exception("変数は '$' に続く数値でなければなりません")
self.eat(TT_NUMBER)
return Variable(index=int(token.value))
##############################
# Interpreter: AST実行器(デバッグ・行数上限チェック付き)
##############################
class Interpreter:
def __init__(self, constants: dict, debug: bool = False):
self.constants = constants # 定数環境: 例: {"H": 3, "W": 3, "A": [[...]], "B": [[...]]}
self.env = {} # 変数環境: 変数番号 -> 値(整数)
self.line_count = 0 # 実行済み行数のカウンタ
self.debug = debug # デバッグモードフラグ
def _increment_line(self, description: str):
self.line_count += 1
if self.line_count > LIMIT_EXEC_ROW:
raise Exception("実行行数が上限を超えました。")
if self.debug:
print(f"Line {self.line_count}: {description}", file=sys.stderr)
def eval_expr(self, expr: Expression) -> int:
if isinstance(expr, Num):
val = expr.value
assert abs(val) <= LIMIT_VALUE, "数値リテラルの絶対値が上限を超えました。"
return val
elif isinstance(expr, Variable):
val = self.env.get(expr.index, 0)
assert abs(val) <= LIMIT_VALUE, "変数の値が上限を超えました。"
return val
elif isinstance(expr, Constant):
if expr.args is None:
if expr.name in ("H", "W"):
return self.constants[expr.name]
else:
raise Exception(f"定数 {expr.name} は引数が必要です")
else:
# AまたはBの場合。引数は1-indexedと仮定して0-indexedに変換
row = self.eval_expr(expr.args[0])
col = self.eval_expr(expr.args[1])
if expr.name in ("A", "B"):
matrix = self.constants[expr.name]
return matrix[row - 1][col - 1]
else:
raise Exception(f"未知の定数: {expr.name}")
elif isinstance(expr, BinOp):
left = self.eval_expr(expr.left)
right = self.eval_expr(expr.right)
if expr.op == '+':
if abs(left + right) >= LIMIT_VALUE:
raise Exception("二項演算の結果が上限を超えました。")
return left + right
elif expr.op == '-':
if abs(left - right) >= LIMIT_VALUE:
raise Exception("二項演算の結果が上限を超えました。")
return left - right
elif expr.op == '*':
if abs(left * right) >= LIMIT_VALUE:
raise Exception("二項演算の結果が上限を超えました。")
return left * right
elif expr.op == '/':
if right == 0:
raise Exception("ゼロ除算エラー")
if abs(left // right) >= LIMIT_VALUE:
raise Exception("二項演算の結果が上限を超えました。")
return left // right
elif expr.op == '%':
if right == 0:
raise Exception("ゼロによる剰余エラー")
if abs(left % right) >= LIMIT_VALUE:
raise Exception("二項演算の結果が上限を超えました。")
return left % right
else:
raise Exception("未知の二項演算子: " + expr.op)
else:
raise Exception("未知の式の型")
def eval_condition(self, cond: Condition) -> bool:
left = self.eval_expr(cond.left)
right = self.eval_expr(cond.right)
op = cond.operator
if op == "<":
return left < right
elif op == "<=":
return left <= right
elif op == ">":
return left > right
elif op == ">=":
return left >= right
elif op == "==":
return left == right
elif op == "!=":
return left != right
else:
raise Exception("未知の比較演算子: " + op)
def execute_statement(self, stmt: Statement):
if isinstance(stmt, Assignment):
self._increment_line(f"Assignment -> {stmt.variable} = {stmt.expression}")
value = self.eval_expr(stmt.expression)
self.env[stmt.variable.index] = value
elif isinstance(stmt, IfStatement):
self._increment_line(f"If statement -> if({stmt.condition})")
if self.eval_condition(stmt.condition):
for s in stmt.assignments:
self.execute_statement(s)
elif isinstance(stmt, WhileStatement):
self._increment_line(f"While statement header -> while({stmt.condition})")
while self.eval_condition(stmt.condition):
for s in stmt.assignments:
self.execute_statement(s)
self._increment_line(f"While statement header -> while({stmt.condition})")
else:
raise Exception("未知の文の型")
def interpret(self, program: Program) -> Tuple[Tuple[int, int], int]:
for stmt in program.statements:
self.execute_statement(stmt)
self._increment_line("Return statement")
ret_left = self.eval_expr(program.result[0])
ret_right = self.eval_expr(program.result[1])
return (ret_left, ret_right), self.line_count
###############################
# プログラムの取得
###############################
def get_output_code():
rows = []
row_count = 0
for _ in range(LIMIT_ROW_LENGTH + 1):
try:
line = input()
rows.append(line)
assert len(line) < LIMIT_TEXT_LENGTH, "行の長さが上限を超えました。"
except EOFError:
break
assert len(rows) <= LIMIT_ROW_LENGTH, "行数が上限を超えました。"
return "\n".join(rows) + "\n"
##############################
# 使用例
##############################
if __name__ == "__main__":
output_code = get_output_code()
# 字句解析
lexer = Lexer(output_code)
tokens = lexer.tokenize()
# 構文解析
parser = Parser(tokens)
ast = parser.parse()
# 定数の設定(例として3x3の行列を用いる)
constants = {
"H": 3,
"W": 3,
"A": [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
],
"B": [
[1, 2, 3],
[4, 5, 6],
[7, 9, 9]
]
}
# インタプリタの生成(デバッグモードをOFFにする場合はdebug=False)
interpreter = Interpreter(constants, debug=True)
# 実行(結果と実行行数を取得)
result, executed_lines = interpreter.interpret(ast)
print("Result:", result)
print("Executed lines:", executed_lines, file=sys.stderr)