├── 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
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 |
15 | ' . implode($list, NL) . '
16 |
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 | ' . $this->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 <<hei~';
26 |
27 | new CollectorDemo();
28 |
29 | (new CollectorDemo())->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
181 | if ($this->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 |
--------------------------------------------------------------------------------