Monkey言語:冪乗の二項演算子"^"実装
目標
「2^3」のような冪乗の演算をMonkey言語に実装する.
字句解析
トークンの定義
token/token.go にHAT = `^`を追加.(p.4)
const (
ILLEGAL = "ILLEGAL" //トークンや文字が未知であることを示す
EOF = "EOF"
//識別子,リテラル
IDENT = "IDENT" //関数や変数など
INT = "INT" //整数
STRING = "STRING"
FLOAT = "FLOAT"
//演算子
ASSIGN = "="
PLUS = "+"
MINUS = "-"
BANG = "!"
ASTERISK = "*"
SLASH = "/"
HAT = "^"
字句解析器の拡張
lexer/lexer.go のNextToken関数にHATの場合分けを追加.(p.16)
func (l *Lexer) NextToken() token.Token {
var tok token.Token
l.skipWhitespace()
switch l.ch {
case '=':
if l.peekChar() == '='{
ch := l.ch
l.readChar()
literal := string(ch) + string(l.ch)
tok = token.Token{Type: token.EQ, Literal: literal}
}else{
tok = newToken(token.ASSIGN, l.ch)
}
case '+':
tok = newToken(token.PLUS, l.ch)
case '-':
tok = newToken(token.MINUS, l.ch)
case '!':
if l.peekChar() == '='{
ch := l.ch
l.readChar()
literal := string(ch) + string(l.ch)
tok = token.Token{Type: token.NOT_EQ, Literal: literal}
}else{
tok = newToken(token.BANG, l.ch)
}
case '/':
tok = newToken(token.SLASH, l.ch)
case '*':
tok = newToken(token.ASTERISK, l.ch)
case '^':
tok = newToken(token.HAT, l.ch)
構文解析
優先順位の追加
parser/parser.go で優先順位”POWER"を追加.(p.58)
2^2*4 tの時に256ではなく16となるように冪乗の優先順位をPRODUCTよりもひとつ高く設定.
const(
_ int = iota
LOWEST
EQUALS
LESSGREATER
SUM
PRODUCT
POWER
PREFIX //-X or !X
CALL //myFunction(X)
INDEX //array[index]
)
中置演算子のマップ拡張
parser/parser.go で中置演算子のマップを次のように拡張.(p.71)
var precedences = map[token.TokenType]int{
token.EQ: EQUALS,
token.NOT_EQ: EQUALS,
token.LT: LESSGREATER,
token.GT: LESSGREATER,
token.PLUS: SUM,
token.MINUS: SUM,
token.SLASH: PRODUCT,
token.ASTERISK: PRODUCT,
token.HAT: POWER,
token.LPAREN: CALL,
token.LBRACKET: INDEX,
}
中置構文解析の登録
(p.72)
func New(l *lexer.Lexer) *Parser{
p := &Parser{
l: l,
errors: []string{},
}
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral)
p.registerPrefix(token.BANG, p.parsePrefixExpression)
p.registerPrefix(token.MINUS, p.parsePrefixExpression)
p.registerPrefix(token.TRUE, p.parseBoolean)
p.registerPrefix(token.FALSE, p.parseBoolean)
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
p.registerPrefix(token.IF, p.parseIfExpression)
p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
p.registerPrefix(token.STRING, p.parseStringLiteral)
p.registerPrefix(token.LBRACKET, p.parseArrayLiteral)
p.registerPrefix(token.LBRACE, p.parseHashLiteral)
p.registerPrefix(token.WHILE, p.parseWhileExpression)
p.registerPrefix(token.FLOAT, p.parseFloatLiteral)
p.registerPrefix(token.LOOP, p.parseLoopExpression)
p.infixParseFns = make(map[token.TokenType]infixParseFn)
p.registerInfix(token.PLUS, p.parseInfixExpression)
p.registerInfix(token.MINUS, p.parseInfixExpression)
p.registerInfix(token.SLASH, p.parseInfixExpression)
p.registerInfix(token.ASTERISK, p.parseInfixExpression)
p.registerInfix(token.EQ, p.parseInfixExpression)
p.registerInfix(token.NOT_EQ, p.parseInfixExpression)
p.registerInfix(token.LT, p.parseInfixExpression)
p.registerInfix(token.GT, p.parseInfixExpression)
p.registerInfix(token.HAT, p.parseInfixExpression)
p.registerInfix(token.LPAREN, p.parseCallExpression)
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
評価器
HATの評価
evaluator/evaluator.go でHATの評価分岐を追加.(p.137)
func evalIntegerInfixExpression(operator string, left, right object.Object) object.Object{
leftVal := left.(*object.Integer).Value
rightVal := right.(*object.Integer).Value
switch operator{
case "+":
return &object.Integer{Value: leftVal + rightVal}
case "-":
return &object.Integer{Value: leftVal - rightVal}
case "*":
return &object.Integer{Value: leftVal * rightVal}
case "/":
return &object.Integer{Value: leftVal / rightVal}
case "^":
return &object.Integer{Value: math.Pow(leftVal, rightVal)}
case "<":
return nativeBoolToBooleanObject(leftVal < rightVal)
case ">":
return nativeBoolToBooleanObject(leftVal > rightVal)
case "==":
return nativeBoolToBooleanObject(leftVal == rightVal)
case "!=":
return nativeBoolToBooleanObject(leftVal != rightVal)
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}
func evalFloatInfixExpression(operator string, left, right object.Object) object.Object{
leftVal := left.(*object.Float).Value
rightVal := right.(*object.Float).Value
switch operator{
case "+":
return &object.Float{Value: leftVal + rightVal}
case "-":
return &object.Float{Value: leftVal - rightVal}
case "*":
return &object.Float{Value: leftVal * rightVal}
case "/":
return &object.Float{Value: leftVal / rightVal}
case "^":
return &object.Float{Value: math.Pow(leftVal, rightVal)}
case "<":
return nativeBoolToBooleanObject(leftVal < rightVal)
case ">":
return nativeBoolToBooleanObject(leftVal > rightVal)
case "==":
return nativeBoolToBooleanObject(leftVal == rightVal)
case "!=":
return nativeBoolToBooleanObject(leftVal != rightVal)
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
この記事が気に入ったらサポートをしてみませんか?