diff --git a/ast/ast.go b/ast/ast.go index c42241f..48f58c1 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -119,6 +119,15 @@ func (il *IntegerLiteral) expressionNode() {} func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal } func (il *IntegerLiteral) String() string { return il.Token.Literal } +type HexadecimalLiteral struct { + Token token.Token + Value int64 +} + +func (hl *HexadecimalLiteral) expressionNode() {} +func (hl *HexadecimalLiteral) TokenLiteral() string { return hl.Token.Literal } +func (hl *HexadecimalLiteral) String() string { return hl.Token.Literal } + type PrefixExpression struct { Token token.Token Operator string diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 5fd219d..25fb60f 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -26,6 +26,9 @@ func Eval(node ast.Node, env *object.Environment) object.Object { case *ast.IntegerLiteral: return &object.Integer{Value: node.Value} + case *ast.HexadecimalLiteral: + return &object.Hexadecimal{Value: node.Value} + case *ast.FloatLiteral: return &object.Float{Value: node.Value} diff --git a/evaluator/infix.go b/evaluator/infix.go index 2e51052..743b605 100644 --- a/evaluator/infix.go +++ b/evaluator/infix.go @@ -1,6 +1,7 @@ package evaluator import ( + "fmt" "math" "strings" @@ -68,17 +69,17 @@ func evalInfixExpression(operator string, left, right object.Object, line int) o rightVal := right.(*object.String).Value return &object.String{Value: strings.Repeat(rightVal, int(leftVal))} - case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ: - return evalIntegerInfixExpression(operator, left, right, line) + case isNumber(left) && isNumber(right): + return evalNumberInfixExpression(operator, left, right, line) case left.Type() == object.FLOAT_OBJ && right.Type() == object.FLOAT_OBJ: return evalFloatInfixExpression(operator, left, right, line) - case left.Type() == object.INTEGER_OBJ && right.Type() == object.FLOAT_OBJ: - return evalFloatIntegerInfixExpression(operator, left, right, line) + case isNumber(left) && right.Type() == object.FLOAT_OBJ: + return evalFloatInfixExpr(operator, left, right, line) - case left.Type() == object.FLOAT_OBJ && right.Type() == object.INTEGER_OBJ: - return evalFloatIntegerInfixExpression(operator, left, right, line) + case left.Type() == object.FLOAT_OBJ && isNumber(right): + return evalFloatInfixExpr(operator, left, right, line) case operator == "==": return nativeBoolToBooleanObject(left == right) @@ -98,14 +99,94 @@ func evalInfixExpression(operator string, left, right object.Object, line int) o } } -func evalFloatIntegerInfixExpression(operator string, left, right object.Object, line int) object.Object { - var leftVal, rightVal float64 +// This are types that can be categorized as a number. +// Hexadecimal, Integer and other types except Float can be consided number +func isNumber(number object.Object) bool { + switch number.(type) { + case *object.Integer, *object.Hexadecimal: + return true + } + + return true +} + +func evalNumberInfixExpression(operator string, left, right object.Object, line int) object.Object { + leftVal, err := getNumberValue(left) + if err != nil { + return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", + line, left.Type(), operator, right.Type()) + } + + rightVal, err := getNumberValue(right) + if err != nil { + return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", + line, left.Type(), operator, right.Type()) + } + + 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.Float{Value: float64(math.Pow(float64(leftVal), float64(rightVal)))} + case "/": + x := float64(leftVal) / float64(rightVal) + if math.Mod(x, 1) == 0 { + return &object.Integer{Value: int64(x)} + } else { + return &object.Float{Value: x} + } + case "%": + return &object.Integer{Value: leftVal % rightVal} + case "<": + return nativeBoolToBooleanObject(leftVal < rightVal) + case "<=": + return nativeBoolToBooleanObject(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("Mstari %d: Operesheni Haieleweki: %s %s %s", + line, left.Type(), operator, right.Type()) + } + +} + +func evalFloatInfixExpr(operator string, left, right object.Object, line int) object.Object { + var rightVal float64 + var leftVal float64 + var _tempv int64 + var err error + if left.Type() == object.FLOAT_OBJ { - leftVal = left.(*object.Float).Value - rightVal = float64(right.(*object.Integer).Value) + leftVal, err = getFloatValue(left) + } else { + _tempv, err = getNumberValue(left) + leftVal = float64(_tempv) + } + if err != nil { + return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", + line, left.Type(), operator, right.Type()) + } + + if right.Type() == object.FLOAT_OBJ { + rightVal, err = getFloatValue(right) } else { - leftVal = float64(left.(*object.Integer).Value) - rightVal = right.(*object.Float).Value + _tempv, err = getNumberValue(right) + rightVal = float64(_tempv) + } + + if err != nil { + return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", + line, left.Type(), operator, right.Type()) } var val float64 @@ -146,6 +227,27 @@ func evalFloatIntegerInfixExpression(operator string, left, right object.Object, } } +func getNumberValue(val object.Object) (int64, error) { + switch val.(type) { + case *object.Hexadecimal: + return val.(*object.Hexadecimal).Value, nil + case *object.Integer: + return val.(*object.Integer).Value, nil + } + + return 0, fmt.Errorf("expected integer, got %T", val) +} + +func getFloatValue(val object.Object) (float64, error) { + + switch val.(type) { + case *object.Float: + return val.(*object.Float).Value, nil + } + + return 0.0, fmt.Errorf("Si desimali") +} + func evalStringInfixExpression(operator string, left, right object.Object, line int) object.Object { leftVal := left.(*object.String).Value @@ -209,43 +311,3 @@ func evalFloatInfixExpression(operator string, left, right object.Object, line i line, left.Type(), operator, right.Type()) } } - -func evalIntegerInfixExpression(operator string, left, right object.Object, line int) 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.Float{Value: float64(math.Pow(float64(leftVal), float64(rightVal)))} - case "/": - x := float64(leftVal) / float64(rightVal) - if math.Mod(x, 1) == 0 { - return &object.Integer{Value: int64(x)} - } else { - return &object.Float{Value: x} - } - case "%": - return &object.Integer{Value: leftVal % rightVal} - case "<": - return nativeBoolToBooleanObject(leftVal < rightVal) - case "<=": - return nativeBoolToBooleanObject(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("Mstari %d: Operesheni Haieleweki: %s %s %s", - line, left.Type(), operator, right.Type()) - } -} diff --git a/lexer/lexer.go b/lexer/lexer.go index 7d83728..d112bcc 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -183,6 +183,8 @@ func (l *Lexer) NextToken() token.Token { tok.Type = token.LookupIdent(tok.Literal) tok.Line = l.line return tok + } else if isDigit(l.ch) && l.ch == '0' && isLetter(l.peekChar()) { + return l.readOtherSystems() } else if isDigit(l.ch) && isLetter(l.peekChar()) { tok.Literal = l.readIdentifier() tok.Type = token.LookupIdent(tok.Literal) @@ -200,6 +202,33 @@ func (l *Lexer) NextToken() token.Token { return tok } +func (l *Lexer) readOtherSystems() token.Token { + var sys_token token.Token + + switch l.peekChar() { + case rune('x'), rune('X'): + sys_token = l.readHexadecimal() + default: + l.readChar() + sys_token = newToken(token.ILLEGAL, l.line, l.ch) + } + + return sys_token +} + +func (l *Lexer) readHexadecimal() token.Token { + l.readChar() // 0 + l.readChar() // x + + position := l.position + + for isDigit(l.ch) || isLetter(l.ch) { + l.readChar() + } + + return token.Token{Type: token.HEXADESIMALI, Literal: string(l.input[position:l.position]), Line: l.line} +} + func newToken(tokenType token.TokenType, line int, ch rune) token.Token { return token.Token{Type: tokenType, Literal: string(ch), Line: line} } diff --git a/object/hexadecimal.go b/object/hexadecimal.go new file mode 100644 index 0000000..e6fa237 --- /dev/null +++ b/object/hexadecimal.go @@ -0,0 +1,14 @@ +package object + +import "fmt" + +type Hexadecimal struct { + Value int64 +} + +func (i *Hexadecimal) Inspect() string { return fmt.Sprintf("%d", i.Value) } +func (i *Hexadecimal) Type() ObjectType { return INTEGER_OBJ } + +func (i *Hexadecimal) HashKey() HashKey { + return HashKey{Type: i.Type(), Value: uint64(i.Value)} +} diff --git a/object/object.go b/object/object.go index 46de338..6594f27 100644 --- a/object/object.go +++ b/object/object.go @@ -8,6 +8,7 @@ type ObjectType string const ( INTEGER_OBJ = "NAMBA" + HEX_OBJ = "HEXADESIMALI" FLOAT_OBJ = "DESIMALI" BOOLEAN_OBJ = "BOOLEAN" NULL_OBJ = "TUPU" diff --git a/parser/hexadecimal.go b/parser/hexadecimal.go new file mode 100644 index 0000000..a38a74a --- /dev/null +++ b/parser/hexadecimal.go @@ -0,0 +1,37 @@ +package parser + +import ( + "fmt" + "math/big" + + "github.com/NuruProgramming/Nuru/ast" +) + +func (p *Parser) parseHexadecimalLiteral() ast.Expression { + lit := &ast.HexadecimalLiteral{Token: p.curToken} + tlit := p.curToken.Literal + + for _, x := range tlit { + if !(isDigit(x) || isLetter(x)) { + msg := fmt.Sprintf("Mstari %d: Hatuwezi kuparse %q kama hexa desimali", p.curToken.Line, tlit) + p.errors = append(p.errors, msg) + return nil + + } + } + + value := new(big.Int) + value.SetString(tlit, 16) + + lit.Value = value.Int64() + + return lit +} + +func isDigit(ch rune) bool { + return '0' <= ch && ch <= '9' +} + +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F' +} diff --git a/parser/parser.go b/parser/parser.go index 0bcf5f8..25e4bce 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -95,6 +95,7 @@ func New(l *lexer.Lexer) *Parser { p.registerPrefix(token.STRING, p.parseStringLiteral) p.registerPrefix(token.IDENT, p.parseIdentifier) p.registerPrefix(token.INT, p.parseIntegerLiteral) + p.registerPrefix(token.HEXADESIMALI, p.parseHexadecimalLiteral) p.registerPrefix(token.FLOAT, p.parseFloatLiteral) p.registerPrefix(token.BANG, p.parsePrefixExpression) p.registerPrefix(token.MINUS, p.parsePrefixExpression) diff --git a/token/token.go b/token/token.go index 18c4f6a..277429a 100644 --- a/token/token.go +++ b/token/token.go @@ -15,10 +15,11 @@ const ( EOF = "MWISHO" // Identifiers + literals - IDENT = "KITAMBULISHI" - INT = "NAMBA" - STRING = "NENO" - FLOAT = "DESIMALI" + IDENT = "KITAMBULISHI" + INT = "NAMBA" + STRING = "NENO" + FLOAT = "DESIMALI" + HEXADESIMALI = "HEXADESIMALI" // Operators ASSIGN = "="