Example #1
0
func TestIfBraces(t *testing.T) {
	testStr := `<?php
    if (true) {
      echo "hello world";
    } else if (false) {
      echo "no hello world";
    }`
	p := NewParser(testStr)
	a, _ := p.Parse()
	tree := &ast.IfStmt{
		Condition: &ast.Literal{Type: ast.Boolean, Value: "true"},
		TrueBranch: &ast.Block{
			Statements: []ast.Statement{ast.Echo(&ast.Literal{Type: ast.String, Value: `"hello world"`})},
		},
		FalseBranch: &ast.IfStmt{
			Condition: &ast.Literal{Type: ast.Boolean, Value: "false"},
			TrueBranch: &ast.Block{
				Statements: []ast.Statement{ast.Echo(&ast.Literal{Type: ast.String, Value: `"no hello world"`})},
			},
			FalseBranch: ast.Block{},
		},
	}
	if !assertEquals(a[0], tree) {
		t.Fatalf("If with braces did not correctly parse")
	}
}
Example #2
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 #3
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 #4
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 #5
0
func TestPHPParserHW(t *testing.T) {
	testStr := `hello world`
	p := NewParser(testStr)
	a, _ := p.Parse()
	tree := ast.Echo(ast.Literal{Type: ast.String, Value: `hello world`})
	if !assertEquals(a[0], tree) {
		t.Fatalf("Hello world did not correctly parse")
	}
}
Example #6
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 #7
0
func (p *Parser) parseNode() ast.Node {
	switch p.current.typ {
	case token.HTML:
		return ast.Echo(ast.Literal{Type: ast.String, Value: p.current.val})
	case token.PHPBegin:
		return nil
	case token.PHPEnd:
		return nil
	}
	return p.parseStmt()
}
Example #8
0
func TestPHPParserHWPHP(t *testing.T) {
	testStr := `<?php
    echo "hello world", "!";`
	p := NewParser(testStr)
	a, _ := p.Parse()
	tree := ast.Echo(
		&ast.Literal{Type: ast.String, Value: `"hello world"`},
		&ast.Literal{Type: ast.String, Value: `"!"`},
	)
	if !assertEquals(a[0], tree) {
		t.Fatalf("Hello world did not correctly parse")
	}
}
Example #9
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 #10
0
func TestComments(t *testing.T) {
	testStr := `<?
  // comment line
  /*
  block
  */
  #line ?>html`
	tree := []ast.Node{
		ast.Echo(ast.Literal{Type: ast.String, Value: "html"}),
	}
	p := NewParser(testStr)
	a, _ := p.Parse()
	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 #11
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 #12
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 #13
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 #14
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")
	}
}
Example #15
0
func TestExpressionParsing(t *testing.T) {
	p := NewParser(`<? if (1 + 2 > 3)
    echo "good"; `)
	a, _ := p.Parse()
	ifStmt := ast.IfStmt{
		Condition: ast.OperatorExpression{
			Operand1: ast.OperatorExpression{
				Operand1: &ast.Literal{Type: ast.Float, Value: "1"},
				Operand2: &ast.Literal{Type: ast.Float, Value: "2"},
				Type:     ast.Numeric,
				Operator: "+",
			},
			Operand2: &ast.Literal{Type: ast.Float, Value: "3"},
			Type:     ast.Boolean,
			Operator: ">",
		},
		TrueBranch:  ast.Echo(&ast.Literal{Type: ast.String, Value: `"good"`}),
		FalseBranch: ast.Block{},
	}
	if len(a) != 1 {
		t.Fatalf("If did not correctly parse")
	}
	parsedIf, ok := a[0].(*ast.IfStmt)
	if !ok {
		t.Fatalf("If did not correctly parse")
	}
	if !assertEquals(*parsedIf, ifStmt) {
		t.Fatalf("If did not correctly parse")
	}

	p = NewParser(`<? if (4 + 5 * 6)
    echo "bad";
  `)
	a, _ = p.Parse()
	ifStmt = ast.IfStmt{
		Condition: ast.OperatorExpression{
			Operand2: ast.OperatorExpression{
				Operand1: &ast.Literal{Type: ast.Float, Value: "5"},
				Operand2: &ast.Literal{Type: ast.Float, Value: "6"},
				Type:     ast.Numeric,
				Operator: "*",
			},
			Operand1: &ast.Literal{Type: ast.Float, Value: "4"},
			Type:     ast.Numeric,
			Operator: "+",
		},
		TrueBranch:  ast.Echo(&ast.Literal{Type: ast.String, Value: `"bad"`}),
		FalseBranch: ast.Block{},
	}
	if len(a) != 1 {
		t.Fatalf("If did not correctly parse")
	}
	parsedIf, ok = a[0].(*ast.IfStmt)
	if !ok {
		t.Fatalf("If did not correctly parse")
	}
	if !reflect.DeepEqual(*parsedIf, ifStmt) {
		t.Fatalf("If did not correctly parse")
	}

	p = NewParser(`<? if (1 > 2 * 3 + 4)
    echo "good";
  `)
	a, _ = p.Parse()
	ifStmt = ast.IfStmt{
		Condition: ast.OperatorExpression{
			Operand1: &ast.Literal{Type: ast.Float, Value: `1`},
			Operand2: ast.OperatorExpression{
				Operand1: ast.OperatorExpression{
					Operand1: &ast.Literal{Type: ast.Float, Value: `2`},
					Operand2: &ast.Literal{Type: ast.Float, Value: `3`},
					Type:     ast.Numeric,
					Operator: "*",
				},
				Operand2: &ast.Literal{Type: ast.Float, Value: `4`},
				Operator: "+",
				Type:     ast.Numeric,
			},
			Type:     ast.Boolean,
			Operator: ">",
		},
		TrueBranch:  ast.Echo(&ast.Literal{Type: ast.String, Value: `"good"`}),
		FalseBranch: ast.Block{},
	}
	if len(a) != 1 {
		t.Fatalf("If did not correctly parse")
	}
	parsedIf, ok = a[0].(*ast.IfStmt)
	if !ok {
		t.Fatalf("If did not correctly parse")
	}
	if !reflect.DeepEqual(*parsedIf, ifStmt) {
		t.Fatalf("If did not correctly parse")
	}

	p = NewParser(`<? if ($var = &$var2 > 2 * (3 + 4) - 2 & 3 && 4 ^ 8 or 14 xor 10 and 13 >> 18 << 10 ? true : false)
    echo "good";
  `)
	p.Debug = true
	a, _ = p.Parse()
	if len(a) != 1 {
		t.Fatalf("Expression did not correctly parse")
	}
}