Skip to content

Commit

Permalink
feat: Chapter 19 attempt #1
Browse files Browse the repository at this point in the history
comitting this; I would rather use manual memory management via
teh-cmc/mmm than rely on Go's garbage collector and miss out on
all the interesting tidbits in the book
  • Loading branch information
kalexmills committed Nov 26, 2020
1 parent f2e735e commit 382d5f5
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 4 deletions.
7 changes: 6 additions & 1 deletion compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,15 @@ func compileGrouping() {
}

func compileNumber() {
// N.B. error from ParseFlot is safely ignored because our scanner correctly identifies valid input
value, _ := strconv.ParseFloat((*parser.Previous.Source)[parser.Previous.Start:parser.Previous.Start+parser.Previous.Length], 64)
emitConstant(NumberVal(value))
}

func compileString() {
emitConstant(NewObjString((*parser.Previous.Source)[parser.Previous.Start+1 : parser.Previous.Start+1+parser.Previous.Length-2]))
}

func compileUnary() {
operatorType := parser.Previous.Type

Expand Down Expand Up @@ -243,7 +248,7 @@ func init() {
TOKEN_LESS: {nil, compileBinary, PREC_COMPARISON},
TOKEN_LESS_EQUAL: {nil, compileBinary, PREC_COMPARISON},
TOKEN_IDENTIFIER: {nil, nil, PREC_NONE},
TOKEN_STRING: {nil, nil, PREC_NONE},
TOKEN_STRING: {compileString, nil, PREC_NONE},
TOKEN_NUMBER: {compileNumber, nil, PREC_NONE},
TOKEN_AND: {nil, nil, PREC_NONE},
TOKEN_CLASS: {nil, nil, PREC_NONE},
Expand Down
22 changes: 20 additions & 2 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ const (
VAL_BOOL ValueType = iota
VAL_NIL
VAL_NUMBER
VAL_OBJ
)

type Value interface { // N.B. go doesn't have sum-types, so we use an interface; this is more verbose
Type() ValueType
AsBoolean() bool
AsNumber() float64
AsObj() Obj
Print()
}

Expand All @@ -27,20 +29,28 @@ func isNil(v Value) bool {
return v.Type() == VAL_NIL
}

func isObj(v Value) bool {
return v.Type() == VAL_OBJ
}

type NilVal struct{}

func (nv NilVal) Type() ValueType {
return VAL_NIL
}

func (nv NilVal) AsBoolean() bool {
panic("nil value is not a boolean!")
panic("nil value is not a boolean!") // N.B. panicking is one choice... returning the zero value is another...
}

func (nv NilVal) AsNumber() float64 {
panic("nil value is not a number!")
}

func (nv NilVal) AsObj() Obj {
panic("nil value is not an object!")
}

func (nv NilVal) Print() {
fmt.Printf("nil")
}
Expand All @@ -56,7 +66,11 @@ func (bv BoolVal) AsBoolean() bool {
}

func (bv BoolVal) AsNumber() float64 {
panic("bool value is not a number!") // N.B. panicking is one choice... returning the zero value is another...
panic("bool value is not a number!")
}

func (bv BoolVal) AsObj() Obj {
panic("bool value is not an object!")
}

func (bv BoolVal) Print() {
Expand All @@ -77,6 +91,10 @@ func (nv NumberVal) AsNumber() float64 {
return float64(nv)
}

func (nv NumberVal) AsObj() Obj {
panic("number value is not an object!")
}

func (nv NumberVal) Print() {
fmt.Printf("%g", float64(nv))
}
Expand Down
17 changes: 16 additions & 1 deletion vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,14 @@ func run() InterpretResult {
return INTERPRET_COMPILE_ERROR
}
case OP_ADD:
if !vm.binaryOp(ADD) {
if isString(vm.peek(0)) && isString(vm.peek(1)) {
concatenate()
} else if isNumber(vm.peek(0)) && isNumber(vm.peek(1)) {
if !vm.binaryOp(ADD) {
return INTERPRET_RUNTIME_ERROR
}
} else {
runtimeError("Operands must be two numbers or two strings.")
return INTERPRET_RUNTIME_ERROR
}
case OP_SUBTRACT:
Expand Down Expand Up @@ -132,6 +139,9 @@ func valuesEqual(a, b Value) bool {
return true
case VAL_NUMBER:
return a.AsNumber() == b.AsNumber()
case VAL_OBJ:
a, b := asString(a), asString(b)
return a.value == b.value
}
return false
}
Expand All @@ -140,6 +150,11 @@ func isFalsey(value Value) bool {
return isNil(value) || (isBool(value) && !value.AsBoolean())
}

func concatenate() {
b, a := asString(vm.pop()), asString(vm.pop())
vm.push(NewObjString(a.value + b.value))
}

func (v *VM) binaryOp(op func(float64, float64) Value) bool {
if !isNumber(v.peek(0)) || !isNumber(v.peek(1)) {
runtimeError("Operands must be numbers.")
Expand Down

0 comments on commit 382d5f5

Please sign in to comment.