Exemple #1
0
/**

We here treat the property values as expressions:

	padding: {expression} {expression} {expression};
	margin: {expression};

*/
func (parser *Parser) ParseExpr(inParenthesis bool) ast.Expr {
	var pos = parser.Pos

	// plus or minus. This creates an unary expression that holds the later term.
	// this is for:  +3 or -4
	var expr ast.Expr = nil

	if tok := parser.acceptAnyOf2(ast.T_PLUS, ast.T_MINUS); tok != nil {
		if term := parser.ParseTerm(); term != nil {
			expr = ast.NewUnaryExpr(ast.NewOpWithToken(tok), term)

			if uexpr, ok := expr.(*ast.UnaryExpr); ok {

				// if it's evaluatable just return the evaluated value.
				if val, ok := runtime.ReduceExpr(uexpr, parser.GlobalContext); ok {
					expr = ast.Expr(val)
				}
			}
		} else {
			parser.restore(pos)
			return nil
		}
	} else {
		expr = parser.ParseTerm()
	}

	if expr == nil {
		debug("ParseExpr failed, got %+v, restoring to %d", expr, pos)
		parser.restore(pos)
		return nil
	}

	var rightTok = parser.peek()
	for rightTok.Type == ast.T_PLUS || rightTok.Type == ast.T_MINUS || rightTok.Type == ast.T_LITERAL_CONCAT {
		// accept plus or minus
		parser.advance()

		if rightTerm := parser.ParseTerm(); rightTerm != nil {
			// XXX: check parenthesis
			var bexpr = ast.NewBinaryExpr(ast.NewOpWithToken(rightTok), expr, rightTerm, inParenthesis)

			if val, ok := runtime.ReduceExpr(bexpr, parser.GlobalContext); ok {

				expr = ast.Expr(val)

			} else {
				// wrap the existing expression with the new binary expression object
				expr = ast.Expr(bexpr)
			}
		} else {
			panic(SyntaxError{
				Reason:      "Expecting term on the right side",
				ActualToken: parser.peek(),
				File:        parser.File,
			})
		}
		rightTok = parser.peek()
	}
	return expr
}
Exemple #2
0
/*
Parse string literal expression (literal concat with interpolation)
*/
func (parser *Parser) ParseLiteralExpr() ast.Expr {
	if expr := parser.ParseExpr(false); expr != nil {
		for tok := parser.accept(ast.T_LITERAL_CONCAT); tok != nil; tok = parser.accept(ast.T_LITERAL_CONCAT) {
			var rightExpr = parser.ParseExpr(false)
			if rightExpr == nil {
				panic(SyntaxError{
					Reason:      "Expecting expression or ident after the literal concat operator.",
					ActualToken: parser.peek(),
					File:        parser.File,
				})
			}
			expr = ast.NewLiteralConcat(expr, rightExpr)
		}

		// Check if the expression is reduce-able
		// For now, division looks like CSS slash at the first level, should be string.
		if runtime.CanReduceExpr(expr) {
			if reducedExpr, ok := runtime.ReduceExpr(expr, parser.GlobalContext); ok {
				return reducedExpr
			}
		} else {
			// Return expression as css slash syntax string
			// TODO: re-visit here later
			return runtime.EvaluateExpr(expr, parser.GlobalContext)
		}

		// if we can't evaluate the value, just return the expression tree
		return expr
	}
	return nil
}
Exemple #3
0
func (parser *Parser) ParseAssignStmt() ast.Stmt {
	var variable = parser.ParseVariable()

	// skip ":", T_COLON token
	parser.expect(ast.T_COLON)

	// Expecting semicolon at the end of the statement
	var valExpr = parser.ParseValue(ast.T_SEMICOLON)
	if valExpr == nil {
		panic(SyntaxError{
			Reason:      "Expecting value after variable assignment.",
			ActualToken: parser.peek(),
			File:        parser.File,
		})
	}

	// Optimize the expression only when it's an expression
	// TODO: for expression inside a map or list we should also optmise them too
	if bexpr, ok := valExpr.(ast.BinaryExpr); ok {
		if reducedExpr, ok := runtime.ReduceExpr(bexpr, parser.GlobalContext); ok {
			valExpr = reducedExpr
		}
	} else if uexpr, ok := valExpr.(ast.UnaryExpr); ok {
		if reducedExpr, ok := runtime.ReduceExpr(uexpr, parser.GlobalContext); ok {
			valExpr = reducedExpr
		}
	}

	// FIXME
	// Even we can visit the variable assignment in the AST visitors but if we
	// could save the information, we can reduce the effort for the visitors.
	/*
		if currentBlock := parser.GlobalContext.CurrentBlock(); currentBlock != nil {
			currentBlock.GetSymTable().Set(variable.Name, valExpr)
		} else {
			panic("nil block")
		}
	*/

	var stm = ast.NewAssignStmt(variable, valExpr)
	parser.ParseFlags(stm)
	parser.accept(ast.T_SEMICOLON)
	return stm
}
Exemple #4
0
/*
Parse the SASS @for statement.

	@for $var from <start> to <end> {  }

	@for $var from <start> through <end> {  }

@see http://sass-lang.com/documentation/file.SASS_REFERENCE.html#_10
*/
func (parser *Parser) ParseForStmt() ast.Stmt {
	parser.expect(ast.T_FOR)

	// get the variable token
	var variable = parser.ParseVariable()
	var stm = ast.NewForStmt(variable)

	if parser.accept(ast.T_FOR_FROM) != nil {

		var fromExpr = parser.ParseExpr(true)
		if reducedExpr, ok := runtime.ReduceExpr(fromExpr, parser.GlobalContext); ok {
			fromExpr = reducedExpr
		}
		stm.From = fromExpr

		// "through" or "to"
		var tok = parser.next()

		if tok.Type != ast.T_FOR_THROUGH && tok.Type != ast.T_FOR_TO {
			panic(SyntaxError{
				Reason:      "Expecting 'through' or 'to' of range syntax.",
				ActualToken: tok,
				File:        parser.File,
			})
		}

		var endExpr = parser.ParseExpr(true)
		if reducedExpr, ok := runtime.ReduceExpr(endExpr, parser.GlobalContext); ok {
			endExpr = reducedExpr
		}

		if tok.Type == ast.T_FOR_THROUGH {

			stm.Through = endExpr

		} else if tok.Type == ast.T_FOR_TO {

			stm.To = endExpr

		}

	} else if parser.accept(ast.T_FOR_IN) != nil {

		var fromExpr = parser.ParseExpr(true)
		if reducedExpr, ok := runtime.ReduceExpr(fromExpr, parser.GlobalContext); ok {
			fromExpr = reducedExpr
		}
		stm.From = fromExpr

		parser.expect(ast.T_RANGE)

		var endExpr = parser.ParseExpr(true)
		if reducedExpr, ok := runtime.ReduceExpr(endExpr, parser.GlobalContext); ok {
			endExpr = reducedExpr
		}

		stm.To = endExpr
	}

	if b := parser.ParseDeclBlock(); b != nil {
		stm.Block = b
	} else {
		panic("The @for statement expecting block after the range syntax")
	}
	return stm
}