Skip to content

Commit

Permalink
Fix bug for extending buildin type.
Browse files Browse the repository at this point in the history
  • Loading branch information
haifenghuang committed Dec 25, 2018
1 parent c6aaef5 commit e8f8d78
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 60 deletions.
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Table of Contents
* [String](#string)
* [Hash](#hash)
* [Tuple](#tuple)
* [Extend basic type](#extend-basic-type)
* [Optional type](#optional-type)
* [class](#class)
* [inheritance and polymorphism](#inheritance-and-polymorphism)
* [operator overloading](#operator-overloading)
Expand Down Expand Up @@ -196,6 +198,8 @@ This project is based on mayoms's project [magpie](https://github.com/mayoms/mag
* function with default value and variadic parameters
* list comprehension and hash comprehension support
* user defined operator support
* Extending basic type with something like 'int.xxx(params)'
* Optional object support
* Using method of Go Package(`RegisterFunctions` and `RegisterVars`)

There are a number of tasks to complete, as well as a number of bugs. The purpose of this project was to dive deeper into Go, as well as get a better understanding of how programming languages work. It has been successful in those goals. There may or may not be continued work - I do plan on untangling a few messy spots, and there are a few features I'd like to see implemented. This will happen as time and interest allows.
Expand Down Expand Up @@ -1289,6 +1293,80 @@ revTuple = reverse(tp)
println(revTuple) //result: (9, 8, 7, 6, 4, 2, 5, 3, 1)
```

### Extend basic type
Magpie also provides support for extending basic types.

The basic types that can be extended are as follows:
* integer
* uinteger
* float
* boolean
* string
* array
* tuple
* hash

The syntax is: BasicType + "$" + MethodName(params)

```swift
fn float$to_integer() {
return ( int( self ) );
}

printf("12.5.to_integer()=%d\n", 12.5.to_integer())

fn array$find(item) {
i = 0;
length = len(self);

while (i < length) {
if (self[i] == item) {
return i;
}
i++;
}

// if not found
return -1;
};

idx = [25,20,38].find(10);
printf("[25,20,38].find(10) = %d\n", idx) // not found, return -1

idx = [25,20,38].find(38);
printf("[25,20,38].find(38) = %d\n", idx) //found, returns 2
```

### Optional type
Magpie has support for Optional type like java8.

```swift
fn safeDivision?(a, b) {
if (b == 0){
return optional.empty();
} else {
return optional.of(a/b);
}
}

op1 = safeDivision?(10, 0)
if (!op1.isPresent()) {
println(op1)

}

op2 = safeDivision?(10, 2)
if (op2.isPresent()) {
println(op2)

let val = op2.get()
printf("safeDivision?(10, 2)=%d\n", int(val))
}
```

It is recommended that you use '?' as the last character of method to denote
that it is an option.

### class

Magpie has limited support for the oop concept, below is a list of features:
Expand Down
77 changes: 77 additions & 0 deletions README_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Table of Contents
* [字符串(String)](#%E5%AD%97%E7%AC%A6%E4%B8%B2string)
* [哈希(Hash)](#%E5%93%88%E5%B8%8Chash)
* [元祖(Tuple)](#%E5%85%83%E7%A5%96tuple)
* [扩展基本类型](#%E6%89%A9%E5%B1%95%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B)
* [Optional类型](#optional%E7%B1%BB%E5%9E%8B)
* [](#%E7%B1%BB)
* [继承和多态](#%E7%BB%A7%E6%89%BF%E5%92%8C%E5%A4%9A%E6%80%81)
* [操作符重载](#%E6%93%8D%E4%BD%9C%E7%AC%A6%E9%87%8D%E8%BD%BD)
Expand Down Expand Up @@ -198,6 +200,8 @@ Property 'LastName' is not valid!
* 支持可变参数和缺省参数的函数
* 支持列表推导(list comprehension)和哈希推导(hash comprehension)
* 支持用户自定义操作符
* 支持基本类型扩展, 例如‘int.xxx(params)'
* Optional对象支持
* 使用Go Package的方法(`RegisterFunctions``RegisterVars`)

这个项目的目的主要有以下几点:
Expand Down Expand Up @@ -1289,6 +1293,79 @@ revTuple = reverse(tp)
println(revTuple) //结果: (9, 8, 7, 6, 4, 2, 5, 3, 1)
```

### 扩展基本类型
对于基本类型的扩展,Magpie也提供了相应的支持。

支持的可以扩展的基本类型如下:
* integer
* uinteger
* float
* boolean
* string
* array
* tuple
* hash

语法是: 基本类型+$+方法名称(参数)

```swift
fn float$to_integer() {
return ( int( self ) );
}

printf("12.5.to_integer()=%d\n", 12.5.to_integer())

fn array$find(item) {
i = 0;
length = len(self);

while (i < length) {
if (self[i] == item) {
return i;
}
i++;
}

// if not found
return -1;
};

idx = [25,20,38].find(10);
printf("[25,20,38].find(10) = %d\n", idx) // not found, return -1

idx = [25,20,38].find(38);
printf("[25,20,38].find(38) = %d\n", idx) //found, returns 2
```

### Optional类型
Magpie支持类似java8的Optional类型。

```swift
fn safeDivision?(a, b) {
if (b == 0){
return optional.empty();
} else {
return optional.of(a/b);
}
}

op1 = safeDivision?(10, 0)
if (!op1.isPresent()) {
println(op1)

}

op2 = safeDivision?(10, 2)
if (op2.isPresent()) {
println(op2)

let val = op2.get()
printf("safeDivision?(10, 2)=%d\n", int(val))
}
```

建议您使用'?'作为方法的最后一个字符,来表示它是一个Optional类型。

###

Magpie支持简单的面向对象编程, 下面列出了Mokey支持的特性:
Expand Down
4 changes: 2 additions & 2 deletions examples/extensionMehod.mp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
fn float.to_integer() {
fn float$to_integer() {
return ( int( self ) );
}

printf("12.5.to_integer()=%d\n", 12.5.to_integer())

fn array.find(item) {
fn array$find(item) {
i = 0;
length = len(self);

Expand Down
10 changes: 5 additions & 5 deletions examples/optional.mp
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
fn safeDivision(a, b) {
fn safeDivision?(a, b) {
if (b == 0){
return optional.empty();
} else {
return optional.of(a/b);
}
}

op1 = safeDivision(10, 0)
op1 = safeDivision?(10, 0)
if (!op1.isPresent()) {
println(op1)

}

op2 = safeDivision(10, 2)
op2 = safeDivision?(10, 2)
if (op2.isPresent()) {
println(op2)

let val = op2.get()
printf("safeDivision(10, 2)=%d\n", int(val))
printf("safeDivision?(10, 2)=%d\n", int(val))
}

println("-------------------------------\n")
Expand Down Expand Up @@ -108,4 +108,4 @@ result = currentValue.filter((a) => { a < 30} )
orElse = result.orElse(555);
if (orElse == 21) {
println("orElse is equal to 21")
}
}
83 changes: 58 additions & 25 deletions src/magpie/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ var (
CONTINUE = &Continue{}
NIL = &Nil{}
EMPTY = &Optional{Value:NIL}

// Built-in types which you can extend.
builtinTypes = []string{
"integer", // signed integer
"uinteger", // unsigned integer
"float",
"boolean",
"string",
"array",
"tuple",
"hash"}
)

var includeScope *Scope
Expand Down Expand Up @@ -4268,36 +4279,58 @@ func evalMethodCallExpression(call *ast.MethodCallExpression, scope *Scope) Obje

default:
switch o := call.Call.(type) {
case *ast.Identifier: //e.g. method call like '[1,2,3].first', 'float.to_integer'
// Check if it's a builtin type extension method, for example: "int.xxx()", "float.xxx()"
name := fmt.Sprintf("%s.%s", strings.ToLower(string(m.Type())), o.String())
if fn, ok := scope.Get(name); ok {
extendScope := NewScope(scope)
extendScope.Set("self", obj) // Set "self" to be the implicit object.
results := Eval(fn.(*Function).Literal.Body, extendScope)
if results.Type() == RETURN_VALUE_OBJ {
return results.(*ReturnValue).Value
case *ast.Identifier: //e.g. method call like '[1,2,3].first', 'float$to_integer'
// Check if it's a builtin type extension method, for example: "float$xxx()"
ok := false
objType := strings.ToLower(string(obj.Type()))
for _, prefix := range builtinTypes {
if strings.HasPrefix(objType, prefix) {
ok = true
}
return results
}

return obj.CallMethod(call.Call.Pos().Sline(), scope, o.String())
case *ast.CallExpression: //e.g. method call like '[1,2,3].first()', 'float.to_integer()'
if ok {
name := fmt.Sprintf("%s$%s", objType, o.String())
if fn, ok := scope.Get(name); ok {
extendScope := NewScope(scope)
extendScope.Set("self", obj) // Set "self" to be the implicit object.
results := Eval(fn.(*Function).Literal.Body, extendScope)
if results.Type() == RETURN_VALUE_OBJ {
return results.(*ReturnValue).Value
}
return results
} else {
return obj.CallMethod(call.Call.Pos().Sline(), scope, o.String())
}
} else {
return obj.CallMethod(call.Call.Pos().Sline(), scope, o.String())
}
case *ast.CallExpression: //e.g. method call like '[1,2,3].first()', 'float$to_integer()'
args := evalArgs(o.Arguments, scope)
// Check if it's a builtin type extension method, for example: "int.xxx()", "float.xxx()"
name := fmt.Sprintf("%s.%s", strings.ToLower(string(m.Type())), o.Function.String())
if fn, ok := scope.Get(name); ok {
extendScope := extendFunctionScope(fn.(*Function), args)
extendScope.Set("self", obj) // Set "self" to be the implicit object.

results := Eval(fn.(*Function).Literal.Body, extendScope)
if results.Type() == RETURN_VALUE_OBJ {
return results.(*ReturnValue).Value
// Check if it's a builtin type extension method, for example: "float$xxx()"
ok := false
objType := strings.ToLower(string(obj.Type()))
for _, prefix := range builtinTypes {
if strings.HasPrefix(objType, prefix) {
ok = true
}
return results
}

return obj.CallMethod(call.Call.Pos().Sline(), scope, o.Function.String(), args...)
if ok {
name := fmt.Sprintf("%s$%s", strings.ToLower(string(m.Type())), o.Function.String())
if fn, ok := scope.Get(name); ok {
extendScope := extendFunctionScope(fn.(*Function), args)
extendScope.Set("self", obj) // Set "self" to be the implicit object.

results := Eval(fn.(*Function).Literal.Body, extendScope)
if results.Type() == RETURN_VALUE_OBJ {
return results.(*ReturnValue).Value
}
return results
} else {
return obj.CallMethod(call.Call.Pos().Sline(), scope, o.Function.String(), args...)
}
} else {
return obj.CallMethod(call.Call.Pos().Sline(), scope, o.Function.String(), args...)
}
}
}

Expand Down
31 changes: 3 additions & 28 deletions src/magpie/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,24 +547,13 @@ func (l *Lexer) readIdentifier() string {
// }
// }()

// Built-in types which you can extend.
builtinTypes := []string{
"integer.", // signed integer
"uinteger.", // unsigned integer
"float.",
"boolean.",
"string.",
"array.",
"tuple.",
"hash."}

position := l.position
// Why '?' : Because Magpie support Optional, so it should be good for
// a Optional type to denote it meaning with a '?' like 'isEmpty?'

// Why '.' : Because Magpie support extend built-in types with 'int', 'float', etc.
// For example, you could extend 'int' type with 'int.funname(xxx)'
for isLetter(l.ch) || isDigit(l.ch) || l.ch == '?' || l.ch == '.' {
// Why '$' : Because Magpie support extend built-in types with 'integer', 'float', etc.
// For example, you could extend 'integer' type with 'integer$funcname(xxx)'
for isLetter(l.ch) || isDigit(l.ch) || l.ch == '?' || l.ch == '$' {
l.readNext()
}

Expand All @@ -582,20 +571,6 @@ func (l *Lexer) readIdentifier() string {
}
}

ok := false
// If contains '.', we only allows identifiers in the builtinTypes variable.
if strings.Contains(ret, ".") {
for _, prefix := range builtinTypes {
if strings.HasPrefix(ret, prefix) {
ok = true
}
}
if !ok {
errStr := fmt.Sprintf("Line[%d]: '.' character only allowed in 'int|uint|float|boolean|string|array|tuple|hash|object'", l.line)
panic(errStr)
}
}

return ret
}

Expand Down

0 comments on commit e8f8d78

Please sign in to comment.