From 7b98b620ab20e116188610335b5704b7eb646c7c Mon Sep 17 00:00:00 2001 From: Drew Wells Date: Thu, 19 May 2016 01:59:00 -0500 Subject: [PATCH 1/3] these are not going to be fun --- compiler/spec_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/spec_test.go b/compiler/spec_test.go index a12de2a..81c4dd9 100644 --- a/compiler/spec_test.go +++ b/compiler/spec_test.go @@ -27,6 +27,10 @@ func findPaths() []file { // files := make([]file, len(inputs)) for _, input = range inputs { + if !strings.Contains(input, "37_") { + continue + } + // detailed commenting if strings.Contains(input, "06_") { continue @@ -57,7 +61,7 @@ func findPaths() []file { expect: exp, }) // Indicates the first test that will not pass tests - if strings.Contains(input, "35_") && testing.Short() { + if strings.Contains(input, "37_") && testing.Short() { break } From 95678e95d92d10bebebd97f8ccaf9e5921181718 Mon Sep 17 00:00:00 2001 From: Drew Wells Date: Tue, 24 May 2016 17:48:16 -0500 Subject: [PATCH 2/3] add support for undefined functions ie. fn("a") --- ast/copy.go | 3 ++ ast/utils.go | 14 +++++++++ compiler/builtin_test.go | 11 +++++++ compiler/spec_test.go | 2 ++ parser/builtin.go | 4 --- parser/parser.go | 66 +++++++++++++++++++++++++++++++++++----- parser/spec_test.go | 2 +- 7 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 ast/utils.go diff --git a/ast/copy.go b/ast/copy.go index 0b3683e..01fc776 100644 --- a/ast/copy.go +++ b/ast/copy.go @@ -150,6 +150,9 @@ func ExprCopy(in Expr) (out Expr) { } lit.Value = ExprsCopy(expr.Value) out = lit + // Be sure you know what you are doing before copying + // new types. More than likely, the type needs to be + // resolved to a simpler type before copying is necessary. default: panic(fmt.Errorf("unsupported expr copy: % #v\n", expr)) } diff --git a/ast/utils.go b/ast/utils.go new file mode 100644 index 0000000..854ce9f --- /dev/null +++ b/ast/utils.go @@ -0,0 +1,14 @@ +package ast + +import "strings" + +// JoinLits accepts a series of lits and optional separator to +// create a string. It's possible this outputs improper output +// for compiler settings +func JoinLits(a []*BasicLit, sep string) string { + s := make([]string, len(a)) + for i := range a { + s[i] = a[i].Value + } + return strings.Join(s, sep) +} diff --git a/compiler/builtin_test.go b/compiler/builtin_test.go index 7889e84..ac2e01b 100644 --- a/compiler/builtin_test.go +++ b/compiler/builtin_test.go @@ -88,3 +88,14 @@ func TestBuiltin_nth(t *testing.T) { ` runParse(t, in, e) } + +func TestBuiltin_url_expression(t *testing.T) { + in := `$x: a b; + div { + c: url(fn($x)); + }` + e := `div { + c: url(fn(a b)); } +` + runParse(t, in, e) +} diff --git a/compiler/spec_test.go b/compiler/spec_test.go index 81c4dd9..8bd6187 100644 --- a/compiler/spec_test.go +++ b/compiler/spec_test.go @@ -27,6 +27,7 @@ func findPaths() []file { // files := make([]file, len(inputs)) for _, input = range inputs { + // Force a single test to run if !strings.Contains(input, "37_") { continue } @@ -60,6 +61,7 @@ func findPaths() []file { input: input, expect: exp, }) + // Indicates the first test that will not pass tests if strings.Contains(input, "37_") && testing.Short() { break diff --git a/parser/builtin.go b/parser/builtin.go index 1078c67..9ed957a 100644 --- a/parser/builtin.go +++ b/parser/builtin.go @@ -124,7 +124,6 @@ func evaluateCall(p *parser, scope *ast.Scope, expr *ast.CallExpr) (ast.Expr, er // callInline looks for the function within Sass itself func (p *parser) callInline(scope *ast.Scope, call *ast.CallExpr) (ast.Expr, error) { - return p.resolveFuncDecl(scope, call) } @@ -135,9 +134,6 @@ func callBuiltin(name string, fn call, expr *ast.CallExpr) (ast.Expr, error) { callargs := make([]ast.Expr, len(fn.params)) for i := range fn.params { expr := fn.params[i].Value - // if expr != nil { - // callargs[i] = expr.(*ast.BasicLit) - // } callargs[i] = expr } var argpos int diff --git a/parser/parser.go b/parser/parser.go index e404fe2..490f63c 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1073,13 +1073,14 @@ func (p *parser) mergeInterps(in []ast.Expr) []ast.Expr { if l.End() == lit.Pos() { prev, ok := out[len(out)-1].(*ast.Interp) if !ok { - panic(fmt.Errorf("\nl:% #v\nr:% #v\n", - l, lit)) + // panic(fmt.Errorf("\nl:% #v\nr:% #v\n", + // l, lit)) + } else { + prev.X = append(prev.X, lit) + // changes to interp require resolution + p.resolveInterp(p.topScope, prev) + continue } - prev.X = append(prev.X, lit) - // changes to interp require resolution - p.resolveInterp(p.topScope, prev) - continue } } out = append(out, in[i]) @@ -1730,7 +1731,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { obj := ast.NewObj(ast.Var, ident.Name) obj.Decl = lit ident.Obj = obj - if err != nil { + if err != nil && err != ErrFuncNotFound { p.error(pos, err.Error()) } } @@ -3146,6 +3147,21 @@ func (p *parser) resolveExpr(scope *ast.Scope, expr ast.Expr) (out []*ast.BasicL assert(p.topScope == scope, "resolveExpr scope mismatch") switch v := expr.(type) { + case *ast.StringExpr: + + // This is pretty shitty + var list []*ast.BasicLit + for i := range v.List { + list = append(list, p.resolveExpr(scope, v.List[i])...) + } + + s := ast.JoinLits(list, "") + + out = append(out, &ast.BasicLit{ + Kind: token.STRING, + ValuePos: v.Pos(), + Value: `"` + s + `"`, + }) case *ast.BasicLit: out = append(out, v) case *ast.CallExpr: @@ -3261,11 +3277,45 @@ func joinLits(a []*ast.BasicLit, sep string) string { return strings.Join(s, sep) } +// ErrFuncNotFound in most cases, this is a user or fatal parsing +// error. However, URL expressions are dicks and allow this. +var ErrFuncNotFound = errors.New("named function was not found") + +var tries = 0 + +// resolveNoFuncDecl accepts callexpr to undefined functions and +// does the requisitie string shit to correctly output things +func (p *parser) resolveNoFuncDecl(scope *ast.Scope, call *ast.CallExpr) (ast.Expr, error) { + ident := call.Fun.(*ast.Ident) + // When resolution fails, we just poop out raw call + // as text + ss := make([]string, 0, 4) + ss = append(ss, ident.Name, "(") + + var args []string + for i := range call.Args { + lits := p.resolveExpr(scope, call.Args[i]) + args = append(args, ast.JoinLits(lits, "")) + } + ss = append(ss, strings.Join(args, ", ")) + + ss = append(ss, ")") + lit := &ast.BasicLit{ + Kind: token.STRING, + Value: strings.Join(ss, ""), + ValuePos: ident.NamePos, + } + return lit, ErrFuncNotFound +} + func (p *parser) resolveFuncDecl(scope *ast.Scope, call *ast.CallExpr) (ast.Expr, error) { ident := call.Fun.(*ast.Ident) p.tryResolve(ident, false) - assert(ident.Obj != nil, "failed to locate function: "+ident.Name) + if ident.Obj == nil { + return p.resolveNoFuncDecl(scope, call) + } + args := call.Args fnDecl := ident.Obj.Decl.(*ast.FuncDecl) diff --git a/parser/spec_test.go b/parser/spec_test.go index 6049844..6671303 100644 --- a/parser/spec_test.go +++ b/parser/spec_test.go @@ -20,7 +20,7 @@ func TestSpec_files(t *testing.T) { mode = Trace | ParseComments var name string for _, name = range inputs { - if strings.Contains(name, "25_") && testing.Short() { + if strings.Contains(name, "36_") && testing.Short() { // This is the last test we currently parse properly return } From c1fe57a276abd8eae64eb91529fc47547441e115 Mon Sep 17 00:00:00 2001 From: Drew Wells Date: Tue, 24 May 2016 23:36:31 -0500 Subject: [PATCH 3/3] new specs for test 37 --- compiler/control_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 compiler/control_test.go diff --git a/compiler/control_test.go b/compiler/control_test.go new file mode 100644 index 0000000..55f6046 --- /dev/null +++ b/compiler/control_test.go @@ -0,0 +1,29 @@ +package compiler + +import "testing" + +func TestControl_inline_if(t *testing.T) { + in := `div { + blah: if($x, ("red.png", blah), "blue.png"); +}` + e := `div { + blah: "red.png", blah; } +` + runParse(t, in, e) +} + +func TestControl_interp_if(t *testing.T) { + in := `$file-1x: "budge.png"; + +@function fudge($str) { + @return "assets/fudge/" + $str; +} + +div { + blah: if($x, fudge("#{$file-1x}"), "#{$file-1x}"); +}` + e := `div { + blah: "assets/fudge/budge.png"; } +` + runParse(t, in, e) +}