Example #1
0
func TestList(t *testing.T) {
	testStr := `<?
    list($one, $two) = array(1, 2);`

	p := NewParser(testStr)
	a, errs := p.Parse()
	if len(errs) != 0 {
		t.Fatalf("Did not parse list correctly: %s", errs)
	}

	tree := ast.ExpressionStmt{&ast.ListStatement{
		Operator: "=",
		Assignees: []ast.Assignable{
			ast.NewVariable("one"),
			ast.NewVariable("two"),
		},
		Value: &ast.ArrayExpression{
			Pairs: []ast.ArrayPair{
				{Key: nil, Value: &ast.Literal{Value: "1", Type: ast.Float}},
				{Key: nil, Value: &ast.Literal{Value: "2", Type: ast.Float}},
			},
		},
	}}

	if !assertEquals(a[0], tree) {
		t.Fatalf("Array bracked did not parse correctly")
	}
}
Example #2
0
func TestForeachLoop(t *testing.T) {
	testStr := `<?
  foreach ($arr as $key => $val) {
    echo $key . $val;
  } ?>`
	p := NewParser(testStr)
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("While loop did not correctly parse")
	}
	tree := &ast.ForeachStmt{
		Source: ast.NewVariable("arr"),
		Key:    ast.NewVariable("key"),
		Value:  ast.NewVariable("val"),
		LoopBlock: &ast.Block{
			Statements: []ast.Statement{ast.Echo(ast.OperatorExpression{
				Operator: ".",
				Operand1: ast.NewVariable("key"),
				Operand2: ast.NewVariable("val"),
				Type:     ast.String,
			})},
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Foreach did not correctly parse")
	}
}
Example #3
0
func (p *Parser) parseForeach() ast.Statement {
	stmt := &ast.ForeachStmt{}
	p.expect(token.OpenParen)
	stmt.Source = p.parseNextExpression()
	p.expect(token.AsOperator)
	if p.peek().typ == token.AmpersandOperator {
		p.expect(token.AmpersandOperator)
	}
	p.expect(token.VariableOperator)
	p.next()
	first := ast.NewVariable(p.current.val)
	if p.peek().typ == token.ArrayKeyOperator {
		stmt.Key = first
		p.expect(token.ArrayKeyOperator)
		if p.peek().typ == token.AmpersandOperator {
			p.expect(token.AmpersandOperator)
		}
		p.expect(token.VariableOperator)
		p.next()
		stmt.Value = ast.NewVariable(p.current.val)
	} else {
		stmt.Value = first
	}
	p.expect(token.CloseParen)
	p.next()
	stmt.LoopBlock = p.parseControlBlock(token.EndForeach)
	return stmt
}
Example #4
0
func TestProperty(t *testing.T) {
	testStr := `<?
  $res = $var->go;
  $var->go = $res;`
	p := NewParser(testStr)
	p.Debug = true
	p.MaxErrors = 0
	a, _ := p.Parse()
	if len(a) != 2 {
		t.Fatalf("Property did not correctly parse")
	}
	tree := ast.ExpressionStmt{ast.AssignmentExpression{
		Assignee: ast.NewVariable("res"),
		Operator: "=",
		Value: &ast.PropertyExpression{
			Receiver: ast.NewVariable("var"),
			Name:     ast.Identifier{Value: "go"},
		},
	}}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Property did not correctly parse")
	}

	tree = ast.ExpressionStmt{ast.AssignmentExpression{
		Assignee: &ast.PropertyExpression{
			Receiver: ast.NewVariable("var"),
			Name:     ast.Identifier{Value: "go"},
		},
		Operator: "=",
		Value:    ast.NewVariable("res"),
	}}
	if !assertEquals(a[1], tree) {
		t.Fatalf("Property did not correctly parse")
	}
}
Example #5
0
func TestWhileLoopWithAssignment(t *testing.T) {
	testStr := `<?
  while ($var = mysql_assoc()) {
    echo $var;
  }`
	p := NewParser(testStr)
	p.Debug = true
	p.MaxErrors = 0
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("While loop did not correctly parse")
	}
	tree := &ast.WhileStmt{
		Termination: ast.AssignmentExpression{
			Assignee: ast.NewVariable("var"),
			Value: &ast.FunctionCallExpression{
				FunctionName: ast.Identifier{Value: "mysql_assoc"},
				Arguments:    make([]ast.Expression, 0),
			},
			Operator: "=",
		},
		LoopBlock: &ast.Block{
			Statements: []ast.Statement{
				ast.Echo(ast.NewVariable("var")),
			},
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("While loop with assignment did not correctly parse")
	}
}
Example #6
0
func TestInstantiation(t *testing.T) {
	testStr := `<?
  $obj = new Obj::$classes['obj']($arg);`
	p := NewParser(testStr)
	a, errs := p.Parse()
	if len(errs) != 0 {
		t.Fatalf("Did not parse instantiation correctly: %s", errs)
	}
	tree := ast.ExpressionStmt{ast.AssignmentExpression{
		Operator: "=",
		Assignee: ast.NewVariable("obj"),
		Value: &ast.NewExpression{
			Class: ast.NewClassExpression("Obj", &ast.ArrayLookupExpression{
				Array: ast.NewVariable("classes"),
				Index: &ast.Literal{Type: ast.String, Value: `'obj'`},
			}),
			Arguments: []ast.Expression{
				ast.NewVariable("arg"),
			},
		},
	}}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Instantiation did not parse correctly")
	}
}
Example #7
0
func TestMethodCall(t *testing.T) {
	testStr := `<?
  $res = $var->go();`
	p := NewParser(testStr)
	p.Debug = true
	p.MaxErrors = 0
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("Method call did not correctly parse")
	}
	tree := ast.ExpressionStmt{ast.AssignmentExpression{
		Assignee: ast.NewVariable("res"),
		Operator: "=",
		Value: &ast.MethodCallExpression{
			Receiver: ast.NewVariable("var"),
			FunctionCallExpression: &ast.FunctionCallExpression{
				FunctionName: ast.Identifier{Value: "go"},
				Arguments:    make([]ast.Expression, 0),
			},
		},
	}}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Method call did not correctly parse")
	}
}
Example #8
0
func TestGlobal(t *testing.T) {
	testStr := `<?
  global $var, $otherVar;`
	p := NewParser(testStr)
	a, _ := p.Parse()
	tree := &ast.GlobalDeclaration{
		Identifiers: []*ast.Variable{
			ast.NewVariable("var"),
			ast.NewVariable("otherVar"),
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Global did not parse correctly")
	}
}
Example #9
0
func TestArrayKeys(t *testing.T) {
	testStr := `<?
  $var = array(1 => "one", 2 => "two", 3 => "three");`
	p := NewParser(testStr)
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("Array did not correctly parse")
	}
	tree := ast.ExpressionStmt{ast.AssignmentExpression{
		Assignee: ast.NewVariable("var"),
		Operator: "=",
		Value: &ast.ArrayExpression{
			ast.BaseNode{},
			ast.ArrayType{},
			[]ast.ArrayPair{
				{Key: &ast.Literal{Type: ast.Float, Value: "1"}, Value: &ast.Literal{Type: ast.String, Value: `"one"`}},
				{Key: &ast.Literal{Type: ast.Float, Value: "2"}, Value: &ast.Literal{Type: ast.String, Value: `"two"`}},
				{Key: &ast.Literal{Type: ast.Float, Value: "3"}, Value: &ast.Literal{Type: ast.String, Value: `"three"`}},
			},
		},
	}}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Array did not correctly parse")
	}
}
Example #10
0
func TestArray(t *testing.T) {
	testStr := `<?
  $var = array("one", "two", "three");`
	p := NewParser(testStr)
	p.Debug = true
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("Array did not correctly parse")
	}
	tree := ast.ExpressionStmt{
		ast.AssignmentExpression{
			Assignee: ast.NewVariable("var"),
			Operator: "=",
			Value: &ast.ArrayExpression{
				ast.BaseNode{},
				ast.ArrayType{},
				[]ast.ArrayPair{
					{Value: &ast.Literal{Type: ast.String, Value: `"one"`}},
					{Value: &ast.Literal{Type: ast.String, Value: `"two"`}},
					{Value: &ast.Literal{Type: ast.String, Value: `"three"`}},
				},
			},
		},
	}
	if !reflect.DeepEqual(a[0], tree) {
		fmt.Printf("Found:    %+v\n", a[0])
		fmt.Printf("Expected: %+v\n", tree)
		t.Fatalf("Array did not correctly parse")
	}
}
Example #11
0
func TestArrayBracket(t *testing.T) {
	testStr := `<?
    $arr = ["one", "two"];
    $arr2 = ["one" => 1, "two" => 2];`

	p := NewParser(testStr)
	a, errs := p.Parse()
	if len(errs) != 0 {
		t.Fatalf("Did not parse array bracket correctly: %s", errs)
	}

	tree := []ast.Statement{
		ast.ExpressionStmt{ast.AssignmentExpression{
			Operator: "=",
			Assignee: ast.NewVariable("arr"),
			Value: &ast.ArrayExpression{
				Pairs: []ast.ArrayPair{
					{Key: nil, Value: &ast.Literal{Value: `"one"`, Type: ast.String}},
					{Key: nil, Value: &ast.Literal{Value: `"two"`, Type: ast.String}},
				},
			},
		}},
		ast.ExpressionStmt{ast.AssignmentExpression{
			Operator: "=",
			Assignee: ast.NewVariable("arr2"),
			Value: &ast.ArrayExpression{
				Pairs: []ast.ArrayPair{
					{
						Key:   &ast.Literal{Value: `"one"`, Type: ast.String},
						Value: &ast.Literal{Value: "1", Type: ast.Float},
					},
					{
						Key:   &ast.Literal{Value: `"two"`, Type: ast.String},
						Value: &ast.Literal{Value: "2", Type: ast.Float},
					},
				},
			},
		}},
	}

	if !assertEquals(a[0], tree[0]) {
		t.Fatalf("Array bracked did not parse correctly")
	}
	if !assertEquals(a[1], tree[1]) {
		t.Fatalf("Array bracked did not parse correctly")
	}
}
Example #12
0
func TestFunction(t *testing.T) {
	testStr := `<?php
    function TestFn($arg) {
      echo $arg;
    }
    $var = TestFn("world", 0);`
	p := NewParser(testStr)
	a, _ := p.Parse()
	tree := []ast.Node{
		&ast.FunctionStmt{
			FunctionDefinition: &ast.FunctionDefinition{
				Name: "TestFn",
				Arguments: []ast.FunctionArgument{
					{
						Variable: ast.NewVariable("arg"),
					},
				},
			},
			Body: &ast.Block{
				Statements: []ast.Statement{ast.Echo(ast.NewVariable("arg"))},
			},
		},
		ast.ExpressionStmt{
			ast.AssignmentExpression{
				Assignee: ast.NewVariable("var"),
				Value: &ast.FunctionCallExpression{
					FunctionName: ast.Identifier{Value: "TestFn"},
					Arguments: []ast.Expression{
						&ast.Literal{Type: ast.String, Value: `"world"`},
						&ast.Literal{Type: ast.Float, Value: "0"},
					},
				},
				Operator: "=",
			},
		},
	}
	if len(a) != 2 {
		t.Fatalf("Function did not correctly parse")
	}
	if !assertEquals(a[0], tree[0]) {
		t.Fatalf("Function did not correctly parse")
	}
	if !assertEquals(a[1], tree[1]) {
		t.Fatalf("Function assignment did not correctly parse")
	}
}
Example #13
0
func TestScopeResolutionOperator(t *testing.T) {
	testStr := `<?
  MyClass::myfunc($var);
  echo MyClass::myconst;
  echo $var::myfunc();`
	p := NewParser(testStr)
	a, _ := p.Parse()
	tree := []ast.Node{
		ast.ExpressionStmt{
			&ast.ClassExpression{
				Receiver: ast.Identifier{Value: "MyClass"},
				Expression: &ast.FunctionCallExpression{
					FunctionName: ast.Identifier{Value: "myfunc"},
					Arguments: []ast.Expression{
						ast.NewVariable("var"),
					},
				},
			},
		},
		ast.Echo(&ast.ClassExpression{
			Receiver: ast.Identifier{Value: "MyClass"},
			Expression: ast.ConstantExpression{
				ast.NewVariable("myconst"),
			},
		}),
		ast.Echo(&ast.ClassExpression{
			Receiver: ast.NewVariable("var"),
			Expression: &ast.FunctionCallExpression{
				FunctionName: ast.Identifier{Value: "myfunc"},
				Arguments:    []ast.Expression{},
			},
		}),
	}
	if !assertEquals(a[0], tree[0]) {
		t.Fatalf("Scope resolution operator function call did not correctly parse")
	}
	if !assertEquals(a[1], tree[1]) {
		t.Fatalf("Scope resolution operator expression did not correctly parse")
	}
	if !assertEquals(a[2], tree[2]) {
		t.Fatalf("Scope resolution operator function call on identifier did not correctly parse")
	}
}
Example #14
0
func TestArrayLookup(t *testing.T) {
	testStr := `<?
  echo $arr['one'][$two];
  $var->arr[] = 2;
  echo $arr[2 + 1];`
	p := NewParser(testStr)
	p.Debug = true
	p.MaxErrors = 0
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("Array lookup did not correctly parse")
	}
	tree := []ast.Node{
		ast.EchoStmt{
			Expressions: []ast.Expression{&ast.ArrayLookupExpression{
				Array: &ast.ArrayLookupExpression{
					Array: ast.NewVariable("arr"),
					Index: &ast.Literal{Type: ast.String, Value: `'one'`},
				},
				Index: ast.NewVariable("two"),
			}},
		},
		ast.ExpressionStmt{
			ast.AssignmentExpression{
				Assignee: ast.ArrayAppendExpression{
					Array: &ast.PropertyExpression{
						Receiver: ast.NewVariable("var"),
						Name:     ast.Identifier{Value: "arr"},
					},
				},
				Operator: "=",
				Value:    &ast.Literal{Type: ast.Float, Value: "2"},
			},
		},
	}
	if !assertEquals(a[0], tree[0]) {
		t.Fatalf("Array lookup did not correctly parse")
	}
	if !assertEquals(a[1], tree[1]) {
		t.Fatalf("Array append expression did not correctly parse")
	}
}
Example #15
0
func TestLiterals(t *testing.T) {
	testStr := `<?
  $var = "one";
  $var = 2;
  $var = true;
  $var = null;`
	p := NewParser(testStr)
	a, _ := p.Parse()
	if len(a) != 4 {
		t.Fatalf("Literals did not correctly parse")
	}
	tree := []ast.Node{
		ast.ExpressionStmt{ast.AssignmentExpression{
			Assignee: ast.NewVariable("var"),
			Value:    &ast.Literal{Type: ast.String, Value: `"one"`},
			Operator: "=",
		}},
		ast.ExpressionStmt{ast.AssignmentExpression{
			Assignee: ast.NewVariable("var"),
			Value:    &ast.Literal{Type: ast.Float, Value: "2"},
			Operator: "=",
		}},
		ast.ExpressionStmt{ast.AssignmentExpression{
			Assignee: ast.NewVariable("var"),
			Value:    &ast.Literal{Type: ast.Boolean, Value: "true"},
			Operator: "=",
		}},
		ast.ExpressionStmt{ast.AssignmentExpression{
			Assignee: ast.NewVariable("var"),
			Value:    &ast.Literal{Type: ast.Null, Value: "null"},
			Operator: "=",
		}},
	}
	if !reflect.DeepEqual(a, tree) {
		fmt.Printf("Found:    %+v\n", a)
		fmt.Printf("Expected: %+v\n", tree)
		t.Fatalf("Literals did not correctly parse")
	}
}
Example #16
0
func TestForLoop(t *testing.T) {
	testStr := `<?
  for ($i = 0; $i < 10; $i++) {
    echo $i;
  }`
	p := NewParser(testStr)
	p.Debug = true
	p.MaxErrors = 0
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("For loop did not correctly parse")
	}
	tree := &ast.ForStmt{
		Initialization: []ast.Expression{ast.AssignmentExpression{
			Assignee: ast.NewVariable("i"),
			Value:    &ast.Literal{Type: ast.Float, Value: "0"},
			Operator: "=",
		}},
		Termination: []ast.Expression{ast.OperatorExpression{
			Operand1: ast.NewVariable("i"),
			Operand2: &ast.Literal{Type: ast.Float, Value: "10"},
			Operator: "<",
			Type:     ast.Boolean,
		}},
		Iteration: []ast.Expression{ast.OperatorExpression{
			Operator: "++",
			Operand1: ast.NewVariable("i"),
			Type:     ast.Numeric,
		}},
		LoopBlock: &ast.Block{
			Statements: []ast.Statement{
				ast.Echo(ast.NewVariable("i")),
			},
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("For did not correctly parse")
	}
}
Example #17
0
func TestWhileLoop(t *testing.T) {
	testStr := `<?
  while ($otherVar) {
    echo $var;
  }`
	p := NewParser(testStr)
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("While loop did not correctly parse")
	}
	tree := &ast.WhileStmt{
		Termination: ast.NewVariable("otherVar"),
		LoopBlock: &ast.Block{
			Statements: []ast.Statement{
				ast.Echo(ast.NewVariable("var")),
			},
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("TestLoop did not correctly parse")
	}
}
Example #18
0
File: oop.go Project: Jayflux/php
func (p *Parser) parseInterface() *ast.Interface {
	i := &ast.Interface{
		Inherits: make([]string, 0),
	}
	p.expect(token.Identifier)
	i.Name = p.current.val
	if p.peek().typ == token.Extends {
		p.expect(token.Extends)
		for {
			p.expect(token.Identifier)
			i.Inherits = append(i.Inherits, p.current.val)
			if p.peek().typ != token.Comma {
				break
			}
			p.expect(token.Comma)
		}
	}
	p.expect(token.BlockBegin)
	for p.peek().typ != token.BlockEnd {
		vis, _ := p.parseVisibility()
		if p.peek().typ == token.Static {
			p.next()
		}
		p.next()
		switch p.current.typ {
		case token.Function:
			f := p.parseFunctionDefinition()
			m := ast.Method{
				Visibility:   vis,
				FunctionStmt: &ast.FunctionStmt{FunctionDefinition: f},
			}
			i.Methods = append(i.Methods, m)
			p.expect(token.StatementEnd)
		case token.Const:
			constant := ast.Constant{}
			p.expect(token.Identifier)
			constant.Variable = ast.NewVariable(p.current.val)
			if p.peek().typ == token.AssignmentOperator {
				p.expect(token.AssignmentOperator)
				constant.Value = p.parseNextExpression()
			}
			i.Constants = append(i.Constants, constant)
			p.expect(token.StatementEnd)
		default:
			p.errorf("unexpected interface member %v", p.current)
		}
	}
	p.expect(token.BlockEnd)
	return i
}
Example #19
0
func TestSwitch(t *testing.T) {
	testStr := `<?
  switch ($var) {
  case 1:
    echo "one";
  case 2: {
    echo "two";
  }
  default:
    echo "def";
  }`
	p := NewParser(testStr)
	a, _ := p.Parse()
	if len(a) == 0 {
		t.Fatalf("Array lookup did not correctly parse")
	}
	tree := ast.SwitchStmt{
		Expression: ast.NewVariable("var"),
		Cases: []*ast.SwitchCase{
			{
				Expression: &ast.Literal{Type: ast.Float, Value: "1"},
				Block: ast.Block{
					Statements: []ast.Statement{
						ast.Echo(&ast.Literal{Type: ast.String, Value: `"one"`}),
					},
				},
			},
			{
				Expression: &ast.Literal{Type: ast.Float, Value: "2"},
				Block: ast.Block{
					Statements: []ast.Statement{
						ast.Echo(&ast.Literal{Type: ast.String, Value: `"two"`}),
					},
				},
			},
		},
		DefaultCase: &ast.Block{
			Statements: []ast.Statement{
				ast.Echo(&ast.Literal{Type: ast.String, Value: `"def"`}),
			},
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Switch did not correctly parse")
	}
}
Example #20
0
func (p *Parser) parseFunctionArgument() ast.FunctionArgument {
	arg := ast.FunctionArgument{}
	switch p.peek().typ {
	case token.Identifier, token.Array, token.Self:
		p.next()
		arg.TypeHint = p.current.val
	}
	if p.peek().typ == token.AmpersandOperator {
		p.next()
	}
	p.expect(token.VariableOperator)
	p.next()
	arg.Variable = ast.NewVariable(p.current.val)
	if p.peek().typ == token.AssignmentOperator {
		p.expect(token.AssignmentOperator)
		p.next()
		arg.Default = p.parseExpression()
	}
	return arg
}
Example #21
0
func TestCastOperator(t *testing.T) {
	testStr := `<?
  $var = (double) 1.0; ?>`
	p := NewParser(testStr)
	a, _ := p.Parse()
	tree := []ast.Node{
		ast.ExpressionStmt{ast.AssignmentExpression{
			Assignee: ast.NewVariable("var"),
			Value: ast.OperatorExpression{
				Operand1: &ast.Literal{Type: ast.Float, Value: "1.0"},
				Operator: "(double)",
				Type:     ast.Numeric,
			},
			Operator: "=",
		}},
	}
	if !assertEquals(a[0], tree[0]) {
		t.Fatalf("Cast operator parsing failed")
	}
}
Example #22
0
func (p *Parser) parseVariable() ast.Expression {
	p.expectCurrent(token.VariableOperator)
	switch p.next(); {
	case isKeyword(p.current.typ, p.current.val):
		// keywords are all valid variable names
		fallthrough
	case p.current.typ == token.Identifier:
		expr := ast.NewVariable(p.current.val)
		return expr
	case p.current.typ == token.BlockBegin:
		expr := &ast.Variable{Name: p.parseNextExpression()}
		p.expect(token.BlockEnd)
		return expr
	case p.current.typ == token.VariableOperator:
		return &ast.Variable{Name: p.parseVariable()}
	default:
		p.errorf("unexpected variable operand %s", p.current)
		return nil
	}
}
Example #23
0
func (p *Parser) parseIdentifier() (expr ast.Expression) {
	switch p.peek().typ {
	case token.OpenParen:
		// Function calls are okay here because we know they came with
		// a non-dynamic identifier.
		expr = p.parseFunctionCall(ast.Identifier{Value: p.current.val})
		p.next()
	case token.ScopeResolutionOperator:
		classIdent := p.current.val
		p.next() // get onto ::, then we get to the next expr
		p.next()
		expr = ast.NewClassExpression(classIdent, p.parseOperand())
		p.next()
	default:
		expr = ast.ConstantExpression{
			Variable: ast.NewVariable(p.current.val),
		}
		p.next()
	}
	return expr
}
Example #24
0
File: oop.go Project: Jayflux/php
func (p *Parser) parseClassFields(c ast.Class) ast.Class {
	// Starting on BlockBegin
	c.Methods = make([]ast.Method, 0)
	c.Properties = make([]ast.Property, 0)
	for p.peek().typ != token.BlockEnd {
		vis, _, _, abstract := p.parseClassMemberSettings()
		p.next()
		switch p.current.typ {
		case token.Function:
			if abstract {
				f := p.parseFunctionDefinition()
				m := ast.Method{
					Visibility:   vis,
					FunctionStmt: &ast.FunctionStmt{FunctionDefinition: f},
				}
				c.Methods = append(c.Methods, m)
				p.expect(token.StatementEnd)
			} else {
				c.Methods = append(c.Methods, ast.Method{
					Visibility:   vis,
					FunctionStmt: p.parseFunctionStmt(),
				})
			}
		case token.Var:
			p.expect(token.VariableOperator)
			fallthrough
		case token.VariableOperator:
			for {
				p.expect(token.Identifier)
				prop := ast.Property{
					Visibility: vis,
					Name:       "$" + p.current.val,
				}
				if p.peek().typ == token.AssignmentOperator {
					p.expect(token.AssignmentOperator)
					prop.Initialization = p.parseNextExpression()
				}
				c.Properties = append(c.Properties, prop)
				if p.accept(token.StatementEnd) {
					break
				}
				p.expect(token.Comma)
				p.expect(token.VariableOperator)
			}
		case token.Const:
			constant := ast.Constant{}
			p.expect(token.Identifier)
			constant.Variable = ast.NewVariable(p.current.val)
			if p.peek().typ == token.AssignmentOperator {
				p.expect(token.AssignmentOperator)
				constant.Value = p.parseNextExpression()
			}
			c.Constants = append(c.Constants, constant)
			p.expect(token.StatementEnd)
		default:
			p.errorf("unexpected class member %v", p.current)
			return c
		}
	}
	p.expect(token.BlockEnd)
	return c
}
Example #25
0
func (p *Parser) parseStmt() ast.Statement {
	switch p.current.typ {
	case token.BlockBegin:
		p.backup()
		return p.parseBlock()
	case token.Global:
		p.next()
		g := &ast.GlobalDeclaration{
			Identifiers: make([]*ast.Variable, 0, 1),
		}
		for p.current.typ == token.VariableOperator {
			variable, ok := p.parseVariable().(*ast.Variable)
			if !ok {
				p.errorf("global declarations must be of standard variables")
				break
			}
			g.Identifiers = append(g.Identifiers, variable)
			if p.peek().typ != token.Comma {
				break
			}
			p.expect(token.Comma)
			p.next()
		}
		p.expectStmtEnd()
		return g
	case token.Namespace:
		p.expect(token.Identifier)
		p.expectStmtEnd()
		// We are ignoring this for now
		return nil
	case token.Use:
		p.expect(token.Identifier)
		if p.peek().typ == token.AsOperator {
			p.expect(token.AsOperator)
			p.expect(token.Identifier)
		}
		p.expectStmtEnd()
		// We are ignoring this for now
		return nil
	case token.Static:
		if p.peek().typ == token.ScopeResolutionOperator {
			expr := p.parseExpression()
			p.expectStmtEnd()
			return expr
		}
		s := &ast.StaticVariableDeclaration{Declarations: make([]ast.Expression, 0)}
		for {
			p.expect(token.VariableOperator)
			p.expect(token.Identifier)
			v := ast.NewVariable(p.current.val)
			if p.peek().typ == token.AssignmentOperator {
				p.expect(token.AssignmentOperator)
				op := p.current.val
				p.expect(token.Null, token.StringLiteral, token.BooleanLiteral, token.NumberLiteral, token.Array)
				switch p.current.typ {
				case token.Array:
					s.Declarations = append(s.Declarations, &ast.AssignmentExpression{Assignee: v, Value: p.parseArrayDeclaration(), Operator: op})
				default:
					s.Declarations = append(s.Declarations, &ast.AssignmentExpression{Assignee: v, Value: p.parseLiteral(), Operator: op})
				}
			}
			s.Declarations = append(s.Declarations, v)
			if p.peek().typ != token.Comma {
				break
			}
			p.next()
		}
		p.expectStmtEnd()
		return s
	case token.VariableOperator, token.UnaryOperator:
		expr := ast.ExpressionStmt{p.parseExpression()}
		p.expectStmtEnd()
		return expr
	case token.Print:
		requireParen := false
		if p.peek().typ == token.OpenParen {
			p.expect(token.OpenParen)
			requireParen = true
		}
		stmt := ast.Echo(p.parseNextExpression())
		if requireParen {
			p.expect(token.CloseParen)
		}
		p.expectStmtEnd()
		return stmt
	case token.Function:
		return p.parseFunctionStmt()
	case token.PHPEnd:
		if p.peek().typ == token.EOF {
			return nil
		}
		var expr ast.Statement
		if p.accept(token.HTML) {
			expr = ast.Echo(&ast.Literal{Type: ast.String, Value: p.current.val})
		}
		p.next()
		if p.current.typ != token.EOF {
			p.expectCurrent(token.PHPBegin)
		}
		return expr
	case token.Echo:
		exprs := []ast.Expression{
			p.parseNextExpression(),
		}
		for p.peek().typ == token.Comma {
			p.expect(token.Comma)
			exprs = append(exprs, p.parseNextExpression())
		}
		p.expectStmtEnd()
		return ast.Echo(exprs...)
	case token.If:
		return p.parseIf()
	case token.While:
		return p.parseWhile()
	case token.Do:
		return p.parseDo()
	case token.For:
		return p.parseFor()
	case token.Foreach:
		return p.parseForeach()
	case token.Switch:
		return p.parseSwitch()
	case token.Abstract, token.Final, token.Class:
		return p.parseClass()
	case token.Interface:
		return p.parseInterface()
	case token.Return:
		p.next()
		stmt := ast.ReturnStmt{}
		if p.current.typ != token.StatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case token.Break:
		p.next()
		stmt := ast.BreakStmt{}
		if p.current.typ != token.StatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case token.Continue:
		p.next()
		stmt := ast.ContinueStmt{}
		if p.current.typ != token.StatementEnd {
			stmt.Expression = p.parseExpression()
			p.expectStmtEnd()
		}
		return stmt
	case token.Throw:
		stmt := ast.ThrowStmt{Expression: p.parseNextExpression()}
		p.expectStmtEnd()
		return stmt
	case token.Exit:
		stmt := ast.ExitStmt{}
		if p.peek().typ == token.OpenParen {
			p.expect(token.OpenParen)
			if p.peek().typ != token.CloseParen {
				stmt.Expression = p.parseNextExpression()
			}
			p.expect(token.CloseParen)
		}
		p.expectStmtEnd()
		return stmt
	case token.Try:
		stmt := &ast.TryStmt{}
		stmt.TryBlock = p.parseBlock()
		for p.expect(token.Catch); p.current.typ == token.Catch; p.next() {
			caught := &ast.CatchStmt{}
			p.expect(token.OpenParen)
			p.expect(token.Identifier)
			caught.CatchType = p.current.val
			p.expect(token.VariableOperator)
			p.expect(token.Identifier)
			caught.CatchVar = ast.NewVariable(p.current.val)
			p.expect(token.CloseParen)
			caught.CatchBlock = p.parseBlock()
			stmt.CatchStmts = append(stmt.CatchStmts, caught)
		}
		p.backup()
		return stmt
	case token.IgnoreErrorOperator:
		// Ignore this operator
		p.next()
		return p.parseStmt()
	case token.StatementEnd:
		// this is an empty statement
		return &ast.EmptyStatement{}
	default:
		expr := p.parseExpression()
		if expr != nil {
			p.expectStmtEnd()
			return ast.ExpressionStmt{expr}
		}
		p.errorf("Found %s, statement or expression", p.current)
		return nil
	}
}
Example #26
0
func TestClass(t *testing.T) {
	testStr := `<?php
    abstract class TestClass {
      public $myProp;
      protected $myProp2;
      const my_const = "test";
      private $arr = array("one", "two");
      abstract public function method0($arg);
      public function method1($arg) {
        echo $arg;
      }
      private function method2(TestClass $arg, $arg2 = false) {
        echo $arg;
        return $arg;
      }
    }`
	p := NewParser(testStr)
	p.Debug = true
	a, errs := p.Parse()
	if len(errs) > 0 {
		t.Fatal(errs)
	}
	if len(a) != 1 {
		t.Fatalf("Class did not correctly parse")
	}
	tree := ast.Class{
		Name: "TestClass",
		Constants: []ast.Constant{
			{
				Variable: ast.NewVariable("my_const"),
				Value:    &ast.Literal{Type: ast.String, Value: `"test"`},
			},
		},
		Properties: []ast.Property{
			{
				Visibility: ast.Public,
				Name:       "$myProp",
			},
			{
				Visibility: ast.Protected,
				Name:       "$myProp2",
			},
			{
				Visibility: ast.Private,
				Name:       "$arr",
				Initialization: &ast.ArrayExpression{
					Pairs: []ast.ArrayPair{
						{Value: &ast.Literal{Type: ast.String, Value: `"one"`}},
						{Value: &ast.Literal{Type: ast.String, Value: `"two"`}},
					},
				},
			},
		},
		Methods: []ast.Method{
			{
				Visibility: ast.Public,
				FunctionStmt: &ast.FunctionStmt{
					FunctionDefinition: &ast.FunctionDefinition{
						Name: "method0",
						Arguments: []ast.FunctionArgument{
							{
								Variable: ast.NewVariable("arg"),
							},
						},
					},
				},
			},
			{
				Visibility: ast.Public,
				FunctionStmt: &ast.FunctionStmt{
					FunctionDefinition: &ast.FunctionDefinition{
						Name: "method1",
						Arguments: []ast.FunctionArgument{
							{
								Variable: ast.NewVariable("arg"),
							},
						},
					},
					Body: &ast.Block{
						Statements: []ast.Statement{
							ast.Echo(ast.NewVariable("arg")),
						},
					},
				},
			},
			{
				Visibility: ast.Private,
				FunctionStmt: &ast.FunctionStmt{
					FunctionDefinition: &ast.FunctionDefinition{
						Name: "method2",
						Arguments: []ast.FunctionArgument{
							{
								TypeHint: "TestClass",
								Variable: ast.NewVariable("arg"),
							},
							{
								Variable: ast.NewVariable("arg2"),
								Default:  &ast.Literal{Type: ast.Boolean, Value: "false"},
							},
						},
					},
					Body: &ast.Block{
						Statements: []ast.Statement{
							ast.Echo(ast.NewVariable("arg")),
							ast.ReturnStmt{Expression: ast.NewVariable("arg")},
						},
					},
				},
			},
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("Class did not parse correctly")
	}
}