コード例 #1
0
ファイル: parser.go プロジェクト: reflexionhealth/vanilla
func (p *Parser) parseExprWithOperators(precedence ast.OpPrecedence) ast.Expr {
	lhs := p.parseBaseExpression()
	if p.tok == token.LEFT_PAREN {
		// TODO: functions like MAX(), MIN(), AVERAGE()
		p.eatUnimplemented("expression")
	} else if !p.tok.IsOperator() {
		return lhs
	}

	op, exists := p.rules.Operators.Lookup(p.tok.String(), ast.Infix)
	if !exists {
		msg := `statement includes '` + p.tok.String() + `', but it is not defined as an operator`
		p.error(p.scanner.Pos(), msg)
	}

	consumable := ast.MaxPrecedence
	for (op.Kind == ast.Infix) &&
		(precedence <= op.Precedence && op.Precedence <= consumable) {

		p.next() // eat operator
		rhs := p.parseExprWithOperators(rightPrec(op))
		lhs = ast.Binary(lhs, op.Type, rhs)

		if p.tok.IsOperator() {
			var exists bool
			op, exists = p.rules.Operators.Lookup(p.tok.String(), ast.Infix)
			if !exists {
				msg := `statement includes '` + p.tok.String() + `', but it is not defined as an operator`
				p.error(p.scanner.Pos(), msg)
			}
			consumable = nextPrec(op)
		} else {
			break
		}
	}

	return lhs
}
コード例 #2
0
func TestParseSelect(t *testing.T) {
	examples := []struct {
		Input  string
		Rules  Ruleset
		Result ast.Stmt
		Trace  bool // for debugging
	}{
		{Input: `SELECT * FROM mytable`, // basics
			Result: &ast.SelectStmt{
				Type: ast.SELECT_ALL,
				From: ast.Name("mytable"),
				Star: true,
			}},
		{Input: `SELECT * FROM mytable;`, // with semicolon
			Result: &ast.SelectStmt{
				Type: ast.SELECT_ALL,
				From: ast.Name("mytable"),
				Star: true,
			}},
		{Input: `SELECT * FROM "mytable"`, // doublequotes (ansi)
			Result: &ast.SelectStmt{
				Type: ast.SELECT_ALL,
				From: ast.Quoted("mytable"),
				Star: true,
			}},
		{Input: "SELECT * FROM `mytable`", // backticks (mysql)
			Rules: MysqlRuleset,
			Result: &ast.SelectStmt{
				Type: ast.SELECT_ALL,
				From: ast.Quoted("mytable"),
				Star: true,
			}},
		{Input: `SELECT foo, bar FROM mytable`, // with columns
			Result: &ast.SelectStmt{
				Type:   ast.SELECT_ALL,
				From:   ast.Name("mytable"),
				Select: []ast.Expr{ast.Name("foo"), ast.Name("bar")},
			}},
		{Input: `SELECT "foo", "bar" FROM mytable`, // with quoted columns
			Result: &ast.SelectStmt{
				Type:   ast.SELECT_ALL,
				From:   ast.Name("mytable"),
				Select: []ast.Expr{ast.Quoted("foo"), ast.Quoted("bar")},
			}},
		{Input: `SELECT ALL * FROM mytable`, // ALL
			Result: &ast.SelectStmt{
				Type: ast.SELECT_ALL,
				From: ast.Name("mytable"),
				Star: true,
			}},
		{Input: `SELECT DISTINCT * FROM mytable`, // DISTINCT
			Result: &ast.SelectStmt{
				Type: ast.DISTINCT,
				From: ast.Name("mytable"),
				Star: true,
			}},
		{Input: `SELECT DISTINCTROW * FROM mytable`, // DISTINCT ROW (mysql)
			Rules: MysqlRuleset,
			Result: &ast.SelectStmt{
				Type: ast.DISTINCT_ROW,
				From: ast.Name("mytable"),
				Star: true,
			}},
		{Input: `SELECT * FROM mytable WHERE id = 3`, // simple WHERE clause
			Rules: AnsiRuleset,
			Result: &ast.SelectStmt{
				Type:  ast.SELECT_ALL,
				Star:  true,
				From:  ast.Name("mytable"),
				Where: ast.Binary(ast.Name("id"), ast.EQUAL, ast.Lit("3")),
			}},

		// a WHERE clause with nested expressions (binary and unary)
		{Input: `SELECT * FROM mytable WHERE kind != "muppet" AND 5 < -size`,
			Rules: MysqlRuleset,
			Result: &ast.SelectStmt{
				Type: ast.SELECT_ALL,
				Star: true,
				From: ast.Name("mytable"),
				Where: ast.Binary(
					ast.Binary(ast.Name("kind"), ast.NOT_EQUAL, ast.Lit(`"muppet"`)),
					ast.AND,
					ast.Binary(ast.Lit("5"), ast.LESS, ast.Unary(ast.NEGATE, ast.Name("size"))),
				),
			}},

		// allow table-less select if someone says its ok
		{Input: `SELECT *`, // TODO: eventually I'd like this to be `SELECT 1+1;`
			Rules:  Ruleset{CanSelectWithoutFrom: true},
			Result: &ast.SelectStmt{Type: ast.SELECT_ALL, Star: true}},

		// allow unimplmented clauses if someone says its ok
		{Input: `SELECT * FROM mytable PROCEDURE compute(foo)`,
			Rules: Ruleset{AllowNotImplemented: true},
			Result: &ast.SelectStmt{
				Type: ast.SELECT_ALL,
				From: ast.Name("mytable"),
				Star: true,
			}},
	}

	for _, example := range examples {
		parser := New([]byte(example.Input), example.Rules)
		if example.Trace {
			parser.Trace = os.Stdout
		}
		stmt, err := parser.ParseStatement()
		expect.Nil(t, err, "Error for `"+example.Input+"`")
		expect.Equal(t, stmt, example.Result, example.Input)
	}
}