diff --git a/README.md b/README.md index 6b0c6da..d79d689 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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. @@ -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: diff --git a/README_cn.md b/README_cn.md index af55f84..faa4981 100644 --- a/README_cn.md +++ b/README_cn.md @@ -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) @@ -198,6 +200,8 @@ Property 'LastName' is not valid! * 支持可变参数和缺省参数的函数 * 支持列表推导(list comprehension)和哈希推导(hash comprehension) * 支持用户自定义操作符 +* 支持基本类型扩展, 例如‘int.xxx(params)' +* Optional对象支持 * 使用Go Package的方法(`RegisterFunctions`和`RegisterVars`) 这个项目的目的主要有以下几点: @@ -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支持的特性: diff --git a/examples/extensionMehod.mp b/examples/extensionMehod.mp index c759b03..234a20d 100644 --- a/examples/extensionMehod.mp +++ b/examples/extensionMehod.mp @@ -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); diff --git a/examples/optional.mp b/examples/optional.mp index 10c9606..cf8e1d7 100644 --- a/examples/optional.mp +++ b/examples/optional.mp @@ -1,4 +1,4 @@ -fn safeDivision(a, b) { +fn safeDivision?(a, b) { if (b == 0){ return optional.empty(); } else { @@ -6,18 +6,18 @@ fn safeDivision(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") @@ -108,4 +108,4 @@ result = currentValue.filter((a) => { a < 30} ) orElse = result.orElse(555); if (orElse == 21) { println("orElse is equal to 21") -} \ No newline at end of file +} diff --git a/src/magpie/eval/eval.go b/src/magpie/eval/eval.go index 214d91a..71a651b 100644 --- a/src/magpie/eval/eval.go +++ b/src/magpie/eval/eval.go @@ -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 @@ -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...) + } } } diff --git a/src/magpie/lexer/lexer.go b/src/magpie/lexer/lexer.go index 279ba72..f287f9e 100644 --- a/src/magpie/lexer/lexer.go +++ b/src/magpie/lexer/lexer.go @@ -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() } @@ -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 }