├── docs └── __unit.th ├── tests ├── xview │ ├── __unit.th │ ├── IViewDemo.tea │ └── BaseView.tea ├── syntax │ ├── type-regex.tea │ ├── assignments.tea │ ├── structure-if.tea │ ├── keyword-echo-print.tea │ ├── __unit.th │ ├── label-expect.tea │ ├── structure-try.tea │ ├── structure-while.tea │ ├── keyword-yield.tea │ ├── structure-case.tea │ ├── type-int-uint-float.tea │ ├── function.tea │ ├── structure-for.tea │ ├── main1.tea │ ├── type-string.tea │ ├── type-collector.tea │ ├── operations.tea │ ├── type-array-dict.tea │ ├── type-xview.tea │ └── class.tea └── PHPDemoUnit │ ├── constants.php │ ├── functions.php │ ├── InterfaceDemo.php │ ├── NS1 │ └── Demo.php │ ├── __unit.php │ └── __public.th ├── dist ├── builtin │ ├── core.php │ ├── __unit.php │ └── __public.th ├── tests │ ├── syntax │ │ ├── keyword-yield.php │ │ ├── type-regex.php │ │ ├── keyword-echo-print.php │ │ ├── assignments.php │ │ ├── function.php │ │ ├── structure-if.php │ │ ├── label-expect.php │ │ ├── type-collector.php │ │ ├── structure-while.php │ │ ├── type-int-uint-float.php │ │ ├── __public.th │ │ ├── structure-try.php │ │ ├── structure-case.php │ │ ├── structure-for.php │ │ ├── operations.php │ │ ├── type-string.php │ │ ├── main1.php │ │ ├── type-array-dict.php │ │ ├── type-xview.php │ │ ├── __unit.php │ │ └── class.php │ └── xview │ │ ├── IViewDemo.php │ │ ├── __unit.php │ │ ├── __public.th │ │ └── BaseView.php └── docs │ ├── __public.th │ ├── __unit.php │ └── summary.php ├── builtin ├── __unit.th └── core.tea ├── .editorconfig ├── compiler ├── ast │ ├── expression │ │ ├── IExpression.php │ │ ├── ExpressionList.php │ │ ├── IncludeExpression.php │ │ ├── Parentheses.php │ │ ├── YieldExpression.php │ │ ├── CallbackArgument.php │ │ ├── Accessing.php │ │ ├── RegularExpression.php │ │ ├── RelayExpression.php │ │ ├── Ton.php │ │ ├── ObjectExpression.php │ │ ├── ConditionalExpression.php │ │ ├── HTMLEscapeExpression.php │ │ ├── StringInterpolation.php │ │ ├── NamespaceIdentifier.php │ │ ├── CallExpression.php │ │ ├── ArrayDictExpression.php │ │ ├── XBlock.php │ │ └── Operations.php │ ├── literal │ │ ├── NoneLiteral.php │ │ ├── ObjectLiteral.php │ │ ├── FloatLiteral.php │ │ ├── BooleanLiteral.php │ │ ├── ILiteral.php │ │ ├── ArrayDictLiteral.php │ │ ├── StringLiteral.php │ │ └── IntegerLiteral.php │ ├── statement │ │ ├── BaseStatement.php │ │ ├── ExitStatement.php │ │ ├── ThrowStatement.php │ │ ├── ReturnStatement.php │ │ ├── NormalStatement.php │ │ ├── EchoStatement.php │ │ ├── UseStatement.php │ │ ├── BreakContinueStatement.php │ │ └── Assignment.php │ ├── base │ │ ├── OperatorSymbol.php │ │ ├── Node.php │ │ ├── Comment.php │ │ ├── DeferChecks.php │ │ ├── Symbol.php │ │ ├── Docs.php │ │ ├── Unit.php │ │ ├── Program.php │ │ ├── Identifiable.php │ │ └── Types.php │ ├── declaration │ │ ├── ExpectDeclaration.php │ │ ├── MaskedDeclaration.php │ │ ├── NamespaceDeclaration.php │ │ ├── PropertyDeclaration.php │ │ ├── CallbackProtocol.php │ │ ├── UseDeclaration.php │ │ ├── BaseDeclaration.php │ │ ├── ConstantDeclaration.php │ │ ├── VariableDeclaration.php │ │ ├── FunctionDeclaration.php │ │ └── ClassLikeDeclaration.php │ ├── block │ │ ├── BaseBlock.php │ │ ├── LambdaBlock.php │ │ ├── WhileLoopBlock.php │ │ ├── CaseBlock.php │ │ ├── TryCatchFinallyBlock.php │ │ ├── IfElseBlock.php │ │ └── ForBlock.php │ ├── ASTHelper.php │ ├── TypeFactory.php │ ├── ReturnBuilder.php │ └── OperatorFactory.php ├── helper │ ├── FileHelper.php │ ├── PHPUnitScanner.php │ ├── PHPLoaderMaker.php │ └── Dumper.php ├── coder │ └── PublicCoder.php ├── parser │ ├── TeaDocsTrait.php │ ├── HeaderParser.php │ ├── TeaStringTrait.php │ ├── TeaHelper.php │ └── TeaXblockTrait.php └── constants.php ├── README.md └── bin ├── tea └── genphploader /docs/__unit.th: -------------------------------------------------------------------------------- 1 | 2 | #unit tea/docs 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/xview/__unit.th: -------------------------------------------------------------------------------- 1 | 2 | #unit tea/tests/xview 3 | 4 | -------------------------------------------------------------------------------- /dist/builtin/core.php: -------------------------------------------------------------------------------- 1 | >= 1 9 | a <<= 1 10 | a &= 1 11 | a |= 1 12 | a ^= 1 13 | 14 | a .= 'abc' 15 | 16 | -------------------------------------------------------------------------------- /builtin/__unit.th: -------------------------------------------------------------------------------- 1 | --- 2 | The builtin libs for Tea 3 | 4 | created: 2019/02 by Benny Lin 5 | copyright: (c) YJ Technology Ltd. All rights reserved. 6 | --- 7 | 8 | #unit tea/builtin 9 | 10 | -------------------------------------------------------------------------------- /tests/xview/IViewDemo.tea: -------------------------------------------------------------------------------- 1 | 2 | public IViewDemo: IView { 3 | name = '' 4 | css_class = '' 5 | 6 | set_css_class(names String) { 7 | this.css_class = names 8 | return this 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/syntax/structure-if.tea: -------------------------------------------------------------------------------- 1 | 2 | a = 0 3 | b = 1 4 | 5 | if a { 6 | // 7 | } 8 | elseif b { 9 | // 10 | } 11 | else { 12 | // 13 | } 14 | catch ex { 15 | // 16 | } 17 | finally { 18 | // 19 | } 20 | 21 | -------------------------------------------------------------------------------- /tests/syntax/keyword-echo-print.tea: -------------------------------------------------------------------------------- 1 | 2 | // print a new line 3 | echo 4 | 5 | str = ' a string' 6 | 7 | // print strings end with newline 8 | echo 'abc', 'efg', '123' // abcefg\n 9 | 10 | // print without end newline 11 | print 'abc', str 12 | -------------------------------------------------------------------------------- /dist/tests/syntax/type-regex.php: -------------------------------------------------------------------------------- 1 | >= 1; 12 | $a <<= 1; 13 | $a &= 1; 14 | $a |= 1; 15 | $a ^= 1; 16 | 17 | $a .= 'abc'; 18 | // --------- 19 | 20 | // program end 21 | -------------------------------------------------------------------------------- /tests/syntax/label-expect.tea: -------------------------------------------------------------------------------- 1 | 2 | // expect variables defined in the including programs 3 | #expect title String, items Array 4 | 5 | list = [] 6 | for k, v in items { 7 | list.push(
  • $k: $v
  • ) 8 | } 9 | 10 | return
    11 |

    $title

    12 | 15 |
    16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | # doc: https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = tab 8 | indent_size = 4 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /tests/PHPDemoUnit/InterfaceDemo.php: -------------------------------------------------------------------------------- 1 | $v) { 9 | array_push($list, '
  • ' . $k . ': ' . $v . '
  • '); 10 | } 11 | 12 | return '
    13 |

    ' . $title . '

    14 | 17 |
    '; 18 | // --------- 19 | 20 | // program end 21 | -------------------------------------------------------------------------------- /compiler/ast/expression/IExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | interface IExpression 13 | { 14 | // 15 | } 16 | -------------------------------------------------------------------------------- /dist/tests/syntax/type-collector.php: -------------------------------------------------------------------------------- 1 | css_class = $names; 18 | return $this; 19 | } 20 | } 21 | 22 | // program end 23 | -------------------------------------------------------------------------------- /tests/syntax/structure-try.tea: -------------------------------------------------------------------------------- 1 | 2 | // normal try-catch-finally 3 | try { 4 | // 5 | } 6 | catch ex { 7 | // 8 | } 9 | finally { 10 | // 11 | } 12 | 13 | // if with catch-finally 14 | if 1 { 15 | // 16 | } 17 | else { 18 | // 19 | } 20 | catch ex {} 21 | finally {} 22 | 23 | // for with catch-finally 24 | for v in [] { 25 | throw ErrorException('some message') 26 | } 27 | catch ex ErrorException {} 28 | finally {} 29 | 30 | // also supported in while 31 | -------------------------------------------------------------------------------- /tests/syntax/structure-while.tea: -------------------------------------------------------------------------------- 1 | 2 | // normal while 3 | i = 0 4 | while i < 10 { 5 | i += 1 6 | continue 7 | } 8 | catch ex { 9 | // 10 | } 11 | 12 | // do-while 13 | while is first or i > 5 { 14 | i -= 1 15 | break 16 | } 17 | 18 | // labeled loops 19 | i = 0 20 | #outer_loop while 1 { 21 | #inner_loop while true { 22 | i = i + 1 23 | if i > 10 { 24 | break #outer_loop 25 | } 26 | else { 27 | continue #inner_loop 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/syntax/keyword-yield.tea: -------------------------------------------------------------------------------- 1 | 2 | // a int range generator use yield 3 | public xrange(start Int, stop Int, step = 1) { 4 | // increment 5 | if step > 0 { 6 | i = start 7 | while i < stop { 8 | yield i 9 | i += step 10 | } 11 | 12 | return 13 | } 14 | 15 | // invalid 16 | if step == 0 { 17 | throw Exception('Step should not be 0.'); 18 | } 19 | 20 | // step less than 0, decrement 21 | i = start 22 | while i > stop { 23 | yield i 24 | i += step 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/syntax/structure-case.tea: -------------------------------------------------------------------------------- 1 | 2 | a = 0 3 | b = 1 4 | num = 1 5 | 6 | #outer_case 7 | case num { 8 | a * 3: 9 | case num { 10 | b: 11 | b = num * 5 12 | echo b 13 | 1, 2, 3: 14 | a = num * 2 15 | break #outer_case 16 | } 17 | else { 18 | echo 'not matched.' 19 | } 20 | 21 | 10, 100: 22 | echo num 23 | } 24 | elseif num > 10 { 25 | echo 'num is greater than 10.' 26 | } 27 | catch ex { 28 | // 29 | } 30 | finally { 31 | echo 'finally!' 32 | } 33 | 34 | -------------------------------------------------------------------------------- /compiler/ast/literal/NoneLiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class NoneLiteral extends Node implements ILiteral 13 | { 14 | const KIND = 'none_literal'; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /compiler/ast/literal/ObjectLiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ObjectLiteral extends ObjectExpression implements ILiteral 13 | { 14 | const KIND = 'object_literal'; 15 | } 16 | -------------------------------------------------------------------------------- /tests/syntax/type-int-uint-float.tea: -------------------------------------------------------------------------------- 1 | 2 | var num1 Int = 123_456 3 | num2 = -123_456 4 | 5 | dec = 123_456_789 // infer to UInt by default 6 | hex = 0xff_ff_ff 7 | oct = 01234567 8 | bin = 0b01010101 9 | 10 | num1 is UInt // true 11 | num2 is UInt // false 12 | dec is UInt // true 13 | dec is Int // true 14 | 15 | var float_num0 Float = 123 16 | float_num1 = 123.123 17 | float_num2 = 123e3 18 | float_num3 = 123.1e-6 19 | 20 | // convert float to string 21 | num_str = dec.string 22 | num_str = 123.1 as String 23 | -------------------------------------------------------------------------------- /dist/tests/xview/__unit.php: -------------------------------------------------------------------------------- 1 | 'IViewDemo.php', 11 | 'tea\tests\xview\IViewDemoTrait' => 'IViewDemo.php', 12 | 'tea\tests\xview\BaseView' => 'BaseView.php' 13 | ]; 14 | 15 | spl_autoload_register(function ($class) { 16 | isset(__AUTOLOADS[$class]) && require UNIT_PATH . __AUTOLOADS[$class]; 17 | }); 18 | 19 | // end 20 | -------------------------------------------------------------------------------- /compiler/ast/statement/BaseStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | interface IStatement {} 13 | 14 | class BaseStatement extends Node implements IStatement 15 | { 16 | public $docs; 17 | } 18 | -------------------------------------------------------------------------------- /dist/tests/syntax/structure-while.php: -------------------------------------------------------------------------------- 1 | 5); 20 | 21 | $i = 0; 22 | while (1) { 23 | while (true) { 24 | $i = $i + 1; 25 | if ($i > 10) { 26 | break 2; 27 | } 28 | else { 29 | continue 1; 30 | } 31 | } 32 | } 33 | // --------- 34 | 35 | // program end 36 | -------------------------------------------------------------------------------- /compiler/ast/literal/FloatLiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class FloatLiteral extends Node implements ILiteral 13 | { 14 | use LiteralTraitWithValue; 15 | const KIND = 'float_literal'; 16 | } 17 | -------------------------------------------------------------------------------- /compiler/ast/literal/BooleanLiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class BooleanLiteral extends Node implements ILiteral 13 | { 14 | use LiteralTraitWithValue; 15 | const KIND = 'bool_literal'; 16 | } 17 | -------------------------------------------------------------------------------- /dist/tests/syntax/type-int-uint-float.php: -------------------------------------------------------------------------------- 1 | fn0(str)) 21 | 22 | fn2(Test1) 23 | internal fn2(class Class) { 24 | class() 25 | } 26 | 27 | // assignable feature 28 | internal fn3(ex Exception, var num String = none) { 29 | num = 1 30 | echo ex.getMessage() 31 | } 32 | 33 | fn3(Exception('message'), 'abc') 34 | 35 | -------------------------------------------------------------------------------- /compiler/ast/literal/ILiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | interface ILiteral extends IExpression {} 13 | 14 | trait LiteralTraitWithValue 15 | { 16 | public $value; 17 | 18 | public function __construct(string $value) 19 | { 20 | $this->value = $value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compiler/ast/literal/ArrayDictLiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ArrayLiteral extends ArrayExpression implements ILiteral 13 | { 14 | const KIND = 'array_literal'; 15 | } 16 | 17 | class DictLiteral extends DictExpression implements ILiteral 18 | { 19 | const KIND = 'dict_literal'; 20 | } 21 | -------------------------------------------------------------------------------- /compiler/ast/statement/ExitStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ExitStatement extends BaseStatement 13 | { 14 | const KIND = 'exit_statement'; 15 | 16 | public $status; 17 | 18 | public function __construct(IExpression $status = null) 19 | { 20 | $this->status = $status; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compiler/ast/statement/ThrowStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ThrowStatement extends BaseStatement 13 | { 14 | const KIND = 'throw_statement'; 15 | 16 | public $argument; 17 | 18 | public function __construct(IExpression $argument) 19 | { 20 | $this->argument = $argument; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compiler/ast/expression/ExpressionList.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ExpressionList extends Node implements IExpression 13 | { 14 | const KIND = 'expression_list'; 15 | 16 | public $items; 17 | 18 | public function __construct(IExpression ...$items) 19 | { 20 | $this->items = $items; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compiler/ast/statement/ReturnStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ReturnStatement extends BaseStatement 13 | { 14 | const KIND = 'return_statement'; 15 | 16 | public $argument; 17 | 18 | public function __construct(?IExpression $argument) 19 | { 20 | $this->argument = $argument; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dist/tests/syntax/__public.th: -------------------------------------------------------------------------------- 1 | // the public declarations 2 | 3 | #unit tea/tests/syntax 4 | 5 | public __CLASS__ String 6 | 7 | public xrange(start Int, stop Int, step UInt = 1) 8 | 9 | #php range(start Int, end Int, step UInt = 1) Array 10 | 11 | #php var_dump(v Any) 12 | 13 | public get_class() Class 14 | 15 | public fn0(str Any) 16 | 17 | public DemoList: BaseView { 18 | ABC String = '12' 19 | 20 | tag String 21 | title String 22 | items Array 23 | 24 | cells Array 25 | 26 | construct(name String, title String = '', items Array = []) -> each(item String) Cell -> done() 27 | 28 | render() String 29 | } 30 | 31 | // program end 32 | -------------------------------------------------------------------------------- /compiler/ast/expression/IncludeExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class IncludeExpression extends Node implements IExpression 13 | { 14 | const KIND = 'include_expression'; 15 | 16 | public $target; 17 | 18 | public function __construct(string $target) 19 | { 20 | $this->target = $target; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compiler/ast/statement/NormalStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class NormalStatement extends BaseStatement 13 | { 14 | const KIND = 'normal_statement'; 15 | 16 | public $expression; 17 | 18 | public function __construct(IExpression $expression) 19 | { 20 | $this->expression = $expression; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tea语言(Tea Programming Language) 2 | 一种新的编程语言,其规范即是语法,简洁易用,并具有简单的类型系统和单元模块系统。(A new programming language, whose specification is grammar, is simple and easy to use, and has simple type system and unit module system.) 3 | 4 | ```Tea 5 | // 来自深圳的问候 6 | echo "世界你好!" 7 | ``` 8 | 9 | Tea语言目前有以下特点: 10 | - 是一个强规范的编程语言(规范即语法),拥有精炼简洁的语法,简约的类型系统和单元模块体系 11 | - 经过精心设计,力求让代码编写体验更轻松自然,让使用者可以更专注于创意实现 12 | - 基于单元模块(Unit)组织程序文件,任何程序文件都必须包含在某个Unit中,可引入使用外部Unit 13 | - 对字符串处理语法进行了特别设计,尤其方便用于WEB开发 14 | - 类型系统支持类型推断,并在类型兼容性方面有一些特别处理 15 | - 编译时将进行类型推断和语法检查,有助于提前发现相关问题 16 | - 通过编译生成PHP代码运行,并可调用PHP库 17 | 18 | Tea语言由创业者Benny设计开发,潘春孟(高级工程师/架构师)与刘景能(计算机博士)参与了早期设计与测试使用。 19 | 20 | -------------------------------------------------------------------------------- /compiler/ast/expression/Parentheses.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class Parentheses extends Node implements IExpression 13 | { 14 | const KIND = 'parentheses'; 15 | 16 | public $expression; 17 | 18 | public function __construct(IExpression $expression) 19 | { 20 | $this->expression = $expression; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compiler/ast/expression/YieldExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class YieldExpression extends Node implements IExpression 13 | { 14 | const KIND = 'yield_expression'; 15 | 16 | public $argument; 17 | 18 | public function __construct(IExpression $argument) 19 | { 20 | $this->argument = $argument; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dist/tests/syntax/structure-try.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class CallbackArgument extends Node 13 | { 14 | const KIND = 'callback_argument'; 15 | 16 | public $name; 17 | public $value; 18 | 19 | public function __construct(?string $name, ICallee $value) 20 | { 21 | $this->name = $name; 22 | $this->value = $value; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /compiler/ast/statement/EchoStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class EchoStatement extends BaseStatement 13 | { 14 | const KIND = 'echo_statement'; 15 | 16 | public $arguments; 17 | 18 | public $end_newline = true; 19 | 20 | public function __construct(IExpression ...$arguments) 21 | { 22 | $this->arguments = $arguments; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /compiler/ast/statement/UseStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class UseStatement extends BaseStatement 13 | { 14 | const KIND = 'use_statement'; 15 | 16 | public $ns; 17 | public $targets; 18 | 19 | public function __construct(NamespaceIdentifier $ns, array $targets = []) 20 | { 21 | $this->ns = $ns; 22 | $this->targets = $targets; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/PHPDemoUnit/__unit.php: -------------------------------------------------------------------------------- 1 | 'InterfaceDemo.php', 22 | 'tea\tests\PHPDemoUnit\Interface1' => 'InterfaceDemo.php', 23 | 'tea\tests\PHPDemoUnit\NS1\Demo' => 'NS1/Demo.php' 24 | ]; 25 | 26 | spl_autoload_register(function ($class) { 27 | isset(__AUTOLOADS[$class]) && require __DIR__ . DIRECTORY_SEPARATOR . __AUTOLOADS[$class]; 28 | }); 29 | 30 | // end 31 | -------------------------------------------------------------------------------- /compiler/ast/base/OperatorSymbol.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class OperatorSymbol 13 | { 14 | public $sign; 15 | public $precedence; 16 | 17 | // for render 18 | public $dist_sign; 19 | public $dist_precedence; 20 | 21 | public function __construct(string $sign, int $precedence) 22 | { 23 | $this->sign = $sign; 24 | $this->precedence = $precedence; 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /compiler/ast/expression/Accessing.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class KeyAccessing extends Node implements IExpression, IAssignable 13 | { 14 | const KIND = 'key_accessing'; 15 | 16 | public $left; 17 | public $right; 18 | 19 | public function __construct(IExpression $left, IExpression $right) 20 | { 21 | $this->left = $left; 22 | $this->right = $right; 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /compiler/ast/expression/RegularExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class RegularExpression extends Node implements IExpression 13 | { 14 | const KIND = 'regular_expression'; 15 | 16 | public $pattern; 17 | public $flags; 18 | 19 | public function __construct(string $pattern, string $flags) 20 | { 21 | $this->pattern = $pattern; 22 | $this->flags = $flags; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /compiler/ast/expression/RelayExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class RelayExpression extends Node implements IExpression 13 | { 14 | const KIND = 'relay_expression'; 15 | 16 | public $argument; 17 | public $callees; 18 | 19 | public function __construct(IExpression $argument, IExpression ...$callees) 20 | { 21 | $this->argument = $argument; 22 | $this->callees = $callees; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/syntax/structure-for.tea: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | // for 未支持类型标注 4 | for key Int, val Int in vt1 { 5 | // 6 | } 7 | --- 8 | 9 | #php range(start Int, end Int, step = 1) Array 10 | 11 | arr = [ 12 | "k1": "hi", 13 | "k2": "hello", 14 | ] 15 | 16 | // for-in 17 | for val in range(0, 9) { 18 | echo val 19 | } 20 | 21 | // for-in-else 22 | for k, v in arr { 23 | // do sth. 24 | } 25 | elseif true { 26 | // 27 | } 28 | else { 29 | // 30 | } 31 | 32 | // for-to 33 | for i = 0 to 9 { 34 | i + 3 35 | echo i 36 | } 37 | else { 38 | echo 'no loops' 39 | } 40 | 41 | // for-downto 42 | for i = 9 * 2 downto 0 + 5 step 2 { 43 | echo i 44 | } 45 | elseif (arr) { 46 | print 'oh!' 47 | } 48 | catch ex { 49 | // 50 | } 51 | finally { 52 | // 53 | } 54 | -------------------------------------------------------------------------------- /compiler/ast/declaration/ExpectDeclaration.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ExpectDeclaration extends RootDeclaration 13 | { 14 | const KIND = 'expect_declaration'; 15 | 16 | // public $name = '__expect'; 17 | 18 | public $parameters; 19 | 20 | public $program; 21 | 22 | public function __construct(ParameterDeclaration ...$parameters) 23 | { 24 | $this->parameters = $parameters; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/PHPDemoUnit/__public.th: -------------------------------------------------------------------------------- 1 | 2 | #unit tea/tests/PHPDemoUnit 3 | 4 | // a php const 5 | public PHP_CONST_DEMO String 6 | 7 | // a php function 8 | public php_function_demo(message String) Int 9 | 10 | // do not need 'as', because of it already has a suffix 'Interface' 11 | public BaseInterface { 12 | public get_class_name(caller String = #default) String 13 | } 14 | 15 | // need use 'as' to add a suffix 'Interface' 16 | public Interface1 as DemoInterface: BaseInterface { 17 | public LINE UInt 18 | public static get_target_class_methods(class Class) 19 | } 20 | 21 | // a php class with a sub-namespace 22 | // 'as' syntax is required for all classes / interfaces witch has sub-namespaces 23 | public NS1.Demo as PHPClassDemo: DemoInterface {} 24 | -------------------------------------------------------------------------------- /compiler/ast/expression/Ton.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class Ton extends Node 13 | { 14 | const KIND = 'ton'; 15 | 16 | public $name; 17 | public $attributes; 18 | public $elements; 19 | 20 | public function __construct(?string $name, array $attributes = [], array $elements = null) 21 | { 22 | $this->name = $name; 23 | $this->attributes = $attributes; 24 | $this->elements = $elements; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /compiler/ast/base/Node.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | abstract class Node 13 | { 14 | const KIND = null; 15 | 16 | public $pos; // the first token position of current node 17 | 18 | public $leading; // the node leading contents 19 | 20 | public $tailing; // the node tailing contents 21 | 22 | public function render(TeaCoder $coder) 23 | { 24 | return $coder->{'render_' . static::KIND}($this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /compiler/ast/block/BaseBlock.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class BaseBlock extends Node implements IStatement 13 | { 14 | public $docs; 15 | 16 | /** 17 | * @var IStatement[] or IExpression 18 | */ 19 | public $body; 20 | 21 | public $symbols = []; 22 | 23 | public $super_block; 24 | 25 | public function set_body_with_statements(IStatement ...$statements) 26 | { 27 | $this->body = $statements; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /compiler/ast/declaration/MaskedDeclaration.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | // 调用时参数顺序不同可能导致不一致的运算结果,采用类似宏替换在部分场景下可能会导致问题 13 | // 问题举例:以为对象形式方法调用时,“对象表达式”可能在实际中被作为非第一个参数,如果其它参数与这个参数相关时,导致调用时序不一致 14 | // 解决方案:用函数包起来,并且预先将“对象表达式”计算出来存储到局部变量中 15 | // 为降低实现复杂度,可采用约定mask函数和真函数对应参数顺序强制一致,并且每个参数仅被调用一次 16 | 17 | class MaskedDeclaration extends FunctionBlock 18 | { 19 | const KIND = 'masked_declaration'; 20 | public $arguments_map = []; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /compiler/ast/declaration/NamespaceDeclaration.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class NamespaceDeclaration extends Node implements IRootDeclaration, IMemberDeclaration 13 | { 14 | const KIND = 'namespace_declaration'; 15 | 16 | public $name; 17 | 18 | public $members = []; 19 | 20 | public $symbols = []; 21 | 22 | public $super_block; 23 | 24 | public function __construct(string $name) 25 | { 26 | $this->name = $name; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /compiler/ast/expression/ObjectExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ObjectExpression extends Node implements IExpression 13 | { 14 | const KIND = 'object_expression'; 15 | 16 | // [key => value] map 17 | public $items; 18 | 19 | /** 20 | * @var bool 21 | */ 22 | public $is_vertical_layout = false; // for render target code 23 | 24 | public function __construct(array $items) 25 | { 26 | $this->items = $items; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /compiler/ast/literal/StringLiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | abstract class StringLiteral extends Node 13 | { 14 | use LiteralTraitWithValue; 15 | public $label; 16 | } 17 | 18 | class UnescapedStringLiteral extends StringLiteral implements ILiteral 19 | { 20 | const KIND = 'unescaped_string_literal'; 21 | } 22 | 23 | class EscapedStringLiteral extends StringLiteral implements ILiteral 24 | { 25 | const KIND = 'escaped_string_literal'; 26 | } 27 | -------------------------------------------------------------------------------- /compiler/ast/expression/ConditionalExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ConditionalExpression extends Node implements IExpression 13 | { 14 | const KIND = 'conditional_expression'; 15 | 16 | public $test; 17 | public $then; 18 | public $else; 19 | 20 | public function __construct(IExpression $test, ?IExpression $then, IExpression $else) 21 | { 22 | $this->test = $test; 23 | $this->then = $then; 24 | $this->else = $else; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /compiler/ast/expression/HTMLEscapeExpression.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class HTMLEscapeExpression extends Node implements IExpression 13 | { 14 | const KIND = 'html_escape_expression'; 15 | 16 | public $expression; 17 | 18 | public function __construct(IExpression $expression) 19 | { 20 | if ($expression instanceof Parentheses) { 21 | $expression = $expression->expression; 22 | } 23 | 24 | $this->expression = $expression; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/xview/BaseView.tea: -------------------------------------------------------------------------------- 1 | 2 | public BaseView: IView { 3 | name String 4 | 5 | protected props = [:] 6 | protected subviews = [] 7 | 8 | construct(props Dict = none) { 9 | if props { 10 | this.props = props 11 | } 12 | } 13 | 14 | prop(key String, value Any) BaseView { 15 | this.props[key] = value 16 | return this 17 | } 18 | 19 | subview(view XView) BaseView { 20 | this.subviews[] = view 21 | return this 22 | } 23 | 24 | protected build_props() { 25 | props = '' 26 | for key, value in this.props { 27 | props .= ' #{key}="#{value}"' 28 | } 29 | 30 | return props 31 | } 32 | 33 | render() String { 34 | return ${this.subviews.join()} 35 | } 36 | 37 | to_string() String { 38 | return this.render() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dist/tests/syntax/structure-case.php: -------------------------------------------------------------------------------- 1 | 10) { 34 | echo 'num is greater than 10.', NL; 35 | } 36 | } 37 | } 38 | catch (\Exception $ex) { 39 | // no any 40 | } 41 | finally { 42 | echo 'finally!', NL; 43 | } 44 | // --------- 45 | 46 | // program end 47 | -------------------------------------------------------------------------------- /compiler/ast/expression/StringInterpolation.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class EscapedStringInterpolation extends Node implements IExpression 13 | { 14 | const KIND = 'escaped_string_interpolation'; 15 | 16 | public $items; 17 | 18 | public function __construct(array $items) 19 | { 20 | $this->items = $items; 21 | } 22 | } 23 | 24 | class UnescapedStringInterpolation extends EscapedStringInterpolation 25 | { 26 | const KIND = 'unescaped_string_interpolation'; 27 | } 28 | -------------------------------------------------------------------------------- /compiler/ast/block/LambdaBlock.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class LambdaBlock extends BaseBlock implements IEnclosingBlock, ICallableDeclaration, ICallee, IExpression 13 | { 14 | use FunctionLikeTrait; 15 | 16 | const KIND = 'lambda_block'; 17 | 18 | /** 19 | * @var PlainIdentifier[] 20 | */ 21 | public $use_variables = []; 22 | 23 | public function __construct(?IType $type, ParameterDeclaration ...$parameters) 24 | { 25 | $this->type = $type; 26 | $this->parameters = $parameters; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /compiler/ast/base/Comment.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class InlineComments extends Node implements IStatement 13 | { 14 | const KIND = 'inline_comments'; 15 | 16 | public $items; 17 | 18 | public function __construct(string ...$items) 19 | { 20 | $this->items = $items; 21 | } 22 | } 23 | 24 | class BlockComment extends Node implements IStatement 25 | { 26 | const KIND = 'block_comment'; 27 | 28 | public $content; 29 | 30 | public function __construct(string $content) 31 | { 32 | $this->content = $content; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /compiler/ast/literal/IntegerLiteral.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class IntegerLiteral extends Node implements ILiteral 13 | { 14 | use LiteralTraitWithValue; 15 | 16 | const KIND = 'int_literal'; 17 | 18 | /** 19 | * The string format integer data 20 | * eg. decimal 999 21 | * eg. octal 0777 22 | * eg. hex 0xfff 23 | * eg. binary 0b101010 24 | * 25 | * @var string 26 | */ 27 | // public $value; 28 | } 29 | 30 | class UnsignedIntegerLiteral extends IntegerLiteral 31 | { 32 | const KIND = 'uint_literal'; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /compiler/ast/base/DeferChecks.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | trait DeferChecksTrait 13 | { 14 | public $defer_check_identifiers = []; 15 | 16 | function set_defer_check_identifier(Identifiable $identifiable) 17 | { 18 | $this->defer_check_identifiers[$identifiable->name] = $identifiable; 19 | } 20 | 21 | function remove_defer_check_identifier(Identifiable $identifiable) 22 | { 23 | if (isset($this->defer_check_identifiers[$identifiable->name])) { 24 | unset($this->defer_check_identifiers[$identifiable->name]); 25 | } 26 | } 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /dist/docs/__public.th: -------------------------------------------------------------------------------- 1 | // the public declarations 2 | 3 | #unit tea/docs 4 | 5 | public PI Float = 3.1415926 6 | public ARRAY_CONST UInt.Array = [1, 2] 7 | public __CLASS__ String 8 | 9 | public demo_function2(message String = 'with a default value') 10 | 11 | public demo_function_with_a_return_type(some String) UInt 12 | 13 | public demo_function_with_callbacks(some String) String -> success(message String) String -> failure(error Any) 14 | 15 | public IDemo { 16 | CONST1 String = 'This is a constant!' 17 | 18 | static a_static_prop String 19 | 20 | static say_hello_with_static(name String = 'Benny') 21 | } 22 | 23 | public DemoPublicClass: DemoBaseClass, IDemo, DemoInterface { 24 | set_message(message String) 25 | } 26 | 27 | #php phpinfo() 28 | 29 | #php BadFunctionCallException: Exception { 30 | public getCode() Int 31 | public getMessage() String 32 | } 33 | 34 | // program end 35 | -------------------------------------------------------------------------------- /compiler/ast/declaration/PropertyDeclaration.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class PropertyDeclaration extends Node implements IClassMemberDeclaration 13 | { 14 | use DeclarationTrait; 15 | 16 | const KIND = 'property_declaration'; 17 | 18 | public $modifier; 19 | 20 | public $value; 21 | 22 | public $is_static; 23 | 24 | public function __construct(?string $modifier, string $name, IType $type = null, IExpression $value = null) 25 | { 26 | $this->modifier = $modifier; 27 | $this->name = $name; 28 | $this->value = $value; 29 | $this->type = $type; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /compiler/ast/declaration/CallbackProtocol.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class CallbackProtocol extends Node implements ICallableDeclaration, IVariableDeclaration 13 | { 14 | use DeclarationTrait; 15 | 16 | const KIND = 'callback_protocol'; 17 | 18 | public $async; 19 | 20 | public $parameters; 21 | 22 | public $checking; 23 | 24 | public function __construct(bool $async, string $name, ?IType $type, ParameterDeclaration ...$parameters) 25 | { 26 | $this->async = $async; 27 | $this->name = $name; 28 | $this->type = $type; 29 | $this->parameters = $parameters; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dist/tests/syntax/structure-for.php: -------------------------------------------------------------------------------- 1 | "hi", 7 | "k2" => "hello" 8 | ]; 9 | 10 | foreach (range(0, 9) as $val) { 11 | echo $val, NL; 12 | } 13 | 14 | if ($arr && count($arr) > 0) { 15 | foreach ($arr as $k => $v) { 16 | // no any 17 | } 18 | } 19 | elseif (true) { 20 | // no any 21 | } 22 | else { 23 | // no any 24 | } 25 | 26 | if (0 <= 9) { 27 | for ($i = 0; $i <= 9; $i += 1) { 28 | $i + 3; 29 | echo $i, NL; 30 | } 31 | } 32 | else { 33 | echo 'no loops', NL; 34 | } 35 | 36 | try { 37 | $__tmp0 = 9 * 2; 38 | $__tmp1 = 0 + 5; 39 | if ($__tmp0 >= $__tmp1) { 40 | for ($i = $__tmp0; $i >= $__tmp1; $i -= 2) { 41 | echo $i, NL; 42 | } 43 | } 44 | elseif ($arr) { 45 | echo 'oh!'; 46 | } 47 | } 48 | catch (\Exception $ex) { 49 | // no any 50 | } 51 | finally { 52 | // no any 53 | } 54 | // --------- 55 | 56 | // program end 57 | -------------------------------------------------------------------------------- /tests/syntax/main1.tea: -------------------------------------------------------------------------------- 1 | 2 | #main 3 | 4 | #php var_dump(v) 5 | 6 | internal TeaDemoClass { 7 | static static_prop1 = 'static prop value' 8 | 9 | prop1 = 'prop1 value' 10 | 11 | static static_method() { 12 | return this.static_prop1 13 | } 14 | 15 | method1(param1 String) { 16 | return this.prop1 17 | } 18 | 19 | method2() String { 20 | return 'some' 21 | } 22 | } 23 | 24 | echo "\n***Test for class call:" 25 | var php_class_demo_object = PHPClassDemo() 26 | echo php_class_demo_object.get_class_name('main1') 27 | var methods = php_class_demo_object.get_target_class_methods(TeaDemoClass) 28 | var_dump(methods) 29 | 30 | php_function_demo('hei~') 31 | var_dump(PHP_CONST_DEMO) 32 | 33 | echo "\n***Test for range:" 34 | var items = range(0, 9, 2) 35 | var_dump(items) 36 | 37 | echo "\n***Test for include:" 38 | var title = 'include from main1.tea' // use in included file 39 | var result = #include(label-expect) 40 | echo result 41 | 42 | -------------------------------------------------------------------------------- /compiler/ast/statement/BreakContinueStatement.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | interface IContinueAble {} 13 | 14 | class BreakStatement extends BaseStatement 15 | { 16 | const KIND = 'break_statement'; 17 | 18 | public $argument; // label or break argument 19 | 20 | public $layer_num; 21 | 22 | public $destination_label; 23 | 24 | public function __construct(string $argument = null, int $layer_num = null) 25 | { 26 | $this->argument = $argument; 27 | $this->layer_num = $layer_num; 28 | } 29 | } 30 | 31 | class ContinueStatement extends BreakStatement 32 | { 33 | const KIND = 'continue_statement'; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /compiler/ast/declaration/UseDeclaration.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class UseDeclaration extends Node implements IRootDeclaration, IMemberDeclaration 13 | { 14 | const KIND = 'use_declaration'; 15 | 16 | public $ns; 17 | 18 | public $name; 19 | 20 | public $target_name; 21 | 22 | public $source_name; 23 | 24 | public $source_declaration; 25 | 26 | public function __construct(NamespaceIdentifier $ns, string $target_name = null, string $source_name = null) 27 | { 28 | $this->ns = $ns; 29 | $this->name = $target_name; 30 | $this->target_name = $target_name; 31 | $this->source_name = $source_name; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/syntax/type-string.tea: -------------------------------------------------------------------------------- 1 | 2 | num = 123 3 | str = "hello " 4 | key = "f3" 5 | 6 | str = trim(str) 7 | 8 | echo str // Hello 9 | 10 | str.slice(2) 11 | str.slice(0, length: 3) 12 | 13 | arr = [ 14 | 'f1': 123, 15 | "f2": "hi", 16 | "f3": "字符串", 17 | "sub": [1, 2, 3] 18 | ] 19 | 20 | // 字符串(执行表达式) 21 | echo "${str concat 'abc'} world" // hello world 22 | echo '$str\n 23 | - ${arr['f1']} 24 | - ${arr[key]} 25 | ' 26 | 27 | // 纯字符串(不执行表达式) 28 | echo #text "$str world" // $str world 29 | echo #text ' 30 | - ${arr[\'f1\']} 31 | - ${arr[key]} 32 | ' 33 | 34 | // 声明变量并赋值 35 | abc = 'hhh' 36 | 37 | var1 =
    ${' 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /compiler/ast/base/Program.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class Program extends Node 13 | { 14 | use DeferChecksTrait; 15 | 16 | const KIND = 'program'; 17 | 18 | public $as_main_program = false; 19 | 20 | public $docs; 21 | 22 | public $name; 23 | 24 | public $file; 25 | 26 | /** 27 | * the use statements in current program 28 | * @var UseStatement[] 29 | */ 30 | public $uses = []; 31 | 32 | /** 33 | * the targets of use statements in current program 34 | * @var UseDeclcaration[] 35 | */ 36 | public $use_targets = []; 37 | 38 | public $declarations = []; 39 | 40 | public $main_function; 41 | 42 | public $symbols = []; 43 | 44 | public $unit; 45 | 46 | public $checked; // set true when checked by ASTChecker 47 | 48 | public function __construct(string $file, Unit $unit) 49 | { 50 | $this->unit = $unit; 51 | $this->file = $file; 52 | $this->name = $this->generate_name(); 53 | } 54 | 55 | public function append_declaration(IDeclaration $declaration) 56 | { 57 | $this->declarations[(string)$declaration->name] = $declaration; 58 | } 59 | 60 | public function append_defer_check_for_block(BaseBlock $block) 61 | { 62 | if (!$block->defer_check_identifiers) { 63 | return; 64 | } 65 | 66 | $this->defer_check_identifiers = array_merge($this->defer_check_identifiers, $block->defer_check_identifiers); 67 | } 68 | 69 | private function generate_name() 70 | { 71 | $name = $this->unit->get_abs_path($this->file); 72 | 73 | if ($dot_pos = strrpos($name, _DOT)) { 74 | $name = substr($name, 0, $dot_pos); 75 | } 76 | 77 | return $name; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /compiler/ast/declaration/FunctionDeclaration.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | interface IEnclosingBlock {} 13 | interface IFunctionDeclaration extends ICallableDeclaration, IRootDeclaration, IClassMemberDeclaration {} 14 | 15 | trait FunctionLikeTrait 16 | { 17 | use DeclarationTrait, DeferChecksTrait; 18 | 19 | public $is_static = false; 20 | 21 | public $parameters; 22 | 23 | public $fixed_body; 24 | 25 | public $auto_declarations = []; 26 | 27 | public $checking; // set true when on checking by ASTChecker 28 | 29 | public $checked; // set true when checked by ASTChecker 30 | 31 | function set_body_with_expression(IExpression $expression) 32 | { 33 | $this->body = $expression; 34 | } 35 | } 36 | 37 | class FunctionDeclaration extends BaseBlock implements IEnclosingBlock, IFunctionDeclaration 38 | { 39 | use FunctionLikeTrait; 40 | 41 | const KIND = 'function_declaration'; 42 | 43 | public $modifier; 44 | 45 | public $callbacks; 46 | 47 | /** 48 | * @var Program 49 | */ 50 | public $program; 51 | 52 | function __construct(?string $modifier, string $name, ?IType $type, ?array $parameters) 53 | { 54 | $this->modifier = $modifier; 55 | $this->name = $name; 56 | $this->type = $type; 57 | $this->parameters = $parameters; 58 | } 59 | 60 | function set_callbacks(CallbackProtocol ...$callbacks) 61 | { 62 | $this->callbacks = $callbacks; 63 | } 64 | } 65 | 66 | class FunctionBlock extends FunctionDeclaration 67 | { 68 | const KIND = 'function_block'; 69 | } 70 | 71 | class MainFunctionBlock extends FunctionBlock 72 | { 73 | const KIND = 'main_function'; 74 | 75 | public function __construct() 76 | { 77 | $this->parameters = []; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /compiler/helper/PHPUnitScanner.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class PHPUnitScanner 13 | { 14 | private $path; 15 | private $function_map = []; 16 | private $class_map = []; 17 | 18 | public function scan(string $path) 19 | { 20 | $this->path = realpath($path) . DS; 21 | $this->scan_files($this->path); 22 | $this->scan_sub_directories($this->path); 23 | 24 | return $this->class_map; 25 | } 26 | 27 | public function scan_sub_directories(string $path) 28 | { 29 | $items = array_diff(scandir($path), ['..', '.', 'test', 'www']); 30 | foreach ($items as $item) { 31 | $sub_path = $path . $item; 32 | if (is_dir($sub_path)) { 33 | $this->scan_files($sub_path . DS); 34 | $this->scan_sub_directories($sub_path . DS); 35 | } 36 | } 37 | } 38 | 39 | public function scan_files(string $path) 40 | { 41 | $files = glob($path . '*.php'); 42 | 43 | foreach ($files as $file) { 44 | $file = realpath($file); 45 | $code = file_get_contents($file); 46 | $file = str_replace($this->path, '', $file); 47 | 48 | preg_match('/namespace\s+([a-z0-9_\\\\]+)/i', $code, $match); 49 | $namespace = $match[1] ?? null; 50 | 51 | preg_match_all('/\n\s?(?:(?:abstract\s+)?class|interface|trait)\s+([a-z0-9_]+)/i', $code, $matches, PREG_PATTERN_ORDER); 52 | $classes = $matches[1]; 53 | 54 | if ($classes) { 55 | foreach ($classes as $class) { 56 | if ($namespace) { 57 | $class = $namespace . '\\' . $class; 58 | } 59 | 60 | if (isset($this->class_map[$class])) { 61 | throw new \Exception("Class '$class' in file '$file' duplicated."); 62 | } 63 | 64 | $this->class_map[$class] = $file; 65 | } 66 | } 67 | elseif (preg_match('/\bfunction\s+[a-z][a-z0-9]/i', $code)) { 68 | $this->function_map[] = $file; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /dist/tests/syntax/type-xview.php: -------------------------------------------------------------------------------- 1 | text = $text; 16 | } 17 | 18 | public function render(): string 19 | { 20 | $text = _str_replace($this->text, NL, '
    '); 21 | return '' . $text . ''; 22 | } 23 | } 24 | 25 | #public 26 | class DemoList extends BaseView 27 | { 28 | const ABC = '12'; 29 | 30 | public $tag; 31 | public $title; 32 | public $items; 33 | 34 | public $cells = []; 35 | 36 | public function __construct(string $name, string $title = '', array $items = [], callable $each = null, callable $done = null) 37 | { 38 | $cell = null; 39 | 40 | $this->items = $items; 41 | 42 | if ($each) { 43 | foreach ($items as $item) { 44 | $cell = $each((string)$item); 45 | array_push($this->cells, $cell); 46 | } 47 | } 48 | 49 | $done && $done(); 50 | } 51 | 52 | public function render(): string 53 | { 54 | $cells = []; 55 | foreach ($this->items as $key => $value) { 56 | if (1) { 57 | array_push($cells, '
  • ' . $key . ': ' . $value . '
  • '); 58 | } 59 | else { 60 | array_push($cells, '
  • ' . $key . ': ' . $value . '
  • '); 61 | } 62 | } 63 | 64 | return '<' . $this->tag . ' id="' . $this->name . '"> 65 | ' . ($this->title == "abc" ? '

    ' . htmlspecialchars($this->title . 123, ENT_QUOTES) . '

    ' : null) . ' 66 | 67 | 68 | ' . implode($cells, NL) . ' 69 | 70 | 71 | ' . implode($this->subviews, NL) . ' 72 | 73 | 74 | tag . '>'; 75 | } 76 | } 77 | 78 | // --------- 79 | $xview = new Cell('string'); 80 | 81 | new DemoList('demo-list', 'title', [], function () { 82 | return new Cell(); 83 | }); 84 | 85 | new DemoList('demo-list', 'Demo List', ['A', 'B', 'C'], function (string $item) { 86 | return new Cell($item); 87 | }); 88 | 89 | $str = 'str'; 90 | $num = 2; 91 | 92 | $abc = new DemoList('', '', ['A', 'B', 'C'], function ($item) { 93 | return new Cell((string)$item); 94 | }, function () use($str, $num) { 95 | echo $str, $num, NL; 96 | }); 97 | 98 | echo $abc, NL; 99 | // --------- 100 | 101 | // program end 102 | -------------------------------------------------------------------------------- /compiler/ast/expression/Operations.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | abstract class BaseBinaryOperation extends Node implements IExpression 13 | { 14 | public $left; 15 | public $right; 16 | public $operator; 17 | } 18 | 19 | class AsOperation extends BaseBinaryOperation 20 | { 21 | const KIND = 'as_operation'; 22 | 23 | public function __construct(IExpression $left, IType $right) 24 | { 25 | $this->operator = OperatorFactory::$_as; 26 | $this->left = $left; 27 | $this->right = $right; 28 | } 29 | } 30 | 31 | class IsOperation extends BaseBinaryOperation 32 | { 33 | const KIND = 'is_operation'; 34 | 35 | public function __construct(IExpression $left, IExpression $right) 36 | { 37 | $this->operator = OperatorFactory::$_is; 38 | $this->left = $left; 39 | $this->right = $right; 40 | } 41 | } 42 | 43 | class BinaryOperation extends BaseBinaryOperation 44 | { 45 | const KIND = 'binary_operation'; 46 | 47 | public function __construct(OperatorSymbol $operator, IExpression $left, IExpression $right) 48 | { 49 | $this->operator = $operator; 50 | $this->left = $left; 51 | $this->right = $right; 52 | } 53 | } 54 | 55 | class PrefixOperation extends Node implements IExpression 56 | { 57 | const KIND = 'prefix_operation'; 58 | 59 | public $operator; 60 | public $expression; 61 | 62 | public function __construct(OperatorSymbol $operator, IExpression $expression) 63 | { 64 | $this->operator = $operator; 65 | $this->expression = $expression; 66 | } 67 | } 68 | 69 | // class PostfixOperation extends Node implements IExpression 70 | // { 71 | // const KIND = 'postfix_operation'; 72 | 73 | // public $operator; 74 | // public $expression; 75 | 76 | // public function __construct(OperatorSymbol $operator, IExpression $expression) 77 | // { 78 | // $this->operator = $operator; 79 | // $this->expression = $expression; 80 | // } 81 | // } 82 | 83 | // class FunctionalOperation extends Node implements IExpression 84 | // { 85 | // const KIND = 'functional_operation'; 86 | 87 | // public $operator; 88 | // public $arguments; 89 | 90 | // public function __construct(OperatorSymbol $operator, IExpression ...$arguments) 91 | // { 92 | // $this->operator = $operator; 93 | // $this->arguments = $arguments; 94 | // } 95 | // } 96 | -------------------------------------------------------------------------------- /compiler/helper/PHPLoaderMaker.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class PHPLoaderMaker 13 | { 14 | const LOADER_FILE = '__unit.php'; 15 | 16 | const GENERATES_TAG = '# --- generates ---'; 17 | 18 | public static function generate_loader_file(string $path, array $autoloads, string $namespace = null) 19 | { 20 | $loader_file = $path . static::LOADER_FILE; 21 | $warring = '// Please do not modify the following contents'; 22 | 23 | if (!file_exists($loader_file)) { 24 | static::init_loader_file($loader_file, $namespace, $warring); 25 | } 26 | 27 | $autoloads = self::render_autoloads_code($autoloads, '__DIR__ . DIRECTORY_SEPARATOR'); 28 | 29 | // write file 30 | $contents = file_get_contents($loader_file); 31 | if (strpos($contents, self::GENERATES_TAG)) { 32 | $contents = preg_replace('/' . self::GENERATES_TAG . '[\w\W]+/', $autoloads, $contents); 33 | if (!$contents) { 34 | throw new \Exception("Unexpected error on replaceing generated contents."); 35 | } 36 | } 37 | else { 38 | $contents .= "{$warring}{$autoloads}"; 39 | } 40 | 41 | file_put_contents($loader_file, $contents); 42 | } 43 | 44 | public static function render_autoloads_code(array $autoloads, string $unit_path) 45 | { 46 | $tag = self::GENERATES_TAG; 47 | $autoloads = static::stringfy_autoloads($autoloads); 48 | 49 | return << $file) { 86 | $items[] = "'$class' => '$file'"; 87 | } 88 | 89 | $items = join(",\n\t", $items); 90 | 91 | return <<subnode->text('red')->subnode = new_collector_demo(); 30 | 31 | $abc = new CollectorDemo(); 32 | 33 | $factory = new CollectorDemoFactory(); 34 | 35 | new_collector_demo(); 36 | $factory->new_collector_demo(); 37 | 38 | if (1) { 39 | (new CollectorDemo())->text('red')->subnode->text('hei~'); 40 | } 41 | 42 | foreach ([1, 2, 3] as $item) { 43 | (new CollectorDemo())->subnode->text('hello'); 44 | } 45 | 46 | return $__collects; 47 | } 48 | 49 | function xrange(int $start, int $stop, int $step = 1) 50 | { 51 | $i = null; 52 | 53 | if ($step > 0) { 54 | $i = $start; 55 | while ($i < $stop) { 56 | yield $i; 57 | $i += $step; 58 | } 59 | 60 | return; 61 | } 62 | 63 | if ($step == 0) { 64 | throw new Exception('Step should not be 0.'); 65 | } 66 | 67 | $i = $start; 68 | while ($i > $stop) { 69 | yield $i; 70 | $i += $step; 71 | } 72 | } 73 | 74 | function get_class(): string 75 | { 76 | return Test1::class; 77 | } 78 | 79 | function fn0($str) 80 | { 81 | echo $str, NL; 82 | } 83 | 84 | function fn1(callable $callee) 85 | { 86 | $unknow_type_value = $callee('hei'); 87 | } 88 | 89 | function fn2(string $class) 90 | { 91 | $class(); 92 | } 93 | 94 | function fn3(\Exception $ex, string $num = null) 95 | { 96 | $num = 1; 97 | echo $ex->getMessage(), NL; 98 | } 99 | 100 | 101 | // program end 102 | 103 | # --- generates --- 104 | const __AUTOLOADS = [ 105 | 'tea\tests\syntax\CollectorDemo' => 'type-collector.php', 106 | 'tea\tests\syntax\CollectorDemoFactory' => 'type-collector.php', 107 | 'tea\tests\syntax\TeaDemoClass' => 'main1.php', 108 | 'tea\tests\syntax\Cell' => 'type-xview.php', 109 | 'tea\tests\syntax\DemoList' => 'type-xview.php', 110 | 'tea\tests\syntax\IDemo' => 'class.php', 111 | 'tea\tests\syntax\IDemoTrait' => 'class.php', 112 | 'tea\tests\syntax\BaseClass' => 'class.php', 113 | 'tea\tests\syntax\Test1' => 'class.php', 114 | 'tea\tests\syntax\Test2' => 'class.php', 115 | 'tea\tests\syntax\ITest' => 'class.php', 116 | 'tea\tests\syntax\Test3' => 'class.php', 117 | 'tea\tests\syntax\Test4' => 'class.php', 118 | 'tea\tests\syntax\Test5' => 'class.php' 119 | ]; 120 | 121 | spl_autoload_register(function ($class) { 122 | isset(__AUTOLOADS[$class]) && require UNIT_PATH . __AUTOLOADS[$class]; 123 | }); 124 | 125 | // end 126 | -------------------------------------------------------------------------------- /compiler/parser/TeaDocsTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | trait TeaDocsTrait 13 | { 14 | protected function read_docs() 15 | { 16 | $opened_pos = $this->pos; 17 | 18 | $left_spaces = $this->get_previous_inline($this->pos - 1); 19 | 20 | $token = $this->scan_token_ignore_space(); 21 | if ($token === NL) { 22 | $this->skip_token(NL); // skip current line 23 | } 24 | else { 25 | $docs = $this->read_inline_docs(); 26 | if ($docs !== null) { 27 | return $docs; 28 | } 29 | } 30 | 31 | return $this->read_multiline_docs($left_spaces, $opened_pos); 32 | } 33 | 34 | private function read_inline_docs() 35 | { 36 | $content = ''; 37 | while (($token = $this->scan_string_component()) !== null) { 38 | if ($token === _DOCS_MARK) { 39 | // doc end 40 | $docs = new Docs([$content]); 41 | break; 42 | } 43 | elseif ($token === NL) { 44 | $docs = null; 45 | break; // ignore inline doc content 46 | } 47 | else { 48 | $content .= $token; 49 | } 50 | } 51 | 52 | return $docs; 53 | } 54 | 55 | private function read_multiline_docs(string $left_spaces, int $opened_pos) 56 | { 57 | // remove the indents 58 | $left_spaces_len = strlen($left_spaces); 59 | 60 | // split each lines to items 61 | $items = []; 62 | 63 | $tmp = ''; 64 | while (($token = $this->scan_string_component()) !== null) { 65 | if ($token === _DOCS_MARK && $this->get_previous_inline($this->pos - 1) === $left_spaces) { 66 | if ($tmp !== _NOTHING) $items[] = $tmp; 67 | break; 68 | } 69 | 70 | if ($token === NL) { 71 | $items[] = $this->remove_prefix_spaces($tmp, $left_spaces, $left_spaces_len); 72 | $tmp = ''; 73 | } 74 | elseif ($token === _AT && trim($tmp) === _NOTHING) { 75 | $items[] = $this->read_parameter_doc(); 76 | } 77 | else { 78 | $tmp .= $token; 79 | } 80 | } 81 | 82 | // is not found the end mark of docs? 83 | if ($this->pos >= $this->tokens_count) { 84 | $line = $this->get_line_by_pos($opened_pos); 85 | throw $this->new_exception("The close mark of Docs which opened on line {$line} not found."); 86 | } 87 | 88 | $this->expect_statement_end(); 89 | 90 | return new Docs($items); 91 | } 92 | 93 | private function remove_prefix_spaces(string $content, string $left_spaces, int $left_spaces_len) 94 | { 95 | if ($left_spaces_len === 0 || $content === _NOTHING) { 96 | // not to do anyting 97 | } 98 | elseif (substr($content, 0, $left_spaces_len) === $left_spaces) { 99 | $content = substr(rtrim($content), $left_spaces_len); 100 | } 101 | else { 102 | throw $this->new_exception("The indents should be same to the indents of Docs begin mark."); 103 | } 104 | 105 | return $content; 106 | } 107 | 108 | private function read_parameter_doc() 109 | { 110 | $name = $this->expect_identifier_token(); 111 | $comment = $this->scan_to_token(NL); 112 | $this->scan_token(); // skip NL 113 | 114 | return new ParameterDoc($name, null, $comment); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /compiler/constants.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | const 13 | // do not use the DIRECTORY_SEPARATOR to render targets code 14 | DS = '/', 15 | 16 | _MAX_STRUCT_DIMENSIONS = 12, 17 | 18 | _BUILTIN_NS = 'tea/builtin', 19 | 20 | // system labels 21 | _TEA = 'tea', _PHP = 'php', 22 | _UNIT = 'unit', _USE = 'use', 23 | _MAIN = 'main', _EXPECT = 'expect', _INCLUDE = 'include', 24 | _TEXT = 'text', _DEFAULT = 'default', 25 | 26 | // meta types 27 | _VOID = 'Void', _NONE = 'None', _ANY = 'Any', 28 | _SCALAR = 'Scalar', 29 | // _BYTES = 'Bytes', 30 | _STRING = 'String', 31 | _INT = 'Int', _UINT = 'UInt', _FLOAT = 'Float', _BOOL = 'Bool', 32 | _ITERABLE = 'Iterable', _DICT = 'Dict', _ARRAY = 'Array', // 'Pair', 'Matrix', 'Tensor' 33 | _OBJECT = 'Object', _XVIEW = 'XView', _REGEX = 'Regex', 34 | _CALLABLE = 'Callable', _CLASS = 'Class', _NAMESPACE = 'Namespace', 35 | 36 | // the number system 37 | _BASE_BINARY = '0b', _BASE_OCTAL = '0', _BASE_DECIMAL = '', _BASE_HEX = '0x', 38 | _SCIENTIFIC_NOTATION = 'e', 39 | 40 | // blank chars 41 | // some blank chars did not supported now, eg. \f, \v, [0x2000 - 0x200B], line separator \u2028, paragraph separator \u2029 42 | _NOTHING = '', _SPACE = ' ', _TAB = "\t", _CR = "\r", 43 | 44 | // syntax chars, operators 45 | _SLASH = '/', _BACK_SLASH = '\\', 46 | _EXCLAMATION = '!', _DOLLAR = '$', _SHARP = '#', _AT = '@', _UNDERSCORE = '_', _STRIKETHROUGH = '-', 47 | _COLON = ':', _COMMA = ',', _SEMICOLON = ';', 48 | _AS = 'as', _IS = 'is', 49 | _IN = 'in', _TO = 'to', _DOWNTO = 'downto', _STEP = 'step', // just use in the 'for' block 50 | _NEGATION = '-', // -1, -num 51 | _ADDITION = '+', _SUBTRACTION = '-', _MULTIPLICATION = '*', _DIVISION = '/', _REMAINDER = '%', _EXPONENTIATION = '**', 52 | _BITWISE_NOT = '~', _BITWISE_AND = '&', _BITWISE_OR = '|', _BITWISE_XOR = '^', //bitwise 53 | _CONCAT = 'concat', _MERGE = 'merge', 54 | _NOT_EQUAL = '!=', 55 | _NOT = 'not', _AND = 'and', _OR = 'or', // bool 56 | _NONE_COALESCING = '??', _CONDITIONAL = '?', 57 | _ASSIGN = '=', _DOT = '.', _NOTIFY = '->', _ARROW = '=>', _RELAY = '==>', 58 | _COLLECT = '>>', 59 | _SHIFT_LEFT = '<<', _SHIFT_RIGHT = '>>', 60 | 61 | _SINGLE_QUOTE = '\'', _DOUBLE_QUOTE = '"', 62 | _XTAG_OPEN = '<', _XTAG_CLOSE = '>', 63 | _PAREN_OPEN = '(', _PAREN_CLOSE = ')', 64 | _BRACKET_OPEN = '[', _BRACKET_CLOSE = ']', 65 | _BRACE_OPEN = '{', _BRACE_CLOSE = '}', 66 | _BLOCK_BEGIN = _BRACE_OPEN, _BLOCK_END = _BRACE_CLOSE, 67 | _DOCS_MARK = '---', _INLINE_COMMENT_MARK = '//', _COMMENTS_OPEN = '/*', _COMMENTS_CLOSE = '*/', 68 | 69 | // keywords 70 | _VAL_NONE = 'none', _VAL_TRUE = 'true', _VAL_FALSE = 'false', _UNIT_PATH = 'UNIT_PATH', 71 | _VAR = 'var', _YIELD = 'yield', 72 | _IF = 'if', _ELSE = 'else', _ELSEIF = 'elseif', _CASE = 'case', 73 | _FOR = 'for', _WHILE = 'while', _LOOP = 'loop', 74 | _TRY = 'try', _CATCH = 'catch', _FINALLY = 'finally', 75 | _ECHO = 'echo', _PRINT = 'print', _RETURN = 'return', _EXIT = 'exit', _BREAK = 'break', _CONTINUE = 'continue', _THROW = 'throw', 76 | _STATIC = 'static', _MASKED = 'masked', _PUBLIC = 'public', _INTERNAL = 'internal', _PROTECTED = 'protected', _PRIVATE = 'private', 77 | _THIS = 'this', _SUPER = 'super', _CONSTRUCT = 'construct', _DESTRUCT = 'destruct'; 78 | 79 | 80 | // program end 81 | 82 | -------------------------------------------------------------------------------- /compiler/ast/base/Identifiable.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | abstract class Identifiable extends Node implements IExpression, ICallee, IAssignable, IType 13 | { 14 | public $name; 15 | 16 | public $symbol; 17 | 18 | /** 19 | * is has any operation like accessing or call 20 | * used for render target code 21 | * @var bool 22 | */ 23 | public $with_call_or_accessing; 24 | 25 | public function __construct(string $name) 26 | { 27 | $this->name = $name; 28 | } 29 | 30 | public function get_unit() 31 | { 32 | return $this->symbol->declaration->unit; 33 | } 34 | } 35 | 36 | class AccessingIdentifier extends Identifiable 37 | { 38 | const KIND = 'accessing_identifier'; 39 | 40 | public $master; 41 | 42 | public function __construct(IExpression $master, string $name) 43 | { 44 | $master->with_call_or_accessing = true; 45 | 46 | $this->master = $master; 47 | $this->name = $name; 48 | } 49 | } 50 | 51 | class PlainIdentifier extends Identifiable 52 | { 53 | const KIND = 'plain_identifier'; 54 | 55 | public static function create_with_symbol(Symbol $symbol) 56 | { 57 | $identifier = new static($symbol->name); 58 | $identifier->symbol = $symbol; 59 | return $identifier; 60 | } 61 | 62 | public function to_class_identifier() 63 | { 64 | $identifier = new ClassIdentifier($this->name); 65 | $identifier->symbol = $this->symbol; 66 | return $identifier; 67 | } 68 | 69 | public function is_based_with(IType $type) 70 | { 71 | if (!$this->symbol->declaration instanceof ClassLikeDeclaration) { 72 | return false; 73 | } 74 | 75 | if ($this->symbol->declaration->is_based_with_symbol($type->symbol)) { 76 | return true; 77 | } 78 | 79 | return false; 80 | } 81 | 82 | public function is_accept_type(IType $type) 83 | { 84 | return $type->symbol === $this->symbol || $type === TypeFactory::$_none 85 | // for check MetaClassDeclaration like String 86 | // can not use symbol to compare MetaClassDeclaration, because of the symbol maybe 'this' 87 | || $this->symbol->declaration === $type->symbol->declaration 88 | || $type->is_based_with($this) 89 | ; 90 | } 91 | 92 | public function is_same_or_based_with(IType $type) 93 | { 94 | return $this->symbol === $type->symbol || $this->is_based_with($type); 95 | } 96 | } 97 | 98 | class ConstantIdentifier extends PlainIdentifier implements ILiteral 99 | { 100 | const KIND = 'constant_identifier'; 101 | } 102 | 103 | class VariableIdentifier extends PlainIdentifier 104 | { 105 | const KIND = 'variable_identifier'; 106 | } 107 | 108 | class ClassLikeIdentifier extends PlainIdentifier 109 | { 110 | const KIND = 'classlike_identifier'; 111 | 112 | public $ns; 113 | 114 | public function __construct(string $name) 115 | { 116 | $this->name = $name; 117 | } 118 | 119 | public function set_namespace(PlainIdentifier $ns) 120 | { 121 | $this->ns = $ns; 122 | } 123 | } 124 | 125 | class ClassIdentifier extends ClassLikeIdentifier 126 | { 127 | const KIND = 'class_identifier'; 128 | } 129 | 130 | // class UriIdentifier extends Identifiable 131 | // { 132 | // const KIND = 'uri_identifier'; 133 | 134 | // public $ns_names; 135 | 136 | // public function __construct(array $ns_names, string $name) 137 | // { 138 | // $this->ns_names = $ns_names; 139 | // $this->name = $name; 140 | // } 141 | 142 | // public function get_first_ns_name() 143 | // { 144 | // return $this->ns_names[0]; 145 | // } 146 | 147 | // public function is_compatible_to(IType $type) 148 | // { 149 | // return $this->symbol === $type->symbol; 150 | // } 151 | 152 | // public function __toString() 153 | // { 154 | // return join(_DOT, $this->ns_names) . _DOT . $this->name; 155 | // } 156 | // } 157 | -------------------------------------------------------------------------------- /compiler/ast/declaration/ClassLikeDeclaration.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ClassLikeDeclaration extends RootDeclaration implements IMemberDeclaration 13 | { 14 | use DeferChecksTrait; 15 | 16 | public $modifier; 17 | 18 | public $ns; 19 | 20 | public $origin_name; 21 | 22 | /** 23 | * the implements interfaces or inherits class 24 | * @var ClassLikeIdentifier[] 25 | */ 26 | public $baseds = []; 27 | 28 | /** 29 | * the inherits class for ClassDeclaration 30 | * @var ClassDeclaration 31 | */ 32 | public $inherits; 33 | 34 | /** 35 | * @var IClassMemberDeclaration[] 36 | */ 37 | public $members = []; 38 | 39 | /** 40 | * 实际可用的成员,包括继承父类的、接口中默认实现的,和本类中定义的 41 | * @var array [name => IClassMemberDeclaration] 42 | */ 43 | public $actual_members = []; 44 | 45 | /** 46 | * The symbols for current class instance 47 | * used for this, super 48 | */ 49 | public $symbols = []; 50 | 51 | // public $member_symbols = []; // constants, properties, methods 52 | 53 | public $super_block; // aways none 54 | 55 | /** 56 | * @var Program 57 | */ 58 | public $program; 59 | 60 | public $checked; // set true when checked by ASTChecker 61 | 62 | public function __construct(?string $modifier, $name) 63 | { 64 | $this->modifier = $modifier; 65 | $this->name = $name; 66 | $this->type = TypeFactory::$_class; 67 | } 68 | 69 | public function set_baseds(ClassLikeIdentifier ...$baseds) 70 | { 71 | $this->baseds = $baseds; 72 | } 73 | 74 | public function append_member(IClassMemberDeclaration $member) 75 | { 76 | if (isset($this->members[$member->name])) { 77 | return false; 78 | } 79 | 80 | $this->members[$member->name] = $member; 81 | return true; 82 | } 83 | 84 | public function is_same_or_based_with_symbol(Symbol $symbol) 85 | { 86 | return $this->symbol === $symbol || $this->is_based_with_symbol($symbol); 87 | } 88 | 89 | public function is_based_with_symbol(Symbol $symbol) 90 | { 91 | // check the implements interfaces 92 | if ($this->baseds) { 93 | foreach ($this->baseds as $interface) { 94 | if ($interface->symbol === $symbol) { 95 | return true; 96 | } 97 | 98 | // if $interface->symbol is null, it should be not checked ast... 99 | 100 | if ($interface->symbol->declaration->is_based_with_symbol($symbol)) { 101 | return true; 102 | } 103 | } 104 | } 105 | 106 | return false; 107 | } 108 | } 109 | 110 | class ClassDeclaration extends ClassLikeDeclaration implements ICallableDeclaration 111 | { 112 | const KIND = 'class_declaration'; 113 | 114 | // if not a declare mode, set true 115 | public $define_mode = false; 116 | 117 | public function is_based_with_symbol(Symbol $symbol) 118 | { 119 | // check the extends class first 120 | if ($this->inherits !== null) { 121 | if ($this->inherits->symbol === $symbol) { 122 | return true; 123 | } 124 | 125 | if ($this->inherits->symbol->declaration->is_based_with_symbol($symbol)) { 126 | return true; 127 | } 128 | } 129 | 130 | return parent::is_based_with_symbol($symbol); 131 | } 132 | } 133 | 134 | class MetaClassDeclaration extends ClassDeclaration 135 | { 136 | const KIND = 'class_declaration'; 137 | } 138 | 139 | class InterfaceDeclaration extends ClassLikeDeclaration 140 | { 141 | const KIND = 'interface_declaration'; 142 | 143 | /** 144 | * 是否有默认实现的成员 145 | * @var bool 146 | */ 147 | public $has_default_implementation; 148 | 149 | public function append_member(IClassMemberDeclaration $member) 150 | { 151 | if ($member instanceof PropertyDeclaration || $member instanceof FunctionBlock) { 152 | $this->has_default_implementation = true; 153 | } 154 | 155 | return parent::append_member($member); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /tests/syntax/class.tea: -------------------------------------------------------------------------------- 1 | 2 | #main 3 | 4 | #php __CLASS__ String 5 | 6 | internal IDemo: BaseInterface { 7 | CONST1 = 'const value' 8 | 9 | prop String 10 | 11 | construct(some String) { 12 | this.prop = some 13 | } 14 | 15 | get_class_name(caller String = 'caller1') String { 16 | return __CLASS__ 17 | } 18 | } 19 | 20 | internal BaseClass { 21 | set_value(num Int) { 22 | // 23 | } 24 | } 25 | 26 | // inherts a class, and implements an interface 27 | internal Test1: BaseClass, IDemo { 28 | construct(some String) { 29 | // 30 | } 31 | 32 | static static_method() { 33 | echo this.CONST1 34 | } 35 | } 36 | 37 | var t1 = Test1('some') 38 | echo t1.get_class_name('Unknow') 39 | 40 | internal Test2 { 41 | // 自定义类作为属性 42 | t1 BaseInterface 43 | 44 | // 参数类型为自定义类 45 | --- 46 | Some text 47 | @t1 some parameter comment 48 | --- 49 | set_t1(t1 BaseInterface) { 50 | this.t1 = t1 51 | } 52 | 53 | call_t1_fn() String { 54 | return this.t1.get_class_name('class.tea') 55 | } 56 | } 57 | 58 | var t2 = Test2() 59 | t2.set_t1(t1) 60 | echo t2.call_t1_fn() 61 | 62 | internal ITest { 63 | f1() String 64 | } 65 | 66 | internal Test3: ITest { 67 | f1() String { 68 | return "hi" 69 | } 70 | } 71 | 72 | internal Test4 { 73 | static fx(it ITest) { 74 | echo it.f1() 75 | } 76 | } 77 | 78 | var a ITest 79 | a = Test3() 80 | 81 | Test4.fx(Test3()) 82 | 83 | internal Test5 { 84 | // const 85 | C_1 = '123' 86 | 87 | // attribute 88 | a_10 = [:] 89 | a_11 = ["f_1": 123, "f_2": "string"] 90 | a_20 Int 91 | //a_21 Int = none 92 | a_22 Int = 123 93 | a_30 String 94 | //a_31 String = none 95 | a_32 String = "hi" 96 | a_40 Bool 97 | a_41 Bool = true 98 | a_42 Bool = false 99 | a_50 Array 100 | a_51 Array = [] 101 | a_52 Array = [1, 2, "str"] 102 | a_53 Array = [1, 2, [4, 5, 6]] 103 | 104 | 105 | // static attribute 106 | static sa_10 = [:] 107 | static sa_11 = ["f_1": 123, "f_2": "string"] 108 | static sa_20 Int 109 | static sa_22 Int = 123 110 | static sa_30 String 111 | static sa_32 String = "hi" 112 | static sa_41 Bool = true 113 | static sa_51 Array = [] 114 | static sa_53 Array = [1, 2, [4, 5, 6]] 115 | 116 | static sm3() { 117 | // none 118 | } 119 | 120 | static sm_30(arg) String { 121 | return "" 122 | } 123 | 124 | static sm_31(arg_1, arg_2) String { 125 | str = "sth." 126 | 127 | return str 128 | } 129 | 130 | static sm_33(arg = [:]) Int { 131 | return 1 132 | } 133 | 134 | static sm_34(arg = [1, 2, 3]) Float { 135 | return 1.23 136 | } 137 | 138 | static sm_35(arg_1 Dict, arg_2) Bool { 139 | return true 140 | } 141 | 142 | static sm_360(arg_1 Dict, arg_2 = "str") Array { 143 | return [] 144 | } 145 | 146 | static sm_361(arg_1 Dict, arg_2 = "str") Array { 147 | return [1, 2, 3] 148 | } 149 | 150 | static sm_362(arg_1 Array, arg_2 = "str") Array { 151 | return arg_1 152 | } 153 | 154 | static sm_370(arg_1 Dict, arg_2 = "str") Dict { 155 | return [:] 156 | } 157 | 158 | static sm_371(arg_1 Dict, arg_2 = "str") Dict { 159 | return arg_1 160 | } 161 | 162 | static sm_372(arg_1 Array, arg_2 = "str") Dict { 163 | return ["a": 123] 164 | } 165 | 166 | m3() { 167 | // none 168 | } 169 | 170 | m_30(arg) String { 171 | return "" 172 | } 173 | 174 | m_31(arg_1, arg_2) String { 175 | str = "sth." 176 | 177 | return str 178 | } 179 | 180 | m_33(arg = [:]) Int { 181 | return 1 182 | } 183 | 184 | m_34(arg = [1, 2, 3]) Float { 185 | return 1.23 186 | } 187 | 188 | m_35(arg_1 Dict, arg_2) Bool { 189 | return true 190 | } 191 | 192 | m_360(arg_1 Dict, arg_2 = "str") Array { 193 | return [] 194 | } 195 | 196 | m_361(arg_1 Dict, arg_2 = "str") Array { 197 | return [1, 2, 3] 198 | } 199 | 200 | m_362(arg_1 Array, arg_2 = "str") Array { 201 | return arg_1 202 | } 203 | 204 | m_363(arg_1 Array, arg_2 = "str") Array { 205 | return this.a_53 206 | } 207 | 208 | m_370(arg_1 Dict, arg_2 = "str") { 209 | return [] 210 | } 211 | 212 | m_371(arg_1 Dict, arg_2 = "str") Dict { 213 | return arg_1 214 | } 215 | 216 | m_372(arg_1 Array, arg_2 = "str") Dict { 217 | return ["a": 123] 218 | } 219 | 220 | m_373(arg_1 Array, arg_2 = "str") Dict { 221 | return this.a_10 222 | } 223 | } 224 | 225 | -------------------------------------------------------------------------------- /compiler/ast/base/Types.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | use Exception; 13 | 14 | interface IType {} 15 | 16 | class BaseType extends Node implements IType { 17 | 18 | const KIND = 'type_identifier'; 19 | 20 | const ACCEPT_TYPES = []; 21 | 22 | public $name; 23 | 24 | // for render 25 | public $dist_name; 26 | 27 | public $symbol; 28 | 29 | public function __construct(string $name, int $pos = null) { 30 | $this->name = $name; 31 | $this->pos = $pos; 32 | } 33 | 34 | // to support class 35 | public function is_based_with(IType $type) { 36 | return false; 37 | } 38 | 39 | public function is_accept_type(IType $type) { 40 | return $type === $this 41 | || $type === TypeFactory::$_none 42 | || in_array($type->name, static::ACCEPT_TYPES, true) 43 | || $type->symbol->declaration === $this->symbol->declaration; 44 | } 45 | 46 | public function is_same_or_based_with(IType $type) { 47 | return $this === $type; 48 | } 49 | } 50 | 51 | class NoneType extends BaseType { 52 | public function is_accept_type(IType $type) { 53 | return false; 54 | } 55 | } 56 | 57 | class AnyType extends BaseType { 58 | public function is_accept_type(IType $type) { 59 | return true; 60 | } 61 | } 62 | 63 | class ScalarType extends BaseType { 64 | const ACCEPT_TYPES = [_STRING, _INT, _UINT, _BOOL, _XVIEW]; 65 | } 66 | 67 | // class BytesType extends ScalarType { 68 | // const ACCEPT_TYPES = [_STRING, _INT, _UINT, _XVIEW]; 69 | // } 70 | 71 | class StringType extends ScalarType { 72 | // PHP中Array下标可为Int和String,并且会将数字内容的String自动转为Int 73 | // 故在使用PHP Array的下标时,实际上可能有Int和String两种数据类型,这给严格的类型系统实现带来困难 74 | // 由于此处规则为可接受Int值,导致在生成PHP代码时,不能用strict_types模式 75 | // Float/Bool在作为PHP Array下标时,会自动转为Int,故为避免问题,不支持直接作为String使用,因为可能发生赋值到String/Any变量后,再作为Array下标的情况 76 | const ACCEPT_TYPES = [_INT, _UINT, _XVIEW]; 77 | } 78 | 79 | class FloatType extends ScalarType { 80 | const ACCEPT_TYPES = [_INT, _UINT]; // Int/UInt作为Float时可能会丢失精度 81 | } 82 | 83 | class IntType extends ScalarType { 84 | const ACCEPT_TYPES = [_UINT]; 85 | } 86 | 87 | class UIntType extends ScalarType { 88 | // UInt在转成PHP后实际上为Int类型 89 | } 90 | 91 | class BoolType extends ScalarType { 92 | // 93 | } 94 | 95 | class IterableType extends BaseType { 96 | /** 97 | * the key type 98 | * UInt for Array, and String/Int for Dict or classes based on Iterable 99 | * @var UIntType/IntType/StringType 100 | */ 101 | public $key_type; 102 | 103 | /** 104 | * the value type 105 | * default to Any when null 106 | * @var IType 107 | */ 108 | public $value_type; 109 | 110 | public function set_value_type(IType $type) { 111 | $this->value_type = $type; 112 | } 113 | 114 | public function is_accept_type(IType $type) { 115 | if ($type === $this || $type === TypeFactory::$_none) { 116 | return true; 117 | } 118 | 119 | // for builtin meta class 120 | if ($type instanceof ClassLikeIdentifier && $this->symbol === $type->symbol) { 121 | return true; 122 | } 123 | 124 | if (!$type instanceof static) { 125 | return false; 126 | } 127 | 128 | if ($type->value_type === null) { 129 | return false; 130 | } 131 | 132 | // if current value type is Any, then accept any types 133 | if ($this->value_type === null || $this->value_type === TypeFactory::$_any) { 134 | return true; 135 | } 136 | 137 | return $this->value_type->is_accept_type($type->value_type); 138 | } 139 | } 140 | 141 | class ArrayType extends IterableType { 142 | public $is_collect_mode; 143 | } 144 | 145 | // 当Float作为PHP Array下标时, 将会自动转为Int, 容易出问题, 需转换成String 146 | class DictType extends IterableType { 147 | // public $key_type; // just support String for Dict keys now 148 | } 149 | 150 | class CallableType extends BaseType { 151 | // 152 | } 153 | 154 | class RegexType extends BaseType { 155 | // 156 | } 157 | 158 | class XViewType extends BaseType { 159 | public function is_accept_type(IType $type) { 160 | if ($type === $this || $type === TypeFactory::$_none || $type->symbol->declaration === $this->symbol->declaration) { 161 | return true; 162 | } 163 | 164 | return $type->symbol->declaration->is_same_or_based_with_symbol(TypeFactory::$_iview_symbol); 165 | } 166 | } 167 | 168 | // document end 169 | -------------------------------------------------------------------------------- /compiler/parser/HeaderParser.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class HeaderParser extends TeaParser 13 | { 14 | const IS_IN_HEADER = true; 15 | 16 | protected function read_root_statement($leading = null, Docs $docs = null) 17 | { 18 | $token = $this->scan_token_ignore_space(); 19 | if ($token === NL) { 20 | return $this->read_root_statement(true); 21 | } 22 | elseif ($token === _SEMICOLON || $token === null) { 23 | // just an empty statement, or at the end of program 24 | return null; 25 | } 26 | 27 | $this->trace_statement($token); 28 | 29 | if ($token === _SHARP) { 30 | $node = $this->read_label_statement(); 31 | } 32 | elseif ($token === _DOCS_MARK) { 33 | $docs = $this->read_docs(); 34 | return $this->read_root_statement($leading, $docs); 35 | } 36 | elseif ($token === _INLINE_COMMENT_MARK) { 37 | $this->skip_current_line(); 38 | return $this->read_root_statement($leading, $docs); 39 | } 40 | elseif (TeaHelper::is_modifier($token)) { 41 | $node = $this->read_declaration_with_modifier($token); 42 | } 43 | else { 44 | throw $this->new_unexpect_exception(); 45 | } 46 | 47 | $this->expect_statement_end(); 48 | if ($node !== null) { 49 | $node->leading = $leading; 50 | $node->docs = $docs; 51 | } 52 | 53 | return $node; 54 | } 55 | 56 | protected function read_declaration_with_modifier(string $modifier) 57 | { 58 | $name = $this->scan_token_ignore_space(); 59 | 60 | $next = $this->get_token_ignore_space(); 61 | if ($next === _DOT) { 62 | // the name path feature, just for class 63 | // eg. Name0.Name1.KeyName 64 | 65 | $namepath = $this->read_dot_name_components($name); 66 | $name = array_pop($namepath); 67 | $next = $this->get_token_ignore_space(); 68 | } 69 | elseif (TeaHelper::is_strict_less_function_name($name)) { 70 | // function 71 | $this->assert_not_reserveds_word($name); 72 | return $this->read_function_declaration($name, $modifier); 73 | } 74 | elseif (TeaHelper::is_constant_name($name) && ($type = $this->try_read_type_identifier()) ) { 75 | // constant 76 | $this->assert_not_reserveds_word($name); 77 | return $this->factory->create_constant_declaration($modifier, $name, $type, null); 78 | } 79 | 80 | // the alias feature 81 | // just for class? 82 | if ($next === _AS) { 83 | // eg. NS1.NS2.OriginName as DestinationName 84 | 85 | if (isset($namepath)) { 86 | $namepath[] = $name; 87 | $origin_name = $namepath; 88 | } 89 | else { 90 | $origin_name = $name; 91 | } 92 | 93 | $this->scan_token_ignore_space(); // skip _AS 94 | $name = $this->scan_token_ignore_space(); 95 | 96 | $this->assert_not_reserveds_word($name); 97 | } 98 | elseif (isset($namepath)) { 99 | throw $this->new_exception("Required the 'as' keyword to alias to a new name without dots."); 100 | } 101 | 102 | if (!TeaHelper::is_classlike_name($name)) { 103 | throw $this->new_exception("Invalid class/interface name."); 104 | } 105 | 106 | // class or interface 107 | $declaration = $this->try_read_classlike_declaration($name, $modifier); 108 | if (!$declaration) { 109 | throw $this->new_unexpect_exception(); 110 | } 111 | 112 | if (isset($origin_name)) { 113 | $declaration->origin_name = $origin_name; 114 | } 115 | 116 | return $declaration; 117 | } 118 | 119 | protected function read_dot_name_components(string $first_name) 120 | { 121 | $components = [$first_name]; 122 | while ($this->skip_token(_DOT)) { 123 | $components[] = $this->expect_identifier_token(); 124 | } 125 | 126 | return $components; 127 | } 128 | 129 | protected function read_class_constant_declaration(string $name, ?string $modifier) 130 | { 131 | $type = $this->try_read_type_identifier(); 132 | 133 | if ($this->skip_token_ignore_space(_ASSIGN)) { 134 | $value = $this->read_literal_expression(); 135 | } 136 | elseif (!$type) { 137 | throw $this->new_exception('Expected type or value assign expression for define constant.'); 138 | } 139 | else { 140 | $value = null; 141 | } 142 | 143 | return $this->factory->create_class_constant_declaration($modifier, $name, $type, $value); 144 | } 145 | 146 | protected function read_method_declaration(string $name, ?string $modifier) 147 | { 148 | $parameters = $this->read_parameters_with_parentheses(); 149 | $return_type = $this->try_read_return_type_identifier(); 150 | $callbacks = $this->try_read_callback_protocols(); 151 | 152 | $declaration = $this->factory->declare_method($modifier, $name, $return_type, $parameters, $callbacks); 153 | return $declaration; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /compiler/parser/TeaStringTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | trait TeaStringTrait 13 | { 14 | protected function read_unescaped_string_literal() 15 | { 16 | return new UnescapedStringLiteral($this->read_quoted_string(_SINGLE_QUOTE)); 17 | } 18 | 19 | protected function read_escaped_string_literal() 20 | { 21 | return new EscapedStringLiteral($this->read_quoted_string(_DOUBLE_QUOTE)); 22 | } 23 | 24 | protected function read_quoted_string(string $quote_mark) 25 | { 26 | $string = ''; 27 | while (($token = $this->scan_string_component()) !== null) { 28 | if ($token === $quote_mark) { 29 | return $string; 30 | } 31 | 32 | $string .= $token; 33 | if ($token === _BACK_SLASH) { 34 | $string .= $this->scan_string_component(); // 略过转义字符 35 | } 36 | } 37 | 38 | throw $this->new_exception("Missed the close quote mark ($quote_mark)."); 39 | } 40 | 41 | protected function read_single_quoted_expression() 42 | { 43 | $items = $this->read_quoted_items(_SINGLE_QUOTE); 44 | 45 | if (empty($items)) { 46 | $expression = new UnescapedStringLiteral(_NOTHING); 47 | } 48 | elseif (!isset($items[1]) && is_string($items[0])) { 49 | $expression = new UnescapedStringLiteral($items[0]); 50 | } 51 | else { 52 | $expression = new UnescapedStringInterpolation($items); 53 | } 54 | 55 | return $expression; 56 | } 57 | 58 | protected function read_double_quoted_expression() 59 | { 60 | $items = $this->read_quoted_items(_DOUBLE_QUOTE); 61 | 62 | if (empty($items)) { 63 | $expression = new UnescapedStringLiteral(_NOTHING); 64 | } 65 | elseif (!isset($items[1]) && is_string($items[0])) { 66 | $expression = new EscapedStringLiteral($items[0]); 67 | } 68 | else { 69 | $expression = new EscapedStringInterpolation($items); 70 | } 71 | 72 | return $expression; 73 | } 74 | 75 | protected function read_quoted_items(string $quote_mark): array 76 | { 77 | $items = []; 78 | $string = ''; 79 | while (($token = $this->scan_string_component()) !== null) { 80 | if ($token === $quote_mark) { 81 | if ($string !== '') { 82 | $items[] = $string; 83 | } 84 | 85 | return $items; 86 | } 87 | elseif ($token === _DOLLAR) { 88 | if ($this->get_token() === _BLOCK_BEGIN) { 89 | $expression = $this->read_dollar_block_expression(); 90 | if (!$expression) { 91 | continue; // just a empty expression 92 | } 93 | } 94 | else { 95 | $expression = $this->try_read_identifier_expression(); 96 | if (!$expression) { 97 | $string .= $token; 98 | continue; 99 | } 100 | } 101 | 102 | static::collect_and_reset_temp($items, $string, $expression); 103 | continue; 104 | } 105 | elseif ($token === _SHARP && $this->skip_token(_BLOCK_BEGIN)) { 106 | $expression = $this->read_instring_sharp_expression(); 107 | if ($expression) { 108 | static::collect_and_reset_temp($items, $string, $expression); 109 | } 110 | continue; 111 | } 112 | elseif ($token === _BACK_SLASH) { 113 | $string .= $token . $this->scan_string_component(); // 略过转义字符 114 | continue; 115 | } 116 | 117 | $string .= $token; 118 | } 119 | 120 | throw $this->new_exception("Missed the quote close mark ($quote_mark)."); 121 | } 122 | 123 | protected function read_instring_sharp_expression() 124 | { 125 | $expression = $this->read_expression(); 126 | if (!$expression) { 127 | throw $this->new_unexpect_exception(); 128 | } 129 | 130 | $this->expect_block_end(); 131 | return new HTMLEscapeExpression($expression); 132 | } 133 | 134 | protected function try_read_identifier_expression(Identifiable $master = null): ?Identifiable 135 | { 136 | $token = $this->get_token(); 137 | if (!TeaHelper::is_identifier_name($token)) { 138 | return null; 139 | } 140 | 141 | $this->scan_token(); 142 | 143 | if ($master) { 144 | $identifer = $this->factory->create_accessing_identifier($master, $token); 145 | } 146 | else { 147 | $identifer = $this->factory->create_identifier($token); 148 | } 149 | 150 | if ($this->get_token() === _DOT) { 151 | $this->scan_token(); // skip the dot 152 | $identifer = $this->try_read_identifier_expression($identifer); 153 | } 154 | 155 | $identifer->pos = $this->pos; 156 | return $identifer; 157 | } 158 | 159 | protected function read_dollar_block_expression(): ?IExpression 160 | { 161 | $this->scan_token(); // skip { 162 | $expression = $this->read_expression(); 163 | $this->expect_block_end(); 164 | 165 | return $expression; 166 | } 167 | 168 | protected static function collect_and_reset_temp(array &$items, string &$string, IExpression $expression) 169 | { 170 | if ($string !== '') { 171 | $items[] = $string; 172 | $string = ''; // reset 173 | } 174 | 175 | $items[] = $expression; 176 | } 177 | } 178 | 179 | // end 180 | -------------------------------------------------------------------------------- /compiler/helper/Dumper.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | const MAX_CHARS_INLINE = 150; 13 | 14 | class Dumper 15 | { 16 | const INDENT = ' '; 17 | 18 | const QUOTE_ENCODES = ['\\' => '\\\\', NL => '\n', "\r" => '\r', _TAB => '\t', '"' => '\"']; 19 | 20 | private $ignore_list = []; 21 | 22 | // private $dumped_objects = []; 23 | 24 | private $max_dump_depth; 25 | private $dumped_depth = 0; 26 | 27 | private $stringfing_objects = []; 28 | 29 | function __construct(array $ignore_list = [], int $max_dump_depth = 5) 30 | { 31 | $this->ignore_list = $ignore_list; 32 | $this->max_dump_depth = $max_dump_depth; 33 | } 34 | 35 | function stringing($data, $indent = 1, $expansion_depth = 1, $max_chars_inline = MAX_CHARS_INLINE) 36 | { 37 | $this->dumped_depth++; 38 | 39 | if (is_null($data) ) { 40 | $tmp = 'null'; 41 | } 42 | elseif (is_int($data) || is_float($data) ) { 43 | $tmp = $data; 44 | } 45 | elseif (is_bool($data)) { 46 | $tmp = $data ? 'true' : 'false'; 47 | } 48 | elseif (is_string($data)) { 49 | $tmp = $this->quote_string($data); 50 | } 51 | elseif (is_array($data)) { 52 | $tmp = $this->stringing_array($data, $indent, $expansion_depth, $max_chars_inline); 53 | } 54 | elseif (is_object($data)) { 55 | $tmp = $this->stringing_object($data, $indent, $expansion_depth, $max_chars_inline); 56 | } 57 | else { 58 | throw new Exception("Unknow data type"); 59 | } 60 | 61 | $this->dumped_depth--; 62 | return $tmp; 63 | } 64 | 65 | function stringing_array(array $data, $indent_num = 1, $expansion_depth = 1, $max_chars_inline = 120) 66 | { 67 | if (empty($data) ) { 68 | return '[]'; 69 | } 70 | 71 | $is_index_array = range(0, count($data) - 1) === array_keys($data); 72 | 73 | if ($is_index_array) { 74 | $indents = $indent_num ? str_repeat(static::INDENT, $indent_num) : ''; 75 | $indents = NL . $indents; 76 | 77 | $items = []; 78 | foreach ($data as $value) { 79 | $items[] = trim($this->stringing($value, $indent_num + 1, $expansion_depth - 1) ); 80 | } 81 | 82 | $code = "[{$indents}" . static::INDENT . implode(",{$indents}" . static::INDENT, $items) . "{$indents}]"; 83 | } 84 | else { 85 | $code = $this->stringing_as_object('Dict', $data, $indent_num, $expansion_depth, $max_chars_inline); 86 | } 87 | 88 | // 换行控制 89 | if (strlen($code) < $max_chars_inline) { 90 | $code = strtr($code, [static::INDENT => '', ",\n" => ', ', NL => '']); 91 | } 92 | 93 | return $code; 94 | } 95 | 96 | function stringing_object(object $object, $indent_num = 1, $expansion_depth = 1, $max_chars_inline = MAX_CHARS_INLINE) 97 | { 98 | $name = get_class($object); 99 | if (isset($object->name)) { 100 | $name .= " {$object->name}"; 101 | } 102 | 103 | if ($this->dumped_depth > $this->max_dump_depth) { 104 | return "{$name} {...}"; 105 | } 106 | elseif (in_array($object, $this->stringfing_objects, true)) { 107 | return "{$name} {recurrence}"; 108 | } 109 | // elseif (in_array($object, $this->dumped_objects, true)) { 110 | // return "{$name} {...} [dumped]"; 111 | // } 112 | // else { 113 | // $this->dumped_objects[] = $object; 114 | // } 115 | 116 | $this->stringfing_objects[] = $object; 117 | $tmp = $this->stringing_as_object($name, $object, $indent_num, $expansion_depth, $max_chars_inline); 118 | array_pop($this->stringfing_objects); 119 | 120 | return $tmp; 121 | } 122 | 123 | function stringing_as_object(string $name, $object, $indent_num = 1, $expansion_depth = 1, $max_chars_inline = MAX_CHARS_INLINE) 124 | { 125 | $indents = $indent_num ? str_repeat(static::INDENT, $indent_num) : ''; 126 | $indents = NL . $indents; 127 | 128 | $items = []; 129 | foreach ($object as $key => $value) { 130 | // if (empty($value) && $value !== 0) continue; 131 | 132 | if (is_object($value) && in_array($key, $this->ignore_list, true)) { 133 | $contents = get_class($value); 134 | if (isset($value->name)) { 135 | $contents .= " {$value->name}"; 136 | } 137 | 138 | $contents .= ' [ignored]'; 139 | } 140 | else { 141 | $contents = trim($this->stringing($value, $indent_num + 1, $expansion_depth - 1) ); 142 | } 143 | 144 | $items[] = sprintf("%s: %s", $this->quote_key($key), $contents); 145 | } 146 | 147 | $code = "$name {{$indents}" . static::INDENT . implode(",{$indents}" . static::INDENT, $items) . "{$indents}}"; 148 | 149 | // 换行控制 150 | if (strlen($code) < $max_chars_inline) { 151 | $code = strtr($code, [static::INDENT => '', ",\n" => ', ', NL => '']); 152 | } 153 | 154 | return $code; 155 | } 156 | 157 | function quote_key(?string $str) 158 | { 159 | if ($str === '') { 160 | return '[empty]'; 161 | } 162 | 163 | return strtr($str, self::QUOTE_ENCODES); 164 | } 165 | 166 | function quote_string(?string $str) 167 | { 168 | if ($str === '') { 169 | return '""'; 170 | } 171 | 172 | return '"' . strtr($str, self::QUOTE_ENCODES) . '"'; 173 | } 174 | } 175 | 176 | -------------------------------------------------------------------------------- /compiler/parser/TeaHelper.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | TeaHelper::$NORMAL_RESERVEDS = array_merge( 13 | TypeFactory::META_TYPES, 14 | TeaHelper::STRUCTURE_KEYS, 15 | TeaHelper::MODIFIERS, 16 | TeaHelper::PREDEFINED_CONSTS, 17 | TeaHelper::OTHER_RESERVEDS 18 | ); 19 | 20 | // TeaHelper::$CLASS_MEMBER_RESERVEDS = array_merge(TeaHelper::MODIFIERS, [_STATIC]); 21 | 22 | class TeaHelper 23 | { 24 | const PATTERN_MODIFIER_REGEX = '/[imug]+/'; 25 | 26 | const STRUCTURE_KEYS = [ 27 | _VAR, 28 | _IF, _CASE, _FOR, _WHILE, _TRY, // _LOOP 29 | _ECHO, _PRINT, _RETURN, _EXIT, _BREAK, _CONTINUE, _THROW, 30 | ]; 31 | 32 | const MODIFIERS = [ 33 | _MASKED, 34 | _PUBLIC, 35 | _INTERNAL, 36 | _PROTECTED, 37 | _PRIVATE 38 | ]; 39 | 40 | const PREDEFINED_CONSTS = [_VAL_NONE, _VAL_TRUE, _VAL_FALSE, 'UNIT_PATH']; 41 | 42 | const OTHER_RESERVEDS = [ 43 | _THIS, _SUPER, _CONSTRUCT, _DESTRUCT, _STATIC, 44 | _ELSEIF, _ELSE, _CATCH, _FINALLY, 45 | // 'class', 'interface', 'abstract', 'trait', 'enum', 'new', 'async', 'await' 46 | ]; 47 | 48 | const ASSIGN_OPERATORS = [_ASSIGN, '.=', '**=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=']; // '??=' 49 | 50 | static $NORMAL_RESERVEDS; 51 | // static $CLASS_MEMBER_RESERVEDS; 52 | 53 | /** 54 | * Check token is a number 55 | * and return the number base type when is a number format 56 | * 57 | * @return string the number base type ('', '0x', '0b', '0') 58 | */ 59 | static function check_number($token) 60 | { 61 | if ($token === '0' || preg_match('/^[1-9][0-9_]*(e[0-9]*)?$/', $token)) { 62 | return _BASE_DECIMAL; 63 | } 64 | 65 | if ($token[0] === '0') { 66 | if ($token[1] === 'x') { 67 | return preg_match('/^0x[0-9a-f][0-9a-f_]*$/i', $token) ? _BASE_HEX : null; 68 | } 69 | elseif ($token[1] === 'b') { 70 | return preg_match('/^0b[01][01_]*$/', $token) ? _BASE_OCTAL : null; 71 | } 72 | else { 73 | return preg_match('/^0[0-7][0-7_]*$/', $token) ? _BASE_OCTAL : null; 74 | } 75 | } 76 | 77 | return null; 78 | } 79 | 80 | static function is_uint_number($token) 81 | { 82 | return preg_match('/^[1-9][0-9]*$/', $token) && $token <= PHP_INT_MAX; 83 | } 84 | 85 | static function is_space_tab($token) 86 | { 87 | return $token === _SPACE || $token === _TAB; 88 | } 89 | 90 | static function is_space_tab_nl($token) 91 | { 92 | return $token === _SPACE || $token === _TAB || $token === NL || $token === _CR; 93 | } 94 | 95 | static function is_normal_reserveds($token) 96 | { 97 | return in_array(strtolower($token), self::$NORMAL_RESERVEDS, true); 98 | } 99 | 100 | static function is_modifier($token) 101 | { 102 | return in_array($token, self::MODIFIERS, true); 103 | } 104 | 105 | static function is_structure_key($token) 106 | { 107 | return in_array($token, self::STRUCTURE_KEYS, true); 108 | } 109 | 110 | static function is_builtin_constant($token) 111 | { 112 | return in_array($token, self::PREDEFINED_CONSTS, true); 113 | } 114 | 115 | static function is_xtag_name($token) 116 | { 117 | return preg_match('/^[a-z\-\!][a-z0-9\-:]*$/i', $token); 118 | } 119 | 120 | static function is_identifier_name(?string $token) 121 | { 122 | return preg_match('/^_*[a-z][a-z0-9_]*$/i', $token); 123 | } 124 | 125 | static function is_declarable_variable_name(?string $token) 126 | { 127 | return preg_match('/^_?[a-z][a-z0-9_]*$/', $token) && !TeaHelper::is_normal_reserveds($token); 128 | } 129 | 130 | static function is_constant_name(?string $token) 131 | { 132 | return preg_match('/^_*[A-Z][A-Z0-9_]+$/', $token); 133 | } 134 | 135 | static function is_function_name(?string $token) 136 | { 137 | return preg_match('/^_?[a-z][a-z0-9_]*$/', $token); 138 | } 139 | 140 | static function is_strict_less_function_name(?string $token) 141 | { 142 | return preg_match('/^[_a-z]+[a-zA-Z0-9_]*$/', $token); 143 | } 144 | 145 | static function is_classlike_name(?string $token) 146 | { 147 | return preg_match('/^[A-Z][a-zA-Z0-9_]*$/', $token); 148 | } 149 | 150 | static function is_interface_marked_name(string $name) 151 | { 152 | return ($name[0] === 'I' && preg_match('/^I[A-Z]/', $name)) || substr($name, -9) === 'Interface'; 153 | } 154 | 155 | static function is_type_name(?string $token) 156 | { 157 | return self::is_meta_type_name($token) || self::is_classlike_name($token); 158 | } 159 | 160 | static function is_meta_type_name(?string $token) 161 | { 162 | return in_array($token, TypeFactory::META_TYPES, true); 163 | } 164 | 165 | static function is_domain_component(?string $token) 166 | { 167 | return preg_match('/^[a-z][a-z0-9\-]*[a-z0-9]$/i', $token); 168 | } 169 | 170 | static function is_subnamespace_name(?string $token) 171 | { 172 | return preg_match('/^[a-z][a-z0-9]+$/i', $token); 173 | } 174 | 175 | static function is_assign_operator(?string $token) 176 | { 177 | return in_array($token, self::ASSIGN_OPERATORS, true); 178 | } 179 | 180 | static function is_regex_flags($token) 181 | { 182 | return preg_match(self::PATTERN_MODIFIER_REGEX, $token); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /compiler/ast/TypeFactory.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class TypeFactory 13 | { 14 | const META_TYPES = [ 15 | _ANY, _VOID, _NONE, // _NONE just a internal type 16 | _STRING, _INT, _UINT, _FLOAT, _BOOL, // maybe support Bytes? 17 | _ITERABLE, _DICT, _ARRAY, // maybe support Matrix, Tensor? 18 | _OBJECT, _XVIEW, _REGEX, 19 | _CALLABLE, _CLASS, _NAMESPACE, 20 | ]; 21 | 22 | static $_void; 23 | 24 | static $_any; 25 | static $_none; 26 | 27 | static $_scalar; 28 | static $_string; 29 | static $_float; 30 | static $_int; 31 | static $_uint; 32 | static $_bool; 33 | 34 | static $_iterable; 35 | static $_array; 36 | static $_dict; 37 | 38 | static $_object; 39 | static $_xview; 40 | static $_regex; 41 | 42 | static $_callable; 43 | static $_class; 44 | static $_namespace; 45 | 46 | // for check XView accepts 47 | static $_iview_symbol; 48 | 49 | // for check Iterable type accepts 50 | static $_iterator_interface_symbol; 51 | 52 | private static $type_map = []; 53 | 54 | static function init() 55 | { 56 | // init all meta types 57 | 58 | self::$_void = self::create_type(_VOID); 59 | self::$_none = self::create_type(_NONE, NoneType::class); 60 | self::$_any = self::create_type(_ANY, AnyType::class); 61 | 62 | self::$_scalar = self::create_type(_SCALAR, ScalarType::class); 63 | self::$_string = self::create_type(_STRING, StringType::class); 64 | self::$_float = self::create_type(_FLOAT, FloatType::class); 65 | self::$_int = self::create_type(_INT, IntType::class); 66 | self::$_uint = self::create_type(_UINT, UIntType::class); 67 | self::$_bool = self::create_type(_BOOL, BoolType::class); 68 | 69 | self::$_iterable = self::create_type(_ITERABLE, IterableType::class); 70 | self::$_array = self::create_type(_ARRAY, ArrayType::class); 71 | self::$_dict = self::create_type(_DICT, DictType::class); 72 | 73 | self::$_xview = self::create_type(_XVIEW, XViewType::class); 74 | self::$_regex = self::create_type(_REGEX, RegexType::class); 75 | 76 | self::$_callable = self::create_type(_CALLABLE, CallableType::class); 77 | 78 | self::$_object = self::create_type(_OBJECT); 79 | self::$_class = self::create_type(_CLASS); 80 | self::$_namespace = self::create_type(_NAMESPACE); 81 | } 82 | 83 | static function is_iterable_type(IType $type) { 84 | if ($type === TypeFactory::$_any || $type instanceof IterableType) { 85 | return true; 86 | } 87 | 88 | return $type->symbol->declaration->is_same_or_based_with_symbol(self::$_iterator_interface_symbol); 89 | } 90 | 91 | static function is_dict_key_directly_supported_type(?IType $type) 92 | { 93 | return $type === self::$_string 94 | || $type === self::$_uint 95 | || $type === self::$_int; 96 | } 97 | 98 | static function is_dict_key_castable_type(?IType $type) 99 | { 100 | // Data type convert is a problem 101 | // false to string in javascript is 'false', and in python is 'False', in PHP is '' ... 102 | 103 | return $type === self::$_int 104 | || $type === self::$_uint 105 | || $type === self::$_float 106 | || $type === self::$_bool 107 | || $type === self::$_any; 108 | } 109 | 110 | static function is_case_testable_type(?IType $type) 111 | { 112 | return $type === self::$_int 113 | || $type === self::$_uint 114 | || $type === self::$_string; 115 | } 116 | 117 | static function is_number_type(?IType $type) 118 | { 119 | return $type === self::$_int 120 | || $type === self::$_uint 121 | || $type === self::$_float; 122 | } 123 | 124 | static function set_symbols(Unit $unit) 125 | { 126 | foreach (static::$type_map as $type_name => $object) { 127 | if (isset($unit->symbols[$type_name])) { 128 | $object->symbol = $unit->symbols[$type_name]; 129 | } 130 | } 131 | 132 | static::$_iview_symbol = $unit->symbols['IView']; 133 | static::$_iterator_interface_symbol = $unit->symbols['IteratorInterface']; 134 | } 135 | 136 | static function exists_type(string $name): bool 137 | { 138 | return isset(static::$type_map[$name]); 139 | } 140 | 141 | static function get_type(string $name) 142 | { 143 | return static::$type_map[$name] ?? null; 144 | } 145 | 146 | static function create_type(string $name, string $class = BaseType::class) 147 | { 148 | $type_object = new $class($name); 149 | static::$type_map[$name] = $type_object; 150 | 151 | return $type_object; 152 | } 153 | 154 | static function create_dict_type(IType $value_type) 155 | { 156 | $type = new DictType(_DICT); 157 | $type->value_type = $value_type; 158 | $type->symbol = static::$_dict->symbol; 159 | 160 | return $type; 161 | } 162 | 163 | static function create_array_type(IType $value_type) 164 | { 165 | $type = new ArrayType(_ARRAY); 166 | $type->value_type = $value_type; 167 | $type->symbol = static::$_array->symbol; 168 | 169 | return $type; 170 | } 171 | 172 | static function create_collector_type(IType $value_type) 173 | { 174 | $type = new ArrayType(_ARRAY); 175 | $type->value_type = $value_type; 176 | $type->symbol = static::$_array->symbol; 177 | $type->is_collect_mode = true; 178 | 179 | return $type; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /dist/docs/summary.php: -------------------------------------------------------------------------------- 1 | message; 39 | } 40 | } 41 | 42 | #internal 43 | class DemoBaseClass 44 | { 45 | public function __construct(string $name) 46 | { 47 | echo "Hey, {$name}, it is constructing...", NL; 48 | } 49 | 50 | public function __destruct() 51 | { 52 | echo "it is destructing...", NL; 53 | } 54 | 55 | protected function a_protected_method() 56 | { 57 | // no any 58 | } 59 | } 60 | 61 | #public 62 | class DemoPublicClass extends DemoBaseClass implements IDemo, DemoInterface 63 | { 64 | use IDemoTrait, DemoInterfaceTrait; 65 | 66 | public function set_message(string $message) 67 | { 68 | $this->message = $message; 69 | } 70 | } 71 | 72 | // --------- 73 | echo "世界你好!", NL; 74 | 75 | echo 'Hey,', 'Who are you?', NL; 76 | 77 | echo 'string1', 'string2', NL; 78 | 79 | $any = null; 80 | $any = 1; 81 | $any = []; 82 | $any = 'abc'; 83 | 84 | $str_from_any = (string)$any; 85 | 86 | $str = 'Unescaped string\n'; 87 | $str = "Escaped string\n"; 88 | $str_with_interpolation = 'Unescaped string with interpolation ' . (5 * 6); 89 | $str_with_interpolation = "Escaped string with interpolation " . (5 * 6) . "\n"; 90 | 91 | $xss = ''; 92 | $html_escaped_interpolation = "The html-escaped string: " . htmlspecialchars($xss, ENT_QUOTES); 93 | $text_labeled = "would not process interpolation \${5 * 6}"; 94 | 95 | iconv_strlen($str); 96 | iconv_substr($str, 0, 3); 97 | 98 | $uint_num = 123; 99 | $int_num = -123; 100 | $float_num = 123.01; 101 | $bool = true; 102 | 103 | $xview = '
    104 |

    XView是什么?

    105 |

    XView类似字符串,但无需引号,可以直接按HTML标签方式编写

    106 |

    Interpolation with origin ' . ($uint_num * 10) . '

    107 |

    Interpolation with html-escaped ' . htmlspecialchars($xss, ENT_QUOTES) . '

    108 |
    '; 109 | 110 | $regex = '/^[a-z0-9\'_"]+$/i'; 111 | if (preg_match($regex, 'Abc\'123"')) { 112 | echo 'matched!', NL; 113 | } 114 | 115 | $any_array = [123, 'Hi', false, [1, 2, 3]]; 116 | 117 | $int_array = []; 118 | $int_array = [-1, 10, 200]; 119 | count($int_array); 120 | array_slice($int_array, 0, 2); 121 | 122 | $str_dict = []; 123 | $str_dict = [ 124 | 'k1' => 'value for string key "k1"', 125 | 123 => 'value for int key "123"' 126 | ]; 127 | 128 | $str_dict_array = [ 129 | ['k0' => 'v0', 'k1' => 'v01'], 130 | $str_dict 131 | ]; 132 | 133 | $str1 = 'Hi!'; 134 | 135 | $str2 = null; 136 | 137 | $var_without_decared = 123; 138 | 139 | $pow_result = ((-2) ** 3) ** 5; 140 | 141 | $string_concat = 'abc' . (1 + (8 & 2) * 3); 142 | $array_concat = array_merge(['A', 'B'], ['A1', 'C1']); 143 | 144 | $array_merge = array_replace(['A', 'B'], ['A1', 'B1']); 145 | $dict_merge = array_replace(['a' => 'A', 'b' => 'B'], ['a' => 'A1', 'c' => 'C1']); 146 | 147 | $int_from_string = (int)'123'; 148 | $uint_from_string = abs('-123'); 149 | $str_from_uint = (string)123; 150 | $str_from_int = (string)-123; 151 | $str_from_float = (string)123.123; 152 | 153 | $ex1 = null; 154 | $ex2 = $ex1; 155 | 156 | is_int(1.1); 157 | is_int(1); 158 | is_uint(2); 159 | new ErrorException('Some') instanceof Exception; 160 | 161 | $not_result = !($uint_num > 3); 162 | 163 | $ternary_result = $uint_num == 1 ? 'one' : ($uint_num == 2 ? 'two' : ($uint_num == 3 ? 'three' : 'other')); 164 | 165 | $a = 0; 166 | $b = 1; 167 | 168 | try { 169 | if ($a) { 170 | // no any 171 | } 172 | elseif ($b) { 173 | // no any 174 | } 175 | else { 176 | // no any 177 | } 178 | } 179 | catch (\ErrorException $ex) { 180 | // no any 181 | } 182 | catch (\Exception $ex) { 183 | // no any 184 | } 185 | finally { 186 | // no any 187 | } 188 | 189 | if ($str_dict && count($str_dict) > 0) { 190 | foreach ($str_dict as $k => $v) { 191 | // no any 192 | } 193 | } 194 | else { 195 | echo 'dict is empty', NL; 196 | } 197 | 198 | if (0 <= 9) { 199 | for ($i = 0; $i <= 9; $i += 1) { 200 | // no any 201 | } 202 | } 203 | else { 204 | // no any 205 | } 206 | 207 | for ($i = 9; $i >= 0; $i -= 2) { 208 | // no any 209 | } 210 | 211 | $i = 0; 212 | try { 213 | while (1) { 214 | while (true) { 215 | $i = $i + 1; 216 | if ($i > 10) { 217 | break 2; 218 | } 219 | else { 220 | continue 1; 221 | } 222 | } 223 | } 224 | } 225 | catch (\Exception $ex) { 226 | // no any 227 | } 228 | 229 | $ret1 = demo_function_with_a_return_type('some data'); 230 | 231 | $ret2 = demo_function_with_callbacks('some data', function ($message) { 232 | echo $message, NL; 233 | return 'some return data'; 234 | }, function ($error) { 235 | echo $error, NL; 236 | }); 237 | 238 | $object = new DemoPublicClass('Benny'); 239 | 240 | $object->set_message('some string'); 241 | 242 | $object::say_hello_with_static(); 243 | DemoPublicClass::say_hello_with_static(); 244 | // --------- 245 | 246 | // program end 247 | -------------------------------------------------------------------------------- /dist/builtin/__public.th: -------------------------------------------------------------------------------- 1 | // the public declarations 2 | 3 | #unit tea/builtin 4 | 5 | public NL String = "\n" 6 | public __DIR__ String 7 | public __LINE__ UInt 8 | public JSON_UNESCAPED_UNICODE Int 9 | 10 | #php Exception { 11 | public construct(message String, code Int = 0) 12 | public getCode() Int 13 | public getMessage() String 14 | } 15 | 16 | #php ErrorException: Exception { 17 | // no any 18 | } 19 | 20 | #php strval(val Any) String 21 | 22 | #php intval(val Any) Int 23 | 24 | #php floatval(val Any) Float 25 | 26 | #php boolval(val Any) Bool 27 | 28 | #php abs(val Any) Float 29 | 30 | #php round(val Any, precision UInt = 0) Float 31 | 32 | #php ceil(val Any) Float 33 | 34 | #php floor(val Any) Float 35 | 36 | #php is_int(val Any) Bool 37 | 38 | public is_uint(val Any) Bool 39 | 40 | #php trim(str Any, char_mask String = #default) String 41 | 42 | #php rtrim(str Any, char_mask String = #default) String 43 | 44 | #php ltrim(str Any, char_mask String = #default) String 45 | 46 | #php iconv_strlen(str String) UInt 47 | 48 | #php iconv_substr(str String, start Int, length UInt = #default) String 49 | 50 | #php iconv_strpos(str String, search String, offset Int = 0) Int 51 | 52 | public _iconv_strpos(str String, search String, offset Int = 0) Int 53 | 54 | #php iconv_strrpos(str String, search String, offset Int = 0) Int 55 | 56 | public _iconv_strrpos(str String, search String, offset Int = 0) Int 57 | 58 | #php mb_strtoupper(str String) String 59 | 60 | #php mb_strtolower(str String) String 61 | 62 | #php strlen(str String) UInt 63 | 64 | #php substr(str String, start Int, length UInt = #default) String 65 | 66 | #php strpos(master String, search String, offset Int = 0) Int 67 | 68 | public _strpos(master String, search String, offset Int = 0) Int 69 | 70 | #php strrpos(master String, search String, offset Int = 0) Int 71 | 72 | public _strrpos(master String, search String, offset Int = 0) Int 73 | 74 | #php strtr(master String, changes Dict) String 75 | 76 | #php str_replace(search Any, replacement Any, master String) String 77 | 78 | public _str_replace(master String, search String, replacement String) String 79 | 80 | #php implode(pieces Any, delimiter String = #default) String 81 | 82 | #php explode(delimiter String, master String) Array 83 | 84 | #php count(array Any) UInt 85 | 86 | #php in_array(needle Any, haystack Any, strict Bool = false) Bool 87 | 88 | #php array_reverse(array Any) 89 | 90 | #php array_slice(master Array, offset Int, length UInt = none) Array 91 | 92 | #php array_unshift(master Array, item Any) UInt 93 | 94 | #php array_shift(master Array) Any 95 | 96 | #php array_push(master Array, item Any) UInt 97 | 98 | #php array_pop(master Array) Any 99 | 100 | #php array_search(search Any, master Any) Int 101 | 102 | public _array_search(master Any, search Any) Int 103 | 104 | #php array_key_exists(key Any, arr Dict) Bool 105 | 106 | #php unset(target Any) 107 | 108 | #php array_keys(array Dict) Array 109 | 110 | #php array_values(array Dict) Array 111 | 112 | #php json_encode(data Any, flags Int = 0, depth UInt = 512) String 113 | 114 | #php json_decode(data Any, assoc Bool = false, depth UInt = 512, flags Int = 0) Dict 115 | 116 | #php date(format String, timestamp Int = #default) String 117 | 118 | #php preg_match(regex Regex, subject String, matches Array = #default, flags Int = #default, offset Int = #default) Bool 119 | 120 | #php preg_match_all(regex Regex, subject String, matches Array = #default, flags Int = #default, offset Int = #default) Bool 121 | 122 | #tea Any { 123 | // no any 124 | } 125 | 126 | #tea None { 127 | // no any 128 | } 129 | 130 | #tea Scalar { 131 | masked string String 132 | masked float Float 133 | masked int Int 134 | masked bool Bool 135 | } 136 | 137 | #tea String: Scalar { 138 | masked byte_length UInt 139 | masked length UInt 140 | masked find(str String, offset Int = 0) Int 141 | masked find_last(search String, offset Int = 0) Int 142 | masked slice(start Int, length UInt = #default) String 143 | masked lower() String 144 | masked upper() String 145 | masked explode(delimiter String) Array 146 | masked replace(search String, replacement String) String 147 | masked json_decode() Dict 148 | } 149 | 150 | #tea Float: Scalar { 151 | masked abs() Float 152 | masked ceil() Float 153 | masked floor() Float 154 | masked round(precision UInt = 0) Float 155 | } 156 | 157 | #tea Int: Scalar { 158 | masked abs() UInt 159 | } 160 | 161 | #tea UInt: Int { 162 | // no any 163 | } 164 | 165 | #tea Bool: Scalar { 166 | masked int UInt 167 | } 168 | 169 | #tea Dict { 170 | masked length UInt 171 | masked keys Array 172 | masked values Array 173 | masked has_key(key Any) Bool 174 | masked has_value(val Any) Bool 175 | masked find(val Any) Int 176 | masked join(delimiter String = NL) String 177 | masked json_encode(flags Int = JSON_UNESCAPED_UNICODE) String 178 | } 179 | 180 | #tea Array { 181 | masked length UInt 182 | masked has_value(val Any) Bool 183 | masked find(val Any) Int 184 | masked unshift(item Any) UInt 185 | masked shift() Any 186 | masked push(item Any) UInt 187 | masked pop() Any 188 | masked slice(start Int, length UInt = #default) Array 189 | masked reverse() Array 190 | masked join(delimiter String = NL) String 191 | masked json_encode(flags Int = JSON_UNESCAPED_UNICODE) String 192 | } 193 | 194 | #tea Regex { 195 | // no any 196 | } 197 | 198 | #tea XView { 199 | // no any 200 | } 201 | 202 | #tea Iterable { 203 | // no any 204 | } 205 | 206 | #tea Callable { 207 | // no any 208 | } 209 | 210 | public IView { 211 | // no any 212 | } 213 | 214 | #php IteratorInterface { 215 | // no any 216 | } 217 | 218 | // program end 219 | -------------------------------------------------------------------------------- /dist/tests/syntax/class.php: -------------------------------------------------------------------------------- 1 | prop = $some; 22 | } 23 | 24 | public function get_class_name(string $caller = 'caller1'): string 25 | { 26 | return __CLASS__; 27 | } 28 | } 29 | 30 | #internal 31 | class BaseClass 32 | { 33 | public function set_value(int $num) 34 | { 35 | // no any 36 | } 37 | } 38 | 39 | #internal 40 | class Test1 extends BaseClass implements IDemo 41 | { 42 | use IDemoTrait; 43 | 44 | public function __construct(string $some) 45 | { 46 | // no any 47 | } 48 | 49 | public static function static_method() 50 | { 51 | echo Test1::CONST1, NL; 52 | } 53 | } 54 | 55 | #internal 56 | class Test2 57 | { 58 | public $t1; 59 | 60 | public function set_t1(BaseInterface $t1) 61 | { 62 | $this->t1 = $t1; 63 | } 64 | 65 | public function call_t1_fn(): string 66 | { 67 | return $this->t1->get_class_name('class.tea'); 68 | } 69 | } 70 | 71 | #internal 72 | interface ITest 73 | { 74 | public function f1(): string; 75 | } 76 | 77 | #internal 78 | class Test3 implements ITest 79 | { 80 | public function f1(): string 81 | { 82 | return "hi"; 83 | } 84 | } 85 | 86 | #internal 87 | class Test4 88 | { 89 | public static function fx(ITest $it) 90 | { 91 | echo $it->f1(), NL; 92 | } 93 | } 94 | 95 | #internal 96 | class Test5 97 | { 98 | const C_1 = '123'; 99 | 100 | public $a_10 = []; 101 | public $a_11 = ["f_1" => 123, "f_2" => "string"]; 102 | public $a_20; 103 | public $a_22 = 123; 104 | public $a_30; 105 | public $a_32 = "hi"; 106 | public $a_40; 107 | public $a_41 = true; 108 | public $a_42 = false; 109 | public $a_50; 110 | public $a_51 = []; 111 | public $a_52 = [1, 2, "str"]; 112 | public $a_53 = [1, 2, [4, 5, 6]]; 113 | 114 | public static $sa_10 = []; 115 | public static $sa_11 = ["f_1" => 123, "f_2" => "string"]; 116 | public static $sa_20; 117 | public static $sa_22 = 123; 118 | public static $sa_30; 119 | public static $sa_32 = "hi"; 120 | public static $sa_41 = true; 121 | public static $sa_51 = []; 122 | public static $sa_53 = [1, 2, [4, 5, 6]]; 123 | 124 | public static function sm3() 125 | { 126 | // no any 127 | } 128 | 129 | public static function sm_30($arg): string 130 | { 131 | return ''; 132 | } 133 | 134 | public static function sm_31($arg_1, $arg_2): string 135 | { 136 | $str = "sth."; 137 | 138 | return $str; 139 | } 140 | 141 | public static function sm_33(array $arg = []): int 142 | { 143 | return 1; 144 | } 145 | 146 | public static function sm_34(array $arg = [1, 2, 3]): float 147 | { 148 | return 1.23; 149 | } 150 | 151 | public static function sm_35(array $arg_1, $arg_2): bool 152 | { 153 | return true; 154 | } 155 | 156 | public static function sm_360(array $arg_1, string $arg_2 = "str"): array 157 | { 158 | return []; 159 | } 160 | 161 | public static function sm_361(array $arg_1, string $arg_2 = "str"): array 162 | { 163 | return [1, 2, 3]; 164 | } 165 | 166 | public static function sm_362(array $arg_1, string $arg_2 = "str"): array 167 | { 168 | return $arg_1; 169 | } 170 | 171 | public static function sm_370(array $arg_1, string $arg_2 = "str"): array 172 | { 173 | return []; 174 | } 175 | 176 | public static function sm_371(array $arg_1, string $arg_2 = "str"): array 177 | { 178 | return $arg_1; 179 | } 180 | 181 | public static function sm_372(array $arg_1, string $arg_2 = "str"): array 182 | { 183 | return ["a" => 123]; 184 | } 185 | 186 | public function m3() 187 | { 188 | // no any 189 | } 190 | 191 | public function m_30($arg): string 192 | { 193 | return ''; 194 | } 195 | 196 | public function m_31($arg_1, $arg_2): string 197 | { 198 | $str = "sth."; 199 | 200 | return $str; 201 | } 202 | 203 | public function m_33(array $arg = []): int 204 | { 205 | return 1; 206 | } 207 | 208 | public function m_34(array $arg = [1, 2, 3]): float 209 | { 210 | return 1.23; 211 | } 212 | 213 | public function m_35(array $arg_1, $arg_2): bool 214 | { 215 | return true; 216 | } 217 | 218 | public function m_360(array $arg_1, string $arg_2 = "str"): array 219 | { 220 | return []; 221 | } 222 | 223 | public function m_361(array $arg_1, string $arg_2 = "str"): array 224 | { 225 | return [1, 2, 3]; 226 | } 227 | 228 | public function m_362(array $arg_1, string $arg_2 = "str"): array 229 | { 230 | return $arg_1; 231 | } 232 | 233 | public function m_363(array $arg_1, string $arg_2 = "str"): array 234 | { 235 | return $this->a_53; 236 | } 237 | 238 | public function m_370(array $arg_1, string $arg_2 = "str"): array 239 | { 240 | return []; 241 | } 242 | 243 | public function m_371(array $arg_1, string $arg_2 = "str"): array 244 | { 245 | return $arg_1; 246 | } 247 | 248 | public function m_372(array $arg_1, string $arg_2 = "str"): array 249 | { 250 | return ["a" => 123]; 251 | } 252 | 253 | public function m_373(array $arg_1, string $arg_2 = "str"): array 254 | { 255 | return $this->a_10; 256 | } 257 | } 258 | 259 | // --------- 260 | $t1 = new Test1('some'); 261 | echo $t1->get_class_name('Unknow'), NL; 262 | 263 | $t2 = new Test2(); 264 | $t2->set_t1($t1); 265 | echo $t2->call_t1_fn(), NL; 266 | 267 | $a = null; 268 | $a = new Test3(); 269 | 270 | Test4::fx(new Test3()); 271 | // --------- 272 | 273 | // program end 274 | -------------------------------------------------------------------------------- /compiler/ast/ReturnBuilder.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | class ReturnBuilder 13 | { 14 | private $node; 15 | 16 | private $collect_type; 17 | 18 | private $vector_identifier; 19 | 20 | private $collecteds = 0; 21 | 22 | private $temp_var_idx = 0; 23 | 24 | public function __construct(IEnclosingBlock $node, IType $collect_type) 25 | { 26 | $this->node = $node; 27 | $this->collect_type = $collect_type; 28 | } 29 | 30 | public function build_return_statements() 31 | { 32 | $array_var_declaration = new VariableDeclaration('__collects', TypeFactory::$_array, new ArrayLiteral([])); 33 | $this->vector_identifier = ASTHelper::create_variable_identifier($array_var_declaration); 34 | 35 | // the fixed statements 36 | $fixeds = $this->collect_block($this->node); 37 | 38 | if (!$this->collecteds) { 39 | throw new SyntaxError("Expect to collect the type '{$this->collect_type->name}' items, but not found anything."); 40 | } 41 | 42 | // the var declaration 43 | array_unshift($fixeds, $array_var_declaration); 44 | 45 | // the return statement 46 | $return = new ReturnStatement($this->vector_identifier); 47 | $return->leading = NL; 48 | $fixeds[] = $return; 49 | 50 | return $fixeds; 51 | } 52 | 53 | private function collect_block(BaseBlock $node) 54 | { 55 | $fixeds = []; 56 | foreach ($node->body as $subnode) { 57 | if ($subnode instanceof NormalStatement) { 58 | $expr = $subnode->expression; 59 | 60 | // eg. View().color('red') 61 | if ($expr instanceof CallExpression) { 62 | $this->collect_call_statement($subnode, $fixeds); 63 | continue; 64 | } 65 | 66 | // just for XView 67 | if ($this->collect_type === TypeFactory::$_xview && $expr instanceof XBlock) { 68 | $this->collecteds++; 69 | $subnode = new ArrayElementAssignment($this->vector_identifier, null, $expr); 70 | } 71 | } 72 | elseif ($subnode instanceof IAssignment) { 73 | // eg. View().text = 'abc' 74 | if ($subnode->master instanceof AccessingIdentifier) { 75 | $this->collect_accessing_master_assignment($subnode, $fixeds); 76 | continue; 77 | } 78 | } 79 | elseif ($subnode instanceof BaseBlock && !$subnode instanceof IEnclosingBlock) { 80 | // eg. for-in, if-elseif-else, try-catch-finally, ... 81 | $to_fix_block = clone $subnode; 82 | $to_fix_block->body = $this->collect_block($to_fix_block); 83 | $subnode = $to_fix_block; 84 | } 85 | 86 | $fixeds[] = $subnode; 87 | } 88 | 89 | return $fixeds; 90 | } 91 | 92 | private function collect_call_statement(NormalStatement $stmt, array &$fixeds) 93 | { 94 | $expr = $stmt->expression; 95 | if ($expr->callee instanceof PlainIdentifier) { 96 | if ($expr->is_class_new() && $expr->callee->is_same_or_based_with($this->collect_type)) { 97 | $this->collecteds++; 98 | $fixeds[] = new ArrayElementAssignment($this->vector_identifier, null, $expr); 99 | return; 100 | } 101 | } 102 | elseif ($hit = $this->find_and_collect_for_call($expr, $fixeds)) { 103 | $this->collecteds++; 104 | $new_stmt = clone $stmt; 105 | $new_stmt->expression = $hit; 106 | 107 | $fixeds_count = count($fixeds); 108 | if ($new_stmt->leading) { 109 | // just to let code prety 110 | $fixeds[$fixeds_count - 2]->leading = NL; 111 | $new_stmt->leading = false; 112 | } 113 | 114 | // we let code prety 115 | $last_item = $fixeds[$fixeds_count - 1]; 116 | $fixeds[$fixeds_count - 1] = $new_stmt; 117 | $fixeds[] = $last_item; 118 | return; 119 | } 120 | 121 | $fixeds[] = $stmt; // do not collected anything 122 | } 123 | 124 | private function collect_accessing_master_assignment(IAssignment $assignment, array &$fixeds) 125 | { 126 | $hit = $this->find_and_collect_for_accessing($assignment->master, $fixeds); 127 | if ($hit) { 128 | $this->collecteds++; 129 | $new_assignment = clone $assignment; 130 | $new_assignment->master = $hit; 131 | 132 | $fixeds_count = count($fixeds); 133 | if ($new_assignment->leading) { 134 | // just to let code prety 135 | $fixeds[$fixeds_count - 2]->leading = NL; 136 | $new_assignment->leading = false; 137 | } 138 | 139 | // we let code prety 140 | $last_item = $fixeds[$fixeds_count - 1]; 141 | $fixeds[$fixeds_count - 1] = $new_assignment; 142 | $fixeds[] = $last_item; 143 | } 144 | else { 145 | $fixeds[] = $assignment; // do not collected anything 146 | } 147 | } 148 | 149 | private function find_and_collect_for_call(CallExpression $call, array &$fixeds): ?Node 150 | { 151 | $callee = $call->callee; 152 | $hit = null; 153 | 154 | if ($callee instanceof PlainIdentifier) { 155 | if ($callee->is_same_or_based_with($this->collect_type)) { 156 | // create a temp variable to insteadof old expression 157 | $var_declar = $this->create_local_variable($call); 158 | $var_ident = ASTHelper::create_variable_identifier($var_declar); 159 | $fixeds[] = $var_declar; 160 | $fixeds[] = new ArrayElementAssignment($this->vector_identifier, null, $var_ident); 161 | 162 | return $var_ident; 163 | } 164 | } 165 | elseif ($callee instanceof CallExpression) { 166 | $hit = $this->find_and_collect_for_call($callee, $fixeds); 167 | } 168 | elseif ($callee instanceof AccessingIdentifier) { 169 | $hit = $this->find_and_collect_for_accessing($callee, $fixeds); 170 | } 171 | 172 | if ($hit) { 173 | $new_call = clone $call; 174 | $new_call->callee = $hit; 175 | return $new_call; 176 | } 177 | 178 | return null; 179 | } 180 | 181 | private function find_and_collect_for_accessing(AccessingIdentifier $accessing, array &$fixeds): ?Node 182 | { 183 | $master = $accessing->master; 184 | 185 | $hit = null; 186 | if ($master instanceof CallExpression) { 187 | $hit = $this->find_and_collect_for_call($master, $fixeds); 188 | } 189 | elseif ($master instanceof AccessingIdentifier) { 190 | $hit = $this->find_and_collect_for_accessing($master, $fixeds); 191 | } 192 | 193 | if ($hit) { 194 | $new_accessing = clone $accessing; 195 | $new_accessing->master = $hit; 196 | return $new_accessing; 197 | } 198 | 199 | return null; 200 | } 201 | 202 | private function create_local_variable(IExpression $value) 203 | { 204 | $name = '__tmp' . $this->temp_var_idx++; 205 | return new VariableDeclaration($name, null, $value); 206 | } 207 | } 208 | 209 | -------------------------------------------------------------------------------- /builtin/core.tea: -------------------------------------------------------------------------------- 1 | --- 2 | The builtin libs for Tea 3 | 4 | created: 2019/02 by Benny Lin 5 | copyright: YJ Technology Ltd. All rights reserved. 6 | --- 7 | 8 | public NL = "\n" 9 | 10 | #php __DIR__ String 11 | #php __LINE__ UInt 12 | 13 | #php $_GET Dict 14 | #php $_POST Dict 15 | #php $_COOKIE Dict 16 | #php $_SERVER Dict 17 | 18 | #php Exception { 19 | public construct(message String, code Int = 0) 20 | public getCode() Int 21 | public getMessage() String 22 | } 23 | 24 | #php ErrorException: Exception {} 25 | 26 | // types 27 | #php strval(val) String 28 | #php intval(val) Int 29 | #php floatval(val) Float 30 | #php boolval(val) Bool 31 | 32 | // math 33 | #php abs(val) Float 34 | #php round(val, precision = 0) Float 35 | #php ceil(val) Float 36 | #php floor(val) Float 37 | 38 | #php is_int(val) Bool 39 | public is_uint(val) Bool { 40 | return is_int(val) and val >= 0 41 | } 42 | 43 | // string 44 | #php trim(str, char_mask String = #default) String 45 | #php rtrim(str, char_mask String = #default) String 46 | #php ltrim(str, char_mask String = #default) String 47 | 48 | // with default charset 49 | #php iconv_strlen(str String) UInt 50 | #php iconv_substr(str String, start Int, length UInt = #default) String 51 | 52 | // first pos in default charset 53 | #php iconv_strpos(str String, search String, offset Int = 0) Int // would return false on not found 54 | public _iconv_strpos(str String, search String, offset Int = 0) Int { 55 | pos = iconv_strpos(str, search, offset); 56 | return pos === false ? -1 : pos // return -1 on not found 57 | } 58 | 59 | // last pos in default charset 60 | #php iconv_strrpos(str String, search String, offset Int = 0) Int // would return false on not found 61 | public _iconv_strrpos(str String, search String, offset Int = 0) Int { 62 | pos = iconv_strrpos(str, search, offset); 63 | return pos === false ? -1 : pos // return -1 on not found 64 | } 65 | 66 | #php mb_strtoupper(str String) String 67 | #php mb_strtolower(str String) String 68 | 69 | // bytes mode functions 70 | #php strlen(str String) UInt 71 | #php substr(str String, start Int, length UInt = #default) String 72 | #php strpos(master String, search String, offset Int = 0) Int // would return false on not found 73 | public _strpos(master String, search String, offset Int = 0) Int { 74 | pos = strpos(master, search, offset); 75 | return pos === false ? -1 : pos // return -1 on not found 76 | } 77 | #php strrpos(master String, search String, offset Int = 0) Int // would return false on not found 78 | public _strrpos(master String, search String, offset Int = 0) Int { 79 | pos = strrpos(master, search, offset); 80 | return pos === false ? -1 : pos // return -1 on not found 81 | } 82 | 83 | // replace in binary mode 84 | #php strtr(master String, changes Dict) String 85 | #php str_replace(search, replacement, master String) String 86 | public _str_replace(master String, search String, replacement String) { 87 | return str_replace(search, replacement, master); 88 | } 89 | 90 | #php implode(pieces, delimiter String = #default) String // 故意使用跟文档不一样的参数顺序,以跟masked参数顺序一致 91 | #php explode(delimiter String, master String) Array 92 | 93 | // array 94 | #php count(array) UInt 95 | #php in_array(needle, haystack, strict Bool = false) Bool 96 | #php array_reverse(array) 97 | 98 | // index array 99 | #php array_slice(master Array, offset Int, length UInt = none) Array 100 | #php array_unshift(master Array, item Any) UInt 101 | #php array_shift(master Array) Any 102 | #php array_push(master Array, item Any) UInt 103 | #php array_pop(master Array) Any 104 | #php array_search(search, master) Int 105 | public _array_search(master, search) Int { 106 | index = array_search(search, master); 107 | return index === false ? -1 : index // return -1 on not found 108 | } 109 | #php array_key_exists(key, arr Dict) Bool 110 | 111 | // assoc array 112 | #php unset(target) Void 113 | #php array_keys(array Dict) Array 114 | #php array_values(array Dict) Array 115 | 116 | #php JSON_UNESCAPED_UNICODE Int 117 | #php json_encode(data, flags Int = 0, depth UInt = 512) String 118 | #php json_decode(data, assoc Bool = false, depth UInt = 512, flags Int = 0) Dict 119 | #php date(format String, timestamp Int = #default) String 120 | 121 | // regex 122 | #php preg_match(regex Regex, subject String, matches Array = #default, flags Int = #default, offset Int = #default) Bool 123 | #php preg_match_all(regex Regex, subject String, matches Array = #default, flags Int = #default, offset Int = #default) Bool 124 | 125 | // public IShareAble { 126 | // vshare() Object 127 | // } 128 | 129 | // public ICloneAble { 130 | // vclone() Object 131 | // } 132 | 133 | #tea Any {} 134 | #tea None {} 135 | 136 | #tea Scalar { 137 | masked string => strval(this) 138 | masked float => floatval(this) 139 | masked int => intval(this) 140 | masked bool => boolval(this) 141 | } 142 | 143 | // 所有 masked function 在定义时都需要考虑参数的顺序,参数的运行时序不一致可能会导致问题 144 | // 考虑禁止在调用函数的参数中改变数据(需要推断出可能改变数据的函数,或直接禁止参数带函数调用),安全表达式? 145 | 146 | #tea String: Scalar { 147 | masked byte_length => strlen(this) 148 | 149 | // handle with default charset 150 | masked length => iconv_strlen(this) 151 | masked find(str String, offset Int = 0) => _iconv_strpos(this, str, offset) 152 | masked find_last(search String, offset Int = 0) => _iconv_strrpos(this, search, offset) 153 | masked slice(start Int, length UInt = #default) => iconv_substr(this, start, length) 154 | // masked slice(start Int, end Int) => substr_by_position(start, end) 155 | 156 | masked lower() => mb_strtolower(this) 157 | masked upper() => mb_strtoupper(this) 158 | 159 | masked explode(delimiter String) => explode(delimiter, this) // 编译后参数顺序有变化,使用时应避免当前字符串被delimiter表达式所改变 160 | masked replace(search String, replacement String) => _str_replace(this, search, replacement) 161 | 162 | masked json_decode() => json_decode(this, true) 163 | } 164 | 165 | #tea Float: Scalar { 166 | masked abs() => abs(this) 167 | masked ceil() => ceil(this) 168 | masked floor() => floor(this) 169 | masked round(precision = 0) => round(this, precision) 170 | } 171 | 172 | #tea Int: Scalar { 173 | masked abs() UInt => abs(this) 174 | } 175 | 176 | #tea UInt: Int {} 177 | 178 | #tea Bool: Scalar { 179 | masked int UInt => intval(this) // to UInt 180 | } 181 | 182 | #tea Dict { 183 | masked length => count(this) 184 | 185 | masked keys => array_keys(this) 186 | masked values => array_values(this) 187 | 188 | masked has_key(key) => array_key_exists(key, this) 189 | masked has_value(val) => in_array(val, this) 190 | masked find(val) => _array_search(this, val) // find the key with supported value 191 | 192 | // masked remove(key String) => unset(this[key]) 193 | 194 | masked join(delimiter String = NL) => implode(this, delimiter) 195 | masked json_encode(flags Int = JSON_UNESCAPED_UNICODE) => json_encode(this, flags) 196 | } 197 | 198 | #tea Array { 199 | masked length => count(this) 200 | 201 | masked has_value(val) => in_array(val, this) 202 | masked find(val) => _array_search(this, val) // find the key with supported value 203 | 204 | masked unshift(item) => array_unshift(this, item) 205 | masked shift() => array_shift(this) 206 | masked push(item) => array_push(this, item) 207 | masked pop() => array_pop(this) 208 | 209 | masked slice(start Int, length UInt = #default) Array => array_slice(this, start, length) 210 | masked reverse() Array => array_reverse(this) 211 | 212 | masked join(delimiter String = NL) => implode(this, delimiter) 213 | masked json_encode(flags Int = JSON_UNESCAPED_UNICODE) => json_encode(this, flags) 214 | } 215 | 216 | #tea Regex {} 217 | #tea XView {} // accept IView 218 | #tea Iterable {} // accept IteratorInterface 219 | #tea Callable {} 220 | 221 | public IView {} 222 | #php Iterator as IteratorInterface {} 223 | 224 | // end of program 225 | -------------------------------------------------------------------------------- /compiler/parser/TeaXblockTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | const _XTAG_SELF_END = '/>'; 13 | 14 | const _LEAF_TAGS = [ 15 | 'meta', 'link', 'img', 'input', 'br', 'hr', '!doctype', 16 | 'wbr', 'col', 'embed', 'param', 'source', 'track', 'area', 'keygen' 17 | ]; 18 | 19 | const _VIRTUAL_TAG = 'vtag'; 20 | 21 | trait TeaXBlockTrait 22 | { 23 | protected function read_xblock() 24 | { 25 | $block_previous_spaces = $this->get_previous_inline_spaces(); 26 | 27 | $token = $this->scan_token(); 28 | if (!TeaHelper::is_xtag_name($token)) { 29 | throw $this->new_unexpect_exception($token); 30 | } 31 | 32 | $items[] = $this->try_read_xtag($token, $block_previous_spaces); 33 | 34 | $skiped_spaces = ''; 35 | while ($this->get_token_closely($skiped_spaces) === _XTAG_OPEN) { 36 | $this->scan_token_ignore_empty(); // _XTAG_OPEN 37 | 38 | $token = $this->scan_token(); // 39 | if (!TeaHelper::is_xtag_name($token)) { 40 | break; 41 | } 42 | 43 | $items[count($items) - 1]->post_spaces = $this->strip_previous_spaces($skiped_spaces, $block_previous_spaces); 44 | $items[] = $this->read_xtag($token, $block_previous_spaces); 45 | } 46 | 47 | $node = new XBlock(...$items); 48 | $node->pos = $this->pos; 49 | 50 | return $node; 51 | } 52 | 53 | protected function try_read_xtag(?string $tag, string $block_previous_spaces) 54 | { 55 | if (!TeaHelper::is_xtag_name($tag)) { 56 | throw $this->new_unexpect_exception($token); 57 | } 58 | 59 | return $this->read_xtag($tag, $block_previous_spaces); 60 | } 61 | 62 | protected function read_xtag(string $tag, string $block_previous_spaces) 63 | { 64 | if ($tag === _EXCLAMATION) { 65 | $tag .= $this->scan_token(); // maybe the !DOCTYPE 66 | if ($tag === '!--') { 67 | // '); 103 | $this->scan_token(); // skip --> 104 | 105 | $block_previous_spaces && $content = $this->strip_previous_spaces($content, $block_previous_spaces); 106 | 107 | return new XBlockComment($content); 108 | } 109 | 110 | protected function read_xtag_attributes() 111 | { 112 | $string = ''; 113 | $items = []; 114 | while (($token = $this->scan_token()) !== null) { 115 | // the close tag 116 | if ($token === _XTAG_CLOSE || $token === _XTAG_SELF_END) { 117 | if ($string !== '') { 118 | $items[] = $string; 119 | } 120 | 121 | return $items; 122 | } 123 | 124 | if ($token === _SHARP && $this->skip_token(_BLOCK_BEGIN)) { 125 | $expression = $this->read_instring_sharp_expression(); 126 | static::collect_and_reset_temp($items, $string, $expression); 127 | continue; 128 | } 129 | elseif ($token === _DOLLAR) { 130 | if ($this->get_token() === _BLOCK_BEGIN) { 131 | $expression = $this->read_dollar_block_expression(); 132 | if (!$expression) { 133 | continue; // just a empty expression 134 | } 135 | } 136 | else { 137 | $expression = $this->try_read_identifier_expression(); 138 | if (!$expression) { 139 | $string .= $token; 140 | continue; 141 | } 142 | } 143 | 144 | static::collect_and_reset_temp($items, $string, $expression); 145 | continue; 146 | } 147 | elseif ($token === _BACK_SLASH) { 148 | $token .= $this->scan_token(); 149 | } 150 | 151 | // the string 152 | $string .= $token; 153 | } 154 | 155 | // close not found 156 | throw $this->new_unexpect_exception($token); 157 | } 158 | 159 | protected function read_xtag_children(string $tag, $block_previous_spaces) 160 | { 161 | $items = []; 162 | $string = ''; 163 | while (($token = $this->scan_token()) !== null) { 164 | switch ($token) { 165 | case _XTAG_OPEN: 166 | if ($string !== '') { 167 | $items[] = $string; 168 | $string = ''; // reset 169 | } 170 | 171 | // it should be a child tag 172 | $next = $this->scan_token(); 173 | if (TeaHelper::is_xtag_name($next)) { 174 | $items[] = $this->read_xtag($next, $block_previous_spaces); 175 | } 176 | elseif (TeaHelper::is_space_tab($next)) { 177 | $string .= $token . $next; // that's just a string 178 | continue 2; 179 | } 180 | elseif ($next === _SLASH) { // the scan_token() !== $tag) { 182 | // a wrong close tag 183 | throw $this->new_unexpect_exception($token); 184 | } 185 | 186 | $this->expect_token_ignore_empty(_XTAG_CLOSE); // the > 187 | 188 | // current element end 189 | return $this->strip_previous_spaces_for_items($items, $block_previous_spaces); 190 | } 191 | else { 192 | throw $this->new_unexpect_exception($next); 193 | } 194 | 195 | break; 196 | 197 | case _SHARP: 198 | if ($this->skip_token(_BLOCK_BEGIN)) { 199 | $expression = $this->read_instring_sharp_expression(); 200 | static::collect_and_reset_temp($items, $string, $expression); 201 | } 202 | else { 203 | $string .= $token; 204 | } 205 | break; 206 | 207 | case _DOLLAR: 208 | if ($this->get_token() === _BLOCK_BEGIN) { 209 | $expression = $this->read_dollar_block_expression(); 210 | if (!$expression) { 211 | break; 212 | } 213 | } 214 | else { 215 | $expression = $this->try_read_identifier_expression(); 216 | if (!$expression) { 217 | $string .= $token; 218 | break; 219 | } 220 | } 221 | 222 | static::collect_and_reset_temp($items, $string, $expression); 223 | break; 224 | 225 | case _BACK_SLASH: 226 | $token .= $this->scan_token(); 227 | // unbreak 228 | 229 | default: 230 | // the string 231 | $string .= $token; 232 | } 233 | } 234 | 235 | // the close not found 236 | throw $this->new_unexpect_exception($token); 237 | } 238 | 239 | protected function strip_previous_spaces_for_items(array $items, string $block_previous_spaces) 240 | { 241 | if (empty($block_previous_spaces)) { 242 | return $items; 243 | } 244 | 245 | foreach ($items as $key => $value) { 246 | if (is_string($value)) { 247 | $items[$key] = $this->strip_previous_spaces($value, $block_previous_spaces); 248 | } 249 | } 250 | 251 | return $items; 252 | } 253 | 254 | protected function strip_previous_spaces(string $string, string $block_previous_spaces) 255 | { 256 | if (strpos($string, $block_previous_spaces) !== false) { 257 | return str_replace(NL . $block_previous_spaces, NL, $string); 258 | } 259 | 260 | return $string; 261 | } 262 | } 263 | 264 | // end 265 | -------------------------------------------------------------------------------- /compiler/ast/OperatorFactory.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright (c)2019 YJ Technology Ltd. [http://tealang.org] 7 | * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. 8 | */ 9 | 10 | namespace Tea; 11 | 12 | use Exception; 13 | 14 | const _PREFIX_OP_PRECEDENCES = [ 15 | _NEGATION => 2, _BITWISE_NOT => 2, 16 | _NOT => 8, 17 | ]; 18 | 19 | const _BINARY_OP_PRECEDENCES = [ 20 | // L1 21 | _DOT => 1, // class/object 22 | _NOTIFY => 1, // the callback notify 23 | // () [] 24 | 25 | // L2 26 | // _NEGATION => 2, _BITWISE_NOT => 2, // prefix unary 27 | _AS => 2, // type cast 28 | 29 | // L3 30 | _EXPONENTIATION => 3, // math 31 | 32 | // L4 33 | _MULTIPLICATION => 4, _DIVISION => 4, _REMAINDER => 4, // math 34 | _SHIFT_LEFT => 4, _SHIFT_RIGHT => 4, _BITWISE_AND => 4, // bitwise 35 | 36 | // L5 37 | _ADDITION => 5, _SUBTRACTION => 5, // math 38 | _BITWISE_OR => 5, _BITWISE_XOR => 5, // bitwise 39 | 40 | // L6 41 | _CONCAT => 6, // String / Array, concat to the end of the left expression 42 | _MERGE => 6, // Dict / Array, merge by key or index 43 | // 'pop', 'take' // todo? 44 | 45 | // L7 comparisons 46 | '<' => 7, '>' => 7, '<=' => 7, '>=' => 7, 47 | '==' => 7, '===' => 7, _NOT_EQUAL => 7, '!==' => 7, '<=>' => 7, 48 | _IS => 7, // type / class, and maybe pattern? 49 | 50 | // L8 51 | // _NOT => 8, 52 | 53 | // L9 54 | _AND => 9, 55 | 56 | // L10 57 | _OR => 10, // _XOR => 10, 58 | 59 | // L11 60 | _NONE_COALESCING => 11, 61 | 62 | // L12 63 | _CONDITIONAL => 12, // ternary conditional expression 64 | ]; 65 | 66 | class OperatorFactory 67 | { 68 | static $_dot; 69 | // static $_notify; // has parsed in the parser 70 | 71 | static $_negation; 72 | static $_bitwise_not; // eg. ~0 == -1 73 | 74 | static $_as; 75 | 76 | static $_exponentiation; 77 | 78 | static $_multiplication; 79 | static $_division; 80 | static $_remainder; 81 | 82 | static $_addition; 83 | static $_subtraction; 84 | 85 | static $_concat; 86 | static $_merge; 87 | 88 | static $_shift_left; 89 | static $_shift_right; 90 | 91 | static $_is; 92 | static $_lessthan; 93 | static $_morethan; 94 | static $_lessthan_or_equal; 95 | static $_morethan_or_equal; 96 | static $_equal; 97 | static $_strict_equal; 98 | 99 | static $_not_equal; 100 | static $_strict_not_equal; 101 | 102 | static $_comparison; 103 | 104 | static $_bitwise_and; 105 | static $_bitwise_xor; 106 | static $_bitwise_or; 107 | 108 | static $_none_coalescing; 109 | static $_conditional; // exp0 ? exp1 : exp2, or exp0 ?: exp1 110 | 111 | static $_bool_not; 112 | static $_bool_and; 113 | // static $_bool_xor; 114 | static $_bool_or; 115 | 116 | private static $prefix_op_symbol_map = []; 117 | private static $binary_op_symbol_map = []; 118 | 119 | private static $number_op_symbols; 120 | private static $bitwise_op_symbols; 121 | private static $bool_op_symbols; 122 | 123 | public static function init() 124 | { 125 | self::$_negation = self::create_prefix_operator_symbol(_NEGATION); 126 | self::$_bitwise_not = self::create_prefix_operator_symbol(_BITWISE_NOT); 127 | 128 | self::$_dot = self::create_normal_operator_symbol(_DOT); 129 | // self::$_notify = self::create_normal_operator_symbol(_NOTIFY); 130 | 131 | self::$_as = self::create_normal_operator_symbol(_AS); 132 | 133 | self::$_exponentiation = self::create_normal_operator_symbol(_EXPONENTIATION); 134 | 135 | self::$_multiplication = self::create_normal_operator_symbol(_MULTIPLICATION); 136 | self::$_division = self::create_normal_operator_symbol(_DIVISION); 137 | self::$_remainder = self::create_normal_operator_symbol(_REMAINDER); 138 | 139 | self::$_addition = self::create_normal_operator_symbol(_ADDITION); 140 | self::$_subtraction = self::create_normal_operator_symbol(_SUBTRACTION); 141 | 142 | self::$_concat = self::create_normal_operator_symbol(_CONCAT); 143 | self::$_merge = self::create_normal_operator_symbol(_MERGE); 144 | 145 | self::$_shift_left = self::create_normal_operator_symbol(_SHIFT_LEFT); 146 | self::$_shift_right = self::create_normal_operator_symbol(_SHIFT_RIGHT); 147 | 148 | self::$_is = self::create_normal_operator_symbol(_IS); 149 | 150 | self::$_lessthan = self::create_normal_operator_symbol('<'); 151 | self::$_morethan = self::create_normal_operator_symbol('>'); 152 | self::$_lessthan_or_equal = self::create_normal_operator_symbol('<='); 153 | self::$_morethan_or_equal = self::create_normal_operator_symbol('>='); 154 | self::$_comparison = self::create_normal_operator_symbol('<=>'); 155 | 156 | self::$_equal = self::create_normal_operator_symbol('=='); 157 | self::$_strict_equal = self::create_normal_operator_symbol('==='); 158 | 159 | self::$_not_equal = self::create_normal_operator_symbol(_NOT_EQUAL); 160 | self::$_strict_not_equal = self::create_normal_operator_symbol('!=='); 161 | 162 | self::$_bitwise_and = self::create_normal_operator_symbol(_BITWISE_AND); 163 | self::$_bitwise_xor = self::create_normal_operator_symbol(_BITWISE_XOR); 164 | self::$_bitwise_or = self::create_normal_operator_symbol(_BITWISE_OR); 165 | 166 | self::$_none_coalescing = self::create_normal_operator_symbol(_NONE_COALESCING); 167 | 168 | self::$_conditional = self::create_normal_operator_symbol(_CONDITIONAL); 169 | 170 | self::$_bool_not = self::create_prefix_operator_symbol(_NOT); 171 | self::$_bool_and = self::create_normal_operator_symbol(_AND); 172 | self::$_bool_or = self::create_normal_operator_symbol(_OR); 173 | 174 | // number 175 | self::$number_op_symbols = [ 176 | self::$_addition, self::$_subtraction, self::$_multiplication, self::$_division, self::$_remainder, self::$_exponentiation, 177 | self::$_comparison 178 | ]; 179 | 180 | // bitwise 181 | self::$bitwise_op_symbols = [self::$_bitwise_and, self::$_bitwise_xor, self::$_bitwise_or, self::$_shift_left, self::$_shift_right]; 182 | 183 | // bool 184 | self::$bool_op_symbols = [ 185 | self::$_bool_and, self::$_bool_or, 186 | self::$_equal, self::$_strict_equal, self::$_not_equal, self::$_strict_not_equal, self::$_is, 187 | self::$_lessthan, self::$_morethan, self::$_lessthan_or_equal, self::$_morethan_or_equal 188 | ]; 189 | } 190 | 191 | /** 192 | * 设置待渲染的目标语言符号映射和优先级 193 | * @map array [src sign => dist sign] 194 | * @precedences array [dist sign => precedence] 195 | */ 196 | public static function set_render_options(array $map, array $precedences) 197 | { 198 | foreach (self::$binary_op_symbol_map as $sign => $symbol) { 199 | $dist_sign = $map[$sign] ?? $sign; 200 | 201 | if (!isset($precedences[$dist_sign])) { 202 | throw new Exception("Dist precedence of '$dist_sign' not found."); 203 | } 204 | 205 | $symbol->dist_sign = $dist_sign; 206 | $symbol->dist_precedence = $precedences[$dist_sign]; 207 | } 208 | } 209 | 210 | public static function is_number_operator(OperatorSymbol $symbol) 211 | { 212 | return in_array($symbol, self::$number_op_symbols, true); 213 | } 214 | 215 | public static function is_bitwise_operator(OperatorSymbol $symbol) 216 | { 217 | return in_array($symbol, self::$bitwise_op_symbols, true); 218 | } 219 | 220 | public static function is_bool_operator(OperatorSymbol $symbol) 221 | { 222 | return in_array($symbol, self::$bool_op_symbols, true); 223 | } 224 | 225 | public static function get_prefix_operator_symbol(?string $sign) 226 | { 227 | return self::$prefix_op_symbol_map[$sign] ?? null; 228 | } 229 | 230 | public static function get_normal_operator_symbol(?string $sign) 231 | { 232 | return self::$binary_op_symbol_map[$sign] ?? null; 233 | } 234 | 235 | private static function create_prefix_operator_symbol(string $sign) 236 | { 237 | return self::$prefix_op_symbol_map[$sign] = new OperatorSymbol($sign, _PREFIX_OP_PRECEDENCES[$sign]); 238 | } 239 | 240 | private static function create_normal_operator_symbol(string $sign) 241 | { 242 | return self::$binary_op_symbol_map[$sign] = new OperatorSymbol($sign, _BINARY_OP_PRECEDENCES[$sign]); 243 | } 244 | } 245 | 246 | --------------------------------------------------------------------------------