pax_global_header00006660000000000000000000000064140521441360014512gustar00rootroot0000000000000052 comment=46bd6384ed47f5ba54daee2d571842e01317c064 golibsass-1.0.0/000077500000000000000000000000001405214413600134765ustar00rootroot00000000000000golibsass-1.0.0/.github/000077500000000000000000000000001405214413600150365ustar00rootroot00000000000000golibsass-1.0.0/.github/FUNDING.yml000066400000000000000000000000171405214413600166510ustar00rootroot00000000000000 github: [bep] golibsass-1.0.0/.gitignore000066400000000000000000000000471405214413600154670ustar00rootroot00000000000000cover*.out *.test *.prof /.idea gen/gengolibsass-1.0.0/.gitmodules000066400000000000000000000000001405214413600156410ustar00rootroot00000000000000golibsass-1.0.0/.travis.yml000066400000000000000000000022111405214413600156030ustar00rootroot00000000000000language: go dist: bionic env: global: - CACHE_NAME=${TRAVIS_ARCH} - GO111MODULE=on - GOPROXY=https://proxy.golang.org git: depth: false go: - "1.13.x" - "1.14.x" - master arch: - amd64 - arm64 os: - linux - osx - windows jobs: allow_failures: - go: master fast_finish: true exclude: - os: windows go: master - arch: arm64 os: osx - arch: arm64 os: windows cache: directories: - $HOME/gopath/pkg/mod - $HOME/.cache/go-build - $HOME/Library/Caches/go-build - $HOME/AppData/Local/go-build before_install: # https://travis-ci.community/t/go-cant-find-gcc-with-go1-11-1-on-windows/293/5 - if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install mingw -y; export PATH=/c/tools/mingw64/bin:"$PATH"; fi install: - mkdir -p $HOME/src - mv $TRAVIS_BUILD_DIR $HOME/src - export TRAVIS_BUILD_DIR=$HOME/src/golibsass - cd $HOME/src/golibsass script: - go mod download || true - go vet ./... || true - go test -race ./libsass -coverprofile=coverage.txt -covermode=atomic after_success: - bash <(curl -s https://codecov.io/bash)golibsass-1.0.0/LICENSE000066400000000000000000000020651405214413600145060ustar00rootroot00000000000000MIT License Copyright (c) 2020 Bjørn Erik Pedersen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golibsass-1.0.0/NOTES000066400000000000000000000025731405214413600143200ustar00rootroot00000000000000This project includes the LibSass source as a Git subtree. For the LibSass license: https://github.com/sass/libsass/blob/master/LICENSE Copyright (C) 2012-2016 by the Sass Open Source Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following files in the spec were taken from the original Ruby Sass project which is copyright Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein and under the same license.golibsass-1.0.0/README.md000066400000000000000000000037221405214413600147610ustar00rootroot00000000000000 [![Build Status](https://travis-ci.org/bep/golibsass.svg?branch=master)](https://travis-ci.org/bep/golibsass) [![Go Report Card](https://goreportcard.com/badge/github.com/bep/golibsass)](https://goreportcard.com/report/github.com/bep/golibsass) [![LibSass Version](https://img.shields.io/badge/LibSass-v3.6.5-blue)](https://github.com/sass/libsass) [![codecov](https://codecov.io/gh/bep/golibsass/branch/master/graph/badge.svg)](https://codecov.io/gh/bep/golibsass) [![GoDoc](https://godoc.org/github.com/bep/golibsass/libsass?status.svg)](https://godoc.org/github.com/bep/golibsass/libsass) The primary motivation for this project is to provide `SCSS` support to [Hugo](https://gohugo.io/). I welcome PRs with bug fixes. I will also consider adding functionality, but please raise an issue discussing it first. If you need more functionality than this project can provide you may want to have a look at [go-libsass](https://github.com/wellington/go-libsass). ## Usage A basic example (error handling omitted): ```go transpiler, _ := libsass.New(libsass.Options{OutputStyle: libsass.CompressedStyle}) result, _ := transpiler.Execute(` $font-stack: Helvetica, sans-serif; $primary-color: #333; body { font: 100% $font-stack; color: $primary-color; } `) fmt.Println(result.CSS) // Output: body{font:100% Helvetica,sans-serif;color:#333} ``` See the [GoDoc](https://godoc.org/github.com/bep/golibsass/libsass) for more options. ## Update LibSass version This project embeds the [LibSASS](https://github.com/sass/libsass) source code as a Git subtree. To update: 1. Pull in the relevant LibSASS version, e.g. `./pull-libsass.sh 3.6.3` 2. Regenerate wrappers with `go generate ./gen` 3. Update the LibSass version badge above. ## Local development Compiling C++ code isn' particulary fast; if you install libsass on your PC you can link against that, useful during development. On a Mac you may do something like: ```bash brew install --HEAD libsass go test ./libsass -tags dev ``` golibsass-1.0.0/codecov.yml000066400000000000000000000002161405214413600156420ustar00rootroot00000000000000coverage: status: project: default: target: auto threshold: 0.5% patch: off comment: require_changes: true golibsass-1.0.0/gen/000077500000000000000000000000001405214413600142475ustar00rootroot00000000000000golibsass-1.0.0/gen/main.go000066400000000000000000000023401405214413600155210ustar00rootroot00000000000000//go:generate go run main.go package main import ( "fmt" "io/ioutil" "log" "os" "path" "path/filepath" "regexp" "runtime" ) func main() { _, filename, _, ok := runtime.Caller(0) if !ok { panic("runtime err") } rootDir := path.Join(path.Dir(filename), "..") dstDir := filepath.Join(rootDir, "internal/libsass") srcDir := filepath.Join(rootDir, "libsass_src", "src") // The Go and the Libsass C++ source must live side-by-side in the same // directory. // // The custom bindings are named with a "a__" prefix. Keep those. fis, err := ioutil.ReadDir(dstDir) if err != nil { log.Fatal(err) } keepRe := regexp.MustCompile(`^(a__|\.)`) for _, fi := range fis { if keepRe.MatchString(fi.Name()) { continue } os.Remove(filepath.Join(dstDir, fi.Name())) } fis, err = ioutil.ReadDir(srcDir) if err != nil { log.Fatal(err) } csourceRe := regexp.MustCompile(`\.[ch](pp)?$`) for _, fi := range fis { if fi.IsDir() || !csourceRe.MatchString(fi.Name()) { continue } target := filepath.Join(dstDir, fi.Name()) if err := ioutil.WriteFile(target, []byte(fmt.Sprintf(`#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/%s" #endif `, fi.Name())), 0644); err != nil { log.Fatal(err) } } } golibsass-1.0.0/go.mod000066400000000000000000000001271405214413600146040ustar00rootroot00000000000000module github.com/bep/golibsass require github.com/frankban/quicktest v1.7.2 go 1.13 golibsass-1.0.0/go.sum000066400000000000000000000013421405214413600146310ustar00rootroot00000000000000github.com/frankban/quicktest v1.7.2 h1:2QxQoC1TS09S7fhCPsrvqYdvP1H5M1P1ih5ABm3BTYk= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= golibsass-1.0.0/internal/000077500000000000000000000000001405214413600153125ustar00rootroot00000000000000golibsass-1.0.0/internal/libsass/000077500000000000000000000000001405214413600167525ustar00rootroot00000000000000golibsass-1.0.0/internal/libsass/MurmurHash2.hpp000066400000000000000000000001201405214413600216310ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/MurmurHash2.hpp" #endif golibsass-1.0.0/internal/libsass/a__cgo.go000066400000000000000000000006111405214413600205060ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // package libsass // #cgo CFLAGS: -O2 -fPIC // #cgo CPPFLAGS: -I../../libsass_src/include // #cgo CXXFLAGS: -g -std=c++0x -O2 -fPIC // #cgo LDFLAGS: -lstdc++ -lm // #cgo darwin linux LDFLAGS: -ldl import "C" golibsass-1.0.0/internal/libsass/a__cgo_dev.go000066400000000000000000000004411405214413600213450ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // // +build dev package libsass // #cgo CPPFLAGS: -DUSE_LIBSASS_SRC // #cgo LDFLAGS: -lsass import "C" golibsass-1.0.0/internal/libsass/a__importer.go000066400000000000000000000050711405214413600216040ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // package libsass // #include // #include "sass/context.h" // // extern struct Sass_Import** BridgeImport(const char* currPath, const char* prevPath, int i); // // Sass_Import_List SassImport(const char* currPath, Sass_Importer_Entry imp, struct Sass_Compiler* comp) // { // void* c = sass_importer_get_cookie(imp); // uintptr_t ci = (uintptr_t)c; // struct Sass_Import* prevPath = sass_compiler_get_last_import(comp); // const char* prev_path = sass_import_get_imp_path(prevPath); // return BridgeImport(currPath, prev_path, ci); // } import "C" import ( "sync" "unsafe" ) const uintptrOffset = 4096 // the smallest legal pointer var importsStore = &idMap{ m: make(map[int]interface{}), i: uintptrOffset, } // AddImportResolver adds a function to resolve imports in LibSASS. // Make sure to run call DeleteImportResolver when done. //go:nocheckptr func AddImportResolver(opts SassOptions, resolver ImportResolver) int { i := importsStore.Set(resolver) // This looks unsafe, but LibSass is using void* to store an int. // TODO(bep) this prevents us from "fail on go vet errors" in Travis. id := unsafe.Pointer(uintptr(i)) importers := C.sass_make_importer_list(1) C.sass_importer_set_list_entry( importers, 0, C.sass_make_importer( C.Sass_Importer_Fn(C.SassImport), C.double(0), id, ), ) C.sass_option_set_c_importers( (*C.struct_Sass_Options)(unsafe.Pointer(opts)), importers, ) return i } func DeleteImportResolver(i int) error { importsStore.Delete(i) return nil } // ImportResolver can be used as a custom import resolver. // Return an empty body to load the import body from the path. // See AddImportResolver. type ImportResolver func(currPath string, prevPath string) (newPath string, body string, resolved bool) type idMap struct { sync.RWMutex m map[int]interface{} i int idStack []int } func (m *idMap) Delete(i int) { m.Lock() defer m.Unlock() m.idStack = append(m.idStack, i) delete(m.m, i) } func (m *idMap) Get(i int) interface{} { m.RLock() defer m.RUnlock() return m.m[i] } func (m *idMap) nextID() (id int) { if len(m.idStack) == 0 { for i := 1; i <= 50; i++ { m.idStack = append(m.idStack, m.i+i) } m.i += 50 } id, m.idStack = m.idStack[len(m.idStack)-1], m.idStack[:len(m.idStack)-1] return } func (m *idMap) Set(v interface{}) int { m.Lock() defer m.Unlock() id := m.nextID() m.m[id] = v return id } golibsass-1.0.0/internal/libsass/a__libsass.go000066400000000000000000000143031405214413600214010ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // package libsass // #include "stdlib.h" // #include "sass/context.h" // #include "sass2scss.h" import "C" import ( "reflect" "unsafe" ) // A bridge function to C to resolve imports. // //export BridgeImport func BridgeImport(currPath, prevPath *C.char, ci C.int) C.Sass_Import_List { parent := C.GoString(prevPath) rel := C.GoString(currPath) clist := C.sass_make_import_list(1) h := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(clist)), Len: 1, Cap: 1, } golist := *(*[]C.Sass_Import_Entry)(unsafe.Pointer(&h)) resolver, ok := importsStore.Get(int(ci)).(ImportResolver) if ok { npath, body, ok := resolver(rel, parent) if ok { var bodyv *C.char // nil signals loading from the path. if body != "" { bodyv = C.CString(body) } entry := C.sass_make_import_entry(C.CString(npath), bodyv, nil) centry := (C.Sass_Import_Entry)(entry) golist[0] = centry return clist } } ent := C.sass_make_import_entry(currPath, nil, nil) cent := (C.Sass_Import_Entry)(ent) golist[0] = cent return clist } // SassCompilerExecute function as declared in sass/context.h:48 func SassCompilerExecute(compiler SassCompiler) { C.sass_compiler_execute(compiler) } // SassCompilerParse function as declared in sass/context.h:47 func SassCompilerParse(compiler SassCompiler) { C.sass_compiler_parse(compiler) } // SassContextGetErrorJSON function as declared in sass/context.h:115 func SassContextGetErrorJSON(ctx SassContext) string { s := C.sass_context_get_error_json(ctx) defer C.free(unsafe.Pointer(s)) return C.GoString(s) } // SassContextGetErrorStatus function as declared in sass/context.h:114 func SassContextGetErrorStatus(ctx SassContext) int { return int(C.sass_context_get_error_status(ctx)) } func SassContextGetOutputString(ctx SassContext) string { s := C.sass_context_get_output_string(ctx) defer C.free(unsafe.Pointer(s)) return C.GoString(s) } // SassContextGetSourceMapString function as declared in sass/context.h:122 func SassContextGetSourceMapString(ctx SassContext) string { s := C.sass_context_get_source_map_string(ctx) return C.GoString(s) } // SassDataContextGetContext function as declared in sass/context.h:61 func SassDataContextGetContext(ctx SassDataContext) SassContext { return (SassContext)(C.sass_data_context_get_context(ctx)) } // SassDataContextGetOptions function as declared in sass/context.h:66 func SassDataContextGetOptions(ctx SassDataContext) SassOptions { return (SassOptions)(C.sass_data_context_get_options(ctx)) } // SassDataContextSetOptions function as declared in sass/context.h:68 func SassDataContextSetOptions(ctx SassDataContext, opt SassOptions) { C.sass_data_context_set_options(ctx, opt) } // SassDeleteCompiler function as declared in sass/context.h:52 func SassDeleteCompiler(compiler SassCompiler) { C.sass_delete_compiler(compiler) } // SassDeleteDataContext function as declared in sass/context.h:57 func SassDeleteDataContext(ctx SassDataContext) { C.sass_delete_data_context(ctx) } // SassDeleteFileContext function as declared in sass/context.h:56 func SassDeleteFileContext(ctx SassFileContext) { C.sass_delete_file_context(ctx) } // SassDeleteOptions function as declared in sass/context.h:53 func SassDeleteOptions(options SassOptions) { C.sass_delete_options(options) } // SassMakeDataCompiler function as declared in sass/context.h:43 func SassMakeDataCompiler(ctx SassDataContext) SassCompiler { return (SassCompiler)(C.sass_make_data_compiler(ctx)) } // SassMakeDataContext function as declared in sass/context.h:35 func SassMakeDataContext(s string) SassDataContext { ctx := C.sass_make_data_context(C.CString(s)) return (SassDataContext)(ctx) } // SassOptionGetSourceMapFile function as declared in sass/context.h:84 func SassOptionGetSourceMapFile(opts SassOptions) string { p := C.sass_option_get_source_map_file(opts) return C.GoString(p) } // SassOptionSetIncludePath function as declared in sass/context.h:104 func SassOptionSetIncludePath(o SassOptions, s string) { C.sass_option_set_include_path(o, C.CString(s)) } // SassOptionSetInputPath function as declared in sass/context.h:101 func SassOptionSetInputPath(o SassOptions, s string) { C.sass_option_set_input_path(o, C.CString(s)) } func SassOptionSetOmitSourceMapURL(o SassOptions, b bool) { C.sass_option_set_omit_source_map_url(o, C.bool(b)) } // SassOptionSetOmitSourceMapUrl function as declared in sass/context.h:97 func SassOptionSetOmitSourceMapUrl(o SassOptions, b bool) { C.sass_option_set_omit_source_map_url(o, C.bool(b)) } // SassOptionSetOutputPath function as declared in sass/context.h:102 func SassOptionSetOutputPath(o SassOptions, s string) { C.sass_option_set_output_path(o, C.CString(s)) } // SassOptionSetOutputStyle function as declared in sass/context.h:92 func SassOptionSetOutputStyle(o SassOptions, i int) { C.sass_option_set_output_style(o, uint32(i)) } // SassOptionGetPrecision function as declared in sass/context.h:91 func SassOptionSetPrecision(o SassOptions, i int) { C.sass_option_set_precision(o, C.int(i)) } // SassOptionSetSourceComments function as declared in sass/context.h:93 func SassOptionSetSourceComments(o SassOptions, b bool) { C.sass_option_set_source_comments(o, C.bool(b)) } // SassOptionSetSourceMapContents function as declared in sass/context.h:95 func SassOptionSetSourceMapContents(o SassOptions, b bool) { C.sass_option_set_source_map_contents(o, C.bool(b)) } // SassOptionSetSourceMapEmbed function as declared in sass/context.h:94 func SassOptionSetSourceMapEmbed(o SassOptions, b bool) { C.sass_option_set_source_map_embed(o, C.bool(b)) } func SassOptionSetSourceMapFile(o SassOptions, s string) { C.sass_option_set_source_map_file(o, C.CString(s)) } // SassOptionSetSourceMapRoot function as declared in sass/context.h:106 func SassOptionSetSourceMapRoot(o SassOptions, s string) { C.sass_option_set_source_map_root(o, C.CString(s)) } // SassToScss converts Sass to Scss using sass2scss. func SassToScss(src string) string { in := C.CString(src) defer C.free(unsafe.Pointer(in)) chars := C.sass2scss( in, C.int(1), ) return C.GoString(chars) } golibsass-1.0.0/internal/libsass/a__types.go000066400000000000000000000035011405214413600211030ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // package libsass // #include "stdint.h" // #include "stdlib.h" // #include "sass/context.h" import "C" import "unsafe" // SassValue as declared in sass/values.h:14 const sizeofSassValue = unsafe.Sizeof(C.union_Sass_Value{}) // SassCalleeEntry as declared in sass/functions.h:25 type SassCalleeEntry C.Sass_Callee_Entry // SassCompiler as declared in sass/functions.h:18 type SassCompiler *C.struct_Sass_Compiler // SassContext as declared in sass/context.h:20 type SassContext *C.struct_Sass_Context // SassDataContext as declared in sass/context.h:22 type SassDataContext *C.struct_Sass_Data_Context // SassEnvFrame as declared in sass/functions.h:23 type SassEnvFrame C.Sass_Env_Frame // SassFileContext as declared in sass/context.h:21 type SassFileContext *C.struct_Sass_File_Context // SassFunctionEntry as declared in sass/functions.h:37 type SassFunctionEntry C.Sass_Function_Entry // SassFunctionList as declared in sass/functions.h:38 type SassFunctionList C.Sass_Function_List // SassImportEntry as declared in sass/functions.h:27 type SassImportEntry C.Sass_Import_Entry // SassImportList as declared in sass/functions.h:28 type SassImportList C.Sass_Import_List // SassImporterEntry as declared in sass/functions.h:30 type SassImporterEntry C.Sass_Importer_Entry // SassImporterFn type as declared in sass/functions.h:33 type SassImporterFn func(url string, cb SassImporterEntry, compiler SassCompiler) SassImportList // SassImporterList as declared in sass/functions.h:31 type SassImporterList C.Sass_Importer_List // SassOptions as declared in sass/functions.h:17 type SassOptions *C.struct_Sass_Options type SassValue [sizeofSassValue]byte golibsass-1.0.0/internal/libsass/ast.cpp000066400000000000000000000001101405214413600202350ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast.cpp" #endif golibsass-1.0.0/internal/libsass/ast.hpp000066400000000000000000000001101405214413600202420ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast.hpp" #endif golibsass-1.0.0/internal/libsass/ast2c.cpp000066400000000000000000000001121405214413600204640ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast2c.cpp" #endif golibsass-1.0.0/internal/libsass/ast2c.hpp000066400000000000000000000001121405214413600204710ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast2c.hpp" #endif golibsass-1.0.0/internal/libsass/ast_def_macros.hpp000066400000000000000000000001231405214413600224300ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_def_macros.hpp" #endif golibsass-1.0.0/internal/libsass/ast_fwd_decl.cpp000066400000000000000000000001211405214413600220660ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_fwd_decl.cpp" #endif golibsass-1.0.0/internal/libsass/ast_fwd_decl.hpp000066400000000000000000000001211405214413600220730ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_fwd_decl.hpp" #endif golibsass-1.0.0/internal/libsass/ast_helpers.hpp000066400000000000000000000001201405214413600217650ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_helpers.hpp" #endif golibsass-1.0.0/internal/libsass/ast_sel_cmp.cpp000066400000000000000000000001201405214413600217400ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_sel_cmp.cpp" #endif golibsass-1.0.0/internal/libsass/ast_sel_super.cpp000066400000000000000000000001221405214413600223210ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_sel_super.cpp" #endif golibsass-1.0.0/internal/libsass/ast_sel_unify.cpp000066400000000000000000000001221405214413600223150ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_sel_unify.cpp" #endif golibsass-1.0.0/internal/libsass/ast_sel_weave.cpp000066400000000000000000000001221405214413600222720ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_sel_weave.cpp" #endif golibsass-1.0.0/internal/libsass/ast_selectors.cpp000066400000000000000000000001221405214413600223230ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_selectors.cpp" #endif golibsass-1.0.0/internal/libsass/ast_selectors.hpp000066400000000000000000000001221405214413600223300ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_selectors.hpp" #endif golibsass-1.0.0/internal/libsass/ast_supports.cpp000066400000000000000000000001211405214413600222160ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_supports.cpp" #endif golibsass-1.0.0/internal/libsass/ast_supports.hpp000066400000000000000000000001211405214413600222230ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_supports.hpp" #endif golibsass-1.0.0/internal/libsass/ast_values.cpp000066400000000000000000000001171405214413600216230ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_values.cpp" #endif golibsass-1.0.0/internal/libsass/ast_values.hpp000066400000000000000000000001171405214413600216300ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ast_values.hpp" #endif golibsass-1.0.0/internal/libsass/backtrace.cpp000066400000000000000000000001161405214413600213730ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/backtrace.cpp" #endif golibsass-1.0.0/internal/libsass/backtrace.hpp000066400000000000000000000001161405214413600214000ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/backtrace.hpp" #endif golibsass-1.0.0/internal/libsass/base64vlq.cpp000066400000000000000000000001161405214413600212630ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/base64vlq.cpp" #endif golibsass-1.0.0/internal/libsass/base64vlq.hpp000066400000000000000000000001161405214413600212700ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/base64vlq.hpp" #endif golibsass-1.0.0/internal/libsass/bind.cpp000066400000000000000000000001111405214413600203630ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/bind.cpp" #endif golibsass-1.0.0/internal/libsass/bind.hpp000066400000000000000000000001111405214413600203700ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/bind.hpp" #endif golibsass-1.0.0/internal/libsass/c2ast.cpp000066400000000000000000000001121405214413600204640ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/c2ast.cpp" #endif golibsass-1.0.0/internal/libsass/c2ast.hpp000066400000000000000000000001121405214413600204710ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/c2ast.hpp" #endif golibsass-1.0.0/internal/libsass/c99func.c000066400000000000000000000001121405214413600203700ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/c99func.c" #endif golibsass-1.0.0/internal/libsass/cencode.c000066400000000000000000000001121405214413600205100ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/cencode.c" #endif golibsass-1.0.0/internal/libsass/check_nesting.cpp000066400000000000000000000001221405214413600222550ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/check_nesting.cpp" #endif golibsass-1.0.0/internal/libsass/check_nesting.hpp000066400000000000000000000001221405214413600222620ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/check_nesting.hpp" #endif golibsass-1.0.0/internal/libsass/color_maps.cpp000066400000000000000000000001171405214413600216130ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/color_maps.cpp" #endif golibsass-1.0.0/internal/libsass/color_maps.hpp000066400000000000000000000001171405214413600216200ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/color_maps.hpp" #endif golibsass-1.0.0/internal/libsass/constants.cpp000066400000000000000000000001161405214413600214700ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/constants.cpp" #endif golibsass-1.0.0/internal/libsass/constants.hpp000066400000000000000000000001161405214413600214750ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/constants.hpp" #endif golibsass-1.0.0/internal/libsass/context.cpp000066400000000000000000000001141405214413600211360ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/context.cpp" #endif golibsass-1.0.0/internal/libsass/context.hpp000066400000000000000000000001141405214413600211430ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/context.hpp" #endif golibsass-1.0.0/internal/libsass/cssize.cpp000066400000000000000000000001131405214413600207510ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/cssize.cpp" #endif golibsass-1.0.0/internal/libsass/cssize.hpp000066400000000000000000000001131405214413600207560ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/cssize.hpp" #endif golibsass-1.0.0/internal/libsass/dart_helpers.hpp000066400000000000000000000001211405214413600221310ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/dart_helpers.hpp" #endif golibsass-1.0.0/internal/libsass/debug.hpp000066400000000000000000000001121405214413600205430ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/debug.hpp" #endif golibsass-1.0.0/internal/libsass/debugger.hpp000066400000000000000000000001151405214413600212440ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/debugger.hpp" #endif golibsass-1.0.0/internal/libsass/emitter.cpp000066400000000000000000000001141405214413600211230ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/emitter.cpp" #endif golibsass-1.0.0/internal/libsass/emitter.hpp000066400000000000000000000001141405214413600211300ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/emitter.hpp" #endif golibsass-1.0.0/internal/libsass/environment.cpp000066400000000000000000000001201405214413600220130ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/environment.cpp" #endif golibsass-1.0.0/internal/libsass/environment.hpp000066400000000000000000000001201405214413600220200ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/environment.hpp" #endif golibsass-1.0.0/internal/libsass/error_handling.cpp000066400000000000000000000001231405214413600224470ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/error_handling.cpp" #endif golibsass-1.0.0/internal/libsass/error_handling.hpp000066400000000000000000000001231405214413600224540ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/error_handling.hpp" #endif golibsass-1.0.0/internal/libsass/eval.cpp000066400000000000000000000001111405214413600203760ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/eval.cpp" #endif golibsass-1.0.0/internal/libsass/eval.hpp000066400000000000000000000001111405214413600204030ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/eval.hpp" #endif golibsass-1.0.0/internal/libsass/eval_selectors.cpp000066400000000000000000000001231405214413600224640ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/eval_selectors.cpp" #endif golibsass-1.0.0/internal/libsass/expand.cpp000066400000000000000000000001131405214413600207300ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/expand.cpp" #endif golibsass-1.0.0/internal/libsass/expand.hpp000066400000000000000000000001131405214413600207350ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/expand.hpp" #endif golibsass-1.0.0/internal/libsass/extender.cpp000066400000000000000000000001151405214413600212710ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/extender.cpp" #endif golibsass-1.0.0/internal/libsass/extender.hpp000066400000000000000000000001151405214413600212760ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/extender.hpp" #endif golibsass-1.0.0/internal/libsass/extension.cpp000066400000000000000000000001161405214413600214700ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/extension.cpp" #endif golibsass-1.0.0/internal/libsass/extension.hpp000066400000000000000000000001161405214413600214750ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/extension.hpp" #endif golibsass-1.0.0/internal/libsass/file.cpp000066400000000000000000000001111405214413600203660ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/file.cpp" #endif golibsass-1.0.0/internal/libsass/file.hpp000066400000000000000000000001111405214413600203730ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/file.hpp" #endif golibsass-1.0.0/internal/libsass/fn_colors.cpp000066400000000000000000000001161405214413600214400ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_colors.cpp" #endif golibsass-1.0.0/internal/libsass/fn_colors.hpp000066400000000000000000000001161405214413600214450ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_colors.hpp" #endif golibsass-1.0.0/internal/libsass/fn_lists.cpp000066400000000000000000000001151405214413600212740ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_lists.cpp" #endif golibsass-1.0.0/internal/libsass/fn_lists.hpp000066400000000000000000000001151405214413600213010ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_lists.hpp" #endif golibsass-1.0.0/internal/libsass/fn_maps.cpp000066400000000000000000000001141405214413600210750ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_maps.cpp" #endif golibsass-1.0.0/internal/libsass/fn_maps.hpp000066400000000000000000000001141405214413600211020ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_maps.hpp" #endif golibsass-1.0.0/internal/libsass/fn_miscs.cpp000066400000000000000000000001151405214413600212540ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_miscs.cpp" #endif golibsass-1.0.0/internal/libsass/fn_miscs.hpp000066400000000000000000000001151405214413600212610ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_miscs.hpp" #endif golibsass-1.0.0/internal/libsass/fn_numbers.cpp000066400000000000000000000001171405214413600216130ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_numbers.cpp" #endif golibsass-1.0.0/internal/libsass/fn_numbers.hpp000066400000000000000000000001171405214413600216200ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_numbers.hpp" #endif golibsass-1.0.0/internal/libsass/fn_selectors.cpp000066400000000000000000000001211405214413600221360ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_selectors.cpp" #endif golibsass-1.0.0/internal/libsass/fn_selectors.hpp000066400000000000000000000001211405214413600221430ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_selectors.hpp" #endif golibsass-1.0.0/internal/libsass/fn_strings.cpp000066400000000000000000000001171405214413600216310ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_strings.cpp" #endif golibsass-1.0.0/internal/libsass/fn_strings.hpp000066400000000000000000000001171405214413600216360ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_strings.hpp" #endif golibsass-1.0.0/internal/libsass/fn_utils.cpp000066400000000000000000000001151405214413600212760ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_utils.cpp" #endif golibsass-1.0.0/internal/libsass/fn_utils.hpp000066400000000000000000000001151405214413600213030ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/fn_utils.hpp" #endif golibsass-1.0.0/internal/libsass/inspect.cpp000066400000000000000000000001141405214413600211170ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/inspect.cpp" #endif golibsass-1.0.0/internal/libsass/inspect.hpp000066400000000000000000000001141405214413600211240ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/inspect.hpp" #endif golibsass-1.0.0/internal/libsass/json.cpp000066400000000000000000000001111405214413600204200ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/json.cpp" #endif golibsass-1.0.0/internal/libsass/json.hpp000066400000000000000000000001111405214413600204250ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/json.hpp" #endif golibsass-1.0.0/internal/libsass/kwd_arg_macros.hpp000066400000000000000000000001231405214413600224410ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/kwd_arg_macros.hpp" #endif golibsass-1.0.0/internal/libsass/lexer.cpp000066400000000000000000000001121405214413600205670ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/lexer.cpp" #endif golibsass-1.0.0/internal/libsass/lexer.hpp000066400000000000000000000001121405214413600205740ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/lexer.hpp" #endif golibsass-1.0.0/internal/libsass/listize.cpp000066400000000000000000000001141405214413600211350ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/listize.cpp" #endif golibsass-1.0.0/internal/libsass/listize.hpp000066400000000000000000000001141405214413600211420ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/listize.hpp" #endif golibsass-1.0.0/internal/libsass/mapping.hpp000066400000000000000000000001141405214413600211120ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/mapping.hpp" #endif golibsass-1.0.0/internal/libsass/memory.hpp000066400000000000000000000001131405214413600207660ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/memory.hpp" #endif golibsass-1.0.0/internal/libsass/operation.hpp000066400000000000000000000001161405214413600214610ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/operation.hpp" #endif golibsass-1.0.0/internal/libsass/operators.cpp000066400000000000000000000001161405214413600214720ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/operators.cpp" #endif golibsass-1.0.0/internal/libsass/operators.hpp000066400000000000000000000001161405214413600214770ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/operators.hpp" #endif golibsass-1.0.0/internal/libsass/ordered_map.hpp000066400000000000000000000001201405214413600217350ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/ordered_map.hpp" #endif golibsass-1.0.0/internal/libsass/output.cpp000066400000000000000000000001131405214413600210110ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/output.cpp" #endif golibsass-1.0.0/internal/libsass/output.hpp000066400000000000000000000001131405214413600210160ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/output.hpp" #endif golibsass-1.0.0/internal/libsass/parser.cpp000066400000000000000000000001131405214413600207450ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/parser.cpp" #endif golibsass-1.0.0/internal/libsass/parser.hpp000066400000000000000000000001131405214413600207520ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/parser.hpp" #endif golibsass-1.0.0/internal/libsass/parser_selectors.cpp000066400000000000000000000001251405214413600230330ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/parser_selectors.cpp" #endif golibsass-1.0.0/internal/libsass/permutate.hpp000066400000000000000000000001161405214413600214670ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/permutate.hpp" #endif golibsass-1.0.0/internal/libsass/plugins.cpp000066400000000000000000000001141405214413600211330ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/plugins.cpp" #endif golibsass-1.0.0/internal/libsass/plugins.hpp000066400000000000000000000001141405214413600211400ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/plugins.hpp" #endif golibsass-1.0.0/internal/libsass/position.cpp000066400000000000000000000001151405214413600213170ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/position.cpp" #endif golibsass-1.0.0/internal/libsass/position.hpp000066400000000000000000000001151405214413600213240ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/position.hpp" #endif golibsass-1.0.0/internal/libsass/prelexer.cpp000066400000000000000000000001151405214413600213010ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/prelexer.cpp" #endif golibsass-1.0.0/internal/libsass/prelexer.hpp000066400000000000000000000001151405214413600213060ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/prelexer.hpp" #endif golibsass-1.0.0/internal/libsass/remove_placeholders.cpp000066400000000000000000000001301405214413600234720ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/remove_placeholders.cpp" #endif golibsass-1.0.0/internal/libsass/remove_placeholders.hpp000066400000000000000000000001301405214413600234770ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/remove_placeholders.hpp" #endif golibsass-1.0.0/internal/libsass/sass.cpp000066400000000000000000000001111405214413600204200ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass.cpp" #endif golibsass-1.0.0/internal/libsass/sass.hpp000066400000000000000000000001111405214413600204250ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass.hpp" #endif golibsass-1.0.0/internal/libsass/sass2scss.cpp000066400000000000000000000001161405214413600214030ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass2scss.cpp" #endif golibsass-1.0.0/internal/libsass/sass_context.cpp000066400000000000000000000001211405214413600221650ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass_context.cpp" #endif golibsass-1.0.0/internal/libsass/sass_context.hpp000066400000000000000000000001211405214413600221720ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass_context.hpp" #endif golibsass-1.0.0/internal/libsass/sass_functions.cpp000066400000000000000000000001231405214413600225130ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass_functions.cpp" #endif golibsass-1.0.0/internal/libsass/sass_functions.hpp000066400000000000000000000001231405214413600225200ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass_functions.hpp" #endif golibsass-1.0.0/internal/libsass/sass_values.cpp000066400000000000000000000001201405214413600217770ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass_values.cpp" #endif golibsass-1.0.0/internal/libsass/sass_values.hpp000066400000000000000000000001201405214413600220040ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/sass_values.hpp" #endif golibsass-1.0.0/internal/libsass/settings.hpp000066400000000000000000000001151405214413600213200ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/settings.hpp" #endif golibsass-1.0.0/internal/libsass/source.cpp000066400000000000000000000001131405214413600207510ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/source.cpp" #endif golibsass-1.0.0/internal/libsass/source.hpp000066400000000000000000000001131405214413600207560ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/source.hpp" #endif golibsass-1.0.0/internal/libsass/source_data.hpp000066400000000000000000000001201405214413600217450ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/source_data.hpp" #endif golibsass-1.0.0/internal/libsass/source_map.cpp000066400000000000000000000001171405214413600216120ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/source_map.cpp" #endif golibsass-1.0.0/internal/libsass/source_map.hpp000066400000000000000000000001171405214413600216170ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/source_map.hpp" #endif golibsass-1.0.0/internal/libsass/stylesheet.cpp000066400000000000000000000001171405214413600216460ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/stylesheet.cpp" #endif golibsass-1.0.0/internal/libsass/stylesheet.hpp000066400000000000000000000001171405214413600216530ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/stylesheet.hpp" #endif golibsass-1.0.0/internal/libsass/to_value.cpp000066400000000000000000000001151405214413600212710ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/to_value.cpp" #endif golibsass-1.0.0/internal/libsass/to_value.hpp000066400000000000000000000001151405214413600212760ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/to_value.hpp" #endif golibsass-1.0.0/internal/libsass/units.cpp000066400000000000000000000001121405214413600206120ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/units.cpp" #endif golibsass-1.0.0/internal/libsass/units.hpp000066400000000000000000000001121405214413600206170ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/units.hpp" #endif golibsass-1.0.0/internal/libsass/utf8.h000066400000000000000000000001071405214413600200070ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/utf8.h" #endif golibsass-1.0.0/internal/libsass/utf8_string.cpp000066400000000000000000000001201405214413600217230ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/utf8_string.cpp" #endif golibsass-1.0.0/internal/libsass/utf8_string.hpp000066400000000000000000000001201405214413600217300ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/utf8_string.hpp" #endif golibsass-1.0.0/internal/libsass/util.cpp000066400000000000000000000001111405214413600204240ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/util.cpp" #endif golibsass-1.0.0/internal/libsass/util.hpp000066400000000000000000000001111405214413600204310ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/util.hpp" #endif golibsass-1.0.0/internal/libsass/util_string.cpp000066400000000000000000000001201405214413600220120ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/util_string.cpp" #endif golibsass-1.0.0/internal/libsass/util_string.hpp000066400000000000000000000001201405214413600220170ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/util_string.hpp" #endif golibsass-1.0.0/internal/libsass/values.cpp000066400000000000000000000001131405214413600207500ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/values.cpp" #endif golibsass-1.0.0/internal/libsass/values.hpp000066400000000000000000000001131405214413600207550ustar00rootroot00000000000000#ifndef USE_LIBSASS_SRC #include "../../libsass_src/src/values.hpp" #endif golibsass-1.0.0/libsass/000077500000000000000000000000001405214413600151365ustar00rootroot00000000000000golibsass-1.0.0/libsass/example_test.go000066400000000000000000000013051405214413600201560ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package libsass_test import ( "fmt" "log" "github.com/bep/golibsass/libsass" ) func ExampleTranspiler() { transpiler, err := libsass.New(libsass.Options{OutputStyle: libsass.CompressedStyle}) if err != nil { log.Fatal(err) } result, err := transpiler.Execute(` $font-stack: Helvetica, sans-serif; $primary-color: #333; body { font: 100% $font-stack; color: $primary-color; } `) if err != nil { log.Fatal(err) } fmt.Println(result.CSS) // Output: body{font:100% Helvetica,sans-serif;color:#333} } golibsass-1.0.0/libsass/transpiler.go000066400000000000000000000111451405214413600176520ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // Package libsass a SCSS transpiler to CSS using LibSASS. package libsass import ( "encoding/json" "fmt" "os" "strings" "github.com/bep/golibsass/internal/libsass" ) type libsassTranspiler struct { options Options } // New creates a new libsass transpiler configured with the given options. func New(options Options) (Transpiler, error) { return libsassTranspiler{options: options}, nil } // Execute transpiles the SCSS or SASS from src into dst. func (t libsassTranspiler) Execute(src string) (Result, error) { var result Result if t.options.SassSyntax { // LibSass does not support this directly, so have to handle the main SASS content // special. src = libsass.SassToScss(src) } dataCtx := libsass.SassMakeDataContext(src) opts := libsass.SassDataContextGetOptions(dataCtx) { // Set options if t.options.ImportResolver != nil { idx := libsass.AddImportResolver(opts, t.options.ImportResolver) defer libsass.DeleteImportResolver(idx) } if t.options.Precision != 0 { libsass.SassOptionSetPrecision(opts, t.options.Precision) } if t.options.SourceMapOptions.Filename != "" { libsass.SassOptionSetSourceMapFile(opts, t.options.SourceMapOptions.Filename) } if t.options.SourceMapOptions.Root != "" { libsass.SassOptionSetSourceMapRoot(opts, t.options.SourceMapOptions.Root) } if t.options.SourceMapOptions.OutputPath != "" { libsass.SassOptionSetOutputPath(opts, t.options.SourceMapOptions.OutputPath) } if t.options.SourceMapOptions.InputPath != "" { libsass.SassOptionSetInputPath(opts, t.options.SourceMapOptions.InputPath) } libsass.SassOptionSetSourceMapContents(opts, t.options.SourceMapOptions.Contents) libsass.SassOptionSetOmitSourceMapURL(opts, t.options.SourceMapOptions.OmitURL) libsass.SassOptionSetSourceMapEmbed(opts, t.options.SourceMapOptions.EnableEmbedded) libsass.SassOptionSetIncludePath(opts, strings.Join(t.options.IncludePaths, string(os.PathListSeparator))) libsass.SassOptionSetOutputStyle(opts, int(t.options.OutputStyle)) libsass.SassOptionSetSourceComments(opts, false) libsass.SassDataContextSetOptions(dataCtx, opts) } ctx := libsass.SassDataContextGetContext(dataCtx) compiler := libsass.SassMakeDataCompiler(dataCtx) defer libsass.SassDeleteCompiler(compiler) libsass.SassCompilerParse(compiler) libsass.SassCompilerExecute(compiler) if status := libsass.SassContextGetErrorStatus(ctx); status != 0 { return result, jsonToError(libsass.SassContextGetErrorJSON(ctx)) } result.CSS = libsass.SassContextGetOutputString(ctx) result.SourceMapFilename = libsass.SassOptionGetSourceMapFile(opts) result.SourceMapContent = libsass.SassContextGetSourceMapString(ctx) return result, nil } type Result struct { CSS string // If source maps are configured. SourceMapFilename string SourceMapContent string } type Transpiler interface { Execute(src string) (Result, error) } type ( OutputStyle int ) const ( NestedStyle OutputStyle = iota ExpandedStyle CompactStyle CompressedStyle ) // ParseOutputStyle will convert s into OutputStyle. // Case insensitive, returns NestedStyle for unknown values. func ParseOutputStyle(s string) OutputStyle { switch strings.ToLower(s) { case "nested": return NestedStyle case "expanded": return ExpandedStyle case "compact": return CompactStyle case "compressed": return CompressedStyle } return NestedStyle } type Options struct { // Default is nested. OutputStyle OutputStyle // Precision of floating point math. Precision int // File paths to use to resolve imports. IncludePaths []string // ImportResolver can be used to supply a custom import resolver, both to redirect // to another URL or to return the body. ImportResolver func(url string, prev string) (newURL string, body string, resolved bool) // Used to indicate "old style" SASS for the input stream. SassSyntax bool SourceMapOptions SourceMapOptions } type SourceMapOptions struct { Filename string Root string InputPath string OutputPath string Contents bool OmitURL bool EnableEmbedded bool } func jsonToError(jsonstr string) (e Error) { _ = json.Unmarshal([]byte(jsonstr), &e) return } type Error struct { Status int `json:"status"` Column int `json:"column"` File string `json:"file"` Line int `json:"line"` Message string `json:"message"` } func (e Error) Error() string { return fmt.Sprintf("file %q, line %d, col %d: %s ", e.File, e.Line, e.Column, e.Message) } golibsass-1.0.0/libsass/transpiler_test.go000066400000000000000000000176151405214413600207210ustar00rootroot00000000000000// Copyright © 2020 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package libsass import ( "fmt" "io/ioutil" "os" "path/filepath" "sync" "testing" qt "github.com/frankban/quicktest" ) const ( sassSample = `nav { ul { margin: 0; padding: 0; list-style: none; } li { display: inline-block; } a { display: block; padding: 6px 12px; text-decoration: none; } }` sassSampleTranspiled = "nav ul {\n margin: 0;\n padding: 0;\n list-style: none; }\n\nnav li {\n display: inline-block; }\n\nnav a {\n display: block;\n padding: 6px 12px;\n text-decoration: none; }\n" ) func TestTranspiler(t *testing.T) { c := qt.New(t) importResolver := func(url string, prev string) (string, string, bool) { // This will make every import the same, which is probably not a common use // case. return url, `$white: #fff`, true } for _, test := range []struct { name string opts Options src string expect interface{} }{ {"Output style compressed", Options{OutputStyle: CompressedStyle}, "div { color: #ccc; }", "div{color:#ccc}\n"}, {"Invalid syntax", Options{OutputStyle: CompressedStyle}, "div { color: $white; }", false}, {"Import not found", Options{OutputStyle: CompressedStyle}, "@import \"foo\"", false}, {"Sass syntax", Options{OutputStyle: CompressedStyle, SassSyntax: true}, "$color: #ccc\ndiv { p { color: $color; } }", "div p{color:#ccc}\n"}, {"Import resolver", Options{ImportResolver: importResolver}, "@import \"colors\";\ndiv { p { color: $white; } }", "div p {\n color: #fff; }\n"}, {"Precision", Options{Precision: 3}, "div { width: percentage(1 / 3); }", "div {\n width: 33.333%; }\n"}, } { test := test c.Run(test.name, func(c *qt.C) { b, ok := test.expect.(bool) shouldFail := ok && !b transpiler, err := New(test.opts) c.Assert(err, qt.IsNil) result, err := transpiler.Execute(test.src) if shouldFail { c.Assert(err, qt.Not(qt.IsNil)) } else { c.Assert(err, qt.IsNil) c.Assert(result.CSS, qt.Equals, test.expect) } }) } } func TestError(t *testing.T) { c := qt.New(t) transpiler, err := New(Options{OutputStyle: CompressedStyle}) c.Assert(err, qt.IsNil) _, err = transpiler.Execute("\n\ndiv { color: $blue; }") c.Assert(err, qt.Not(qt.IsNil)) lerr := err.(Error) c.Assert(lerr.Line, qt.Equals, 3) c.Assert(lerr.Column, qt.Equals, 14) c.Assert(lerr.Message, qt.Equals, `Undefined variable: "$blue".`) c.Assert(lerr.Error(), qt.Equals, `file "stdin", line 3, col 14: Undefined variable: "$blue". `) } func TestSourceMapSettings(t *testing.T) { c := qt.New(t) src := `div { p { color: blue; } }` transpiler, err := New(Options{ SourceMapOptions: SourceMapOptions{ EnableEmbedded: false, Contents: true, OmitURL: false, Filename: "source.map", OutputPath: "outout.css", InputPath: "input.scss", Root: "/my/root", }, }) c.Assert(err, qt.IsNil) result, err := transpiler.Execute(src) c.Assert(err, qt.IsNil) c.Assert(result.CSS, qt.Equals, "div p {\n color: blue; }\n\n/*# sourceMappingURL=source.map */") c.Assert(result.SourceMapFilename, qt.Equals, "source.map") c.Assert(`"sourceRoot": "/my/root",`, qt.Contains, `"sourceRoot": "/my/root",`) c.Assert(`"file": "outout.css",`, qt.Contains, `"file": "outout.css",`) c.Assert(`"input.scss",`, qt.Contains, `"input.scss",`) c.Assert(`mappings": "AAGA,AAAM,GAAH,CAAG,CAAC,CAAC;EAAE,KAAK,ECFH,OAAO,GDEM"`, qt.Contains, `mappings": "AAGA,AAAM,GAAH,CAAG,CAAC,CAAC;EAAE,KAAK,ECFH,OAAO,GDEM"`) } func TestIncludePaths(t *testing.T) { dir1, _ := ioutil.TempDir(os.TempDir(), "libsass-test-include-paths-dir1") defer os.RemoveAll(dir1) dir2, _ := ioutil.TempDir(os.TempDir(), "libsass-test-include-paths-dir2") defer os.RemoveAll(dir2) colors := filepath.Join(dir1, "_colors.scss") content := filepath.Join(dir2, "_content.scss") ioutil.WriteFile(colors, []byte(` $moo: #f442d1 !default; `), 0644) ioutil.WriteFile(content, []byte(` content { color: #ccc; } `), 0644) c := qt.New(t) src := ` @import "colors"; @import "content"; div { p { color: $moo; } }` transpiler, err := New(Options{ IncludePaths: []string{dir1, dir2}, OutputStyle: CompressedStyle, ImportResolver: func(url string, prev string) (newUrl string, body string, resolved bool) { // Let LibSass resolve the import. return "", "", false }, }) c.Assert(err, qt.IsNil) result, err := transpiler.Execute(src) c.Assert(err, qt.IsNil) c.Assert(result.CSS, qt.Equals, "content{color:#ccc}div p{color:#f442d1}\n") } func TestConcurrentTranspile(t *testing.T) { c := qt.New(t) importResolver := func(url string, prev string) (string, string, bool) { return url, `$white: #fff`, true } transpiler, err := New(Options{ OutputStyle: CompressedStyle, ImportResolver: importResolver}) c.Assert(err, qt.IsNil) var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 10; j++ { src := ` @import "colors"; div { p { color: $white; } }` result, err := transpiler.Execute(src) c.Check(err, qt.IsNil) c.Check(result.CSS, qt.Equals, "div p{color:#fff}\n") if c.Failed() { return } } }() } wg.Wait() } func TestImportResolverConcurrent(t *testing.T) { c := qt.New(t) createImportResolver := func(width int) func(url string, prev string) (string, string, bool) { return func(url string, prev string) (string, string, bool) { return url, fmt.Sprintf(`$width: %d`, width), true } } var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 100; j++ { transpiler, err := New(Options{ OutputStyle: CompressedStyle, ImportResolver: createImportResolver(j)}) c.Check(err, qt.IsNil) src := ` @import "widths"; div { p { width: $width; } }` for k := 0; k < 10; k++ { result, err := transpiler.Execute(src) c.Check(err, qt.IsNil) c.Check(result.CSS, qt.Equals, fmt.Sprintf("div p{width:%d}\n", j)) if c.Failed() { return } } } }() } wg.Wait() } func BenchmarkTranspile(b *testing.B) { type tester struct { src string expect string transpiler Transpiler } newTester := func(b *testing.B, opts Options) tester { transpiler, err := New(opts) if err != nil { b.Fatal(err) } return tester{ transpiler: transpiler, } } runBench := func(b *testing.B, t tester) { b.ResetTimer() for n := 0; n < b.N; n++ { result, err := t.transpiler.Execute(t.src) if err != nil { b.Fatal(err) } if result.CSS != t.expect { b.Fatal("Got:", result.CSS) } } } b.Run("SCSS", func(b *testing.B) { t := newTester(b, Options{}) t.src = sassSample t.expect = sassSampleTranspiled runBench(b, t) }) b.Run("SCSS Parallel", func(b *testing.B) { t := newTester(b, Options{}) t.src = sassSample t.expect = sassSampleTranspiled b.RunParallel(func(pb *testing.PB) { for pb.Next() { result, err := t.transpiler.Execute(t.src) if err != nil { b.Fatal(err) } if result.CSS != t.expect { b.Fatalf("Got: %q\n", result.CSS) } } }) }) b.Run("Sass", func(b *testing.B) { t := newTester(b, Options{OutputStyle: CompressedStyle, SassSyntax: true}) t.src = ` $color: #333; .content-navigation border-color: $color` t.expect = ".content-navigation{border-color:#333}\n" runBench(b, t) }) } func TestParseOutputStyle(t *testing.T) { c := qt.New(t) c.Assert(ParseOutputStyle("nested"), qt.Equals, NestedStyle) c.Assert(ParseOutputStyle("expanded"), qt.Equals, ExpandedStyle) c.Assert(ParseOutputStyle("compact"), qt.Equals, CompactStyle) c.Assert(ParseOutputStyle("compressed"), qt.Equals, CompressedStyle) c.Assert(ParseOutputStyle("EXPANDED"), qt.Equals, ExpandedStyle) c.Assert(ParseOutputStyle("foo"), qt.Equals, NestedStyle) } golibsass-1.0.0/libsass_src/000077500000000000000000000000001405214413600160055ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/.editorconfig000066400000000000000000000004421405214413600204620ustar00rootroot00000000000000# This file is for unifying the coding style for different editors and IDEs # editorconfig.org root = true [*] charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 2 [{Makefile,GNUmakefile.am}] indent_style = tab indent_size = 4 golibsass-1.0.0/libsass_src/.gitattributes000066400000000000000000000001021405214413600206710ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto golibsass-1.0.0/libsass_src/.github/000077500000000000000000000000001405214413600173455ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/.github/CONTRIBUTING.md000066400000000000000000000072021405214413600215770ustar00rootroot00000000000000# Contributing to LibSass :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: The following is a set of guidelines for contributing to LibSass, which is hosted in the [Sass Organization](https://github.com/sass) on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. LibSass is a library that implements a [sass language][8] compiler. As such it does not directly interface with end users (frontend developers). For direct contributions to the LibSass code base you will need to have at least a rough idea of C++, we will not lie about that. But there are other ways to contribute to the progress of LibSass. All contributions are done via github pull requests. You can also contribute to the LibSass [documentation][9] or provide additional [spec tests][10] (and we will gladly point you in the direction for corners that lack test coverage). Foremost we rely on good and concise bug reports for issues the spec tests do not yet catch. ## Precheck: My Sass isn't compiling - [ ] Check if you can reproduce the issue via [SourceMap Inspector][5] (updated regularly). - [ ] Validate official ruby sass compiler via [SassMeister][6] produces your expected result. - [ ] Search for similar issue in [LibSass][1] and [node-sass][2] (include closed tickets) - [ ] Optionally test your code directly with [sass][7] or [sassc][3] ([installer][4]) ## Precheck: My build/install fails - [ ] Problems with building or installing libsass should be directed to implementors first! - [ ] Except for issues directly verified via sassc or LibSass own build (make/autotools9 ## Craft a meaningfull error report - [ ] Include the version of libsass and the implementor (i.e. node-sass or sassc) - [ ] Include information about your operating system and environment (i.e. io.js) - [ ] Either create a self contained sample that shows your issue ... - [ ] ... or provide it as a fetchable (github preferred) archive/repo - [ ] ... and include a step by step list of command to get all dependencies - [ ] Make it clear if you use indented or/and scss syntax ## My error is hiding in a big code base 1. we do not have time to support your code base! 2. to fix occurring issues we need precise bug reports 3. the more precise you are, the faster we can help you 4. lazy reports get overlooked even when exposing serious bugs 5. it's not hard to do, it only takes time - [ ] Make sure you saved the current state (i.e. commit to git) - [ ] Start by uncommenting blocks in the initial source file - [ ] Check if the problem is still there after each edit - [ ] Repeat until the problem goes away - [ ] Inline imported files as you go along - [ ] Finished once you cannot remove more - [ ] The emphasis is on the word "repeat" ... ## What makes a code test case Important is that someone else can get the test case up and running to reproduce it locally. For this we urge you to verify that your sample yields the expected result by testing it via [SassMeister][6] or directly via ruby sass or node-sass (or any other libsass implementor) before submitting your bug report. Once you verified all of the above, you may use the template below to file your bug report. [1]: https://github.com/sass/libsass/issues?utf8=%E2%9C%93&q=is%3Aissue [2]: https://github.com/sass/node-sass/issues?utf8=%E2%9C%93&q=is%3Aissue [3]: https://github.com/sass/sassc [4]: http://libsass.ocbnet.ch/installer/ [5]: http://libsass.ocbnet.ch/srcmap/ [6]: http://www.sassmeister.com/ [7]: https://rubygems.org/gems/sass [8]: https://sass-lang.com/ [9]: https://github.com/sass/libsass/tree/master/docs [10]: https://github.com/sass/sass-spec golibsass-1.0.0/libsass_src/.github/ISSUE_TEMPLATE.md000066400000000000000000000020531405214413600220520ustar00rootroot00000000000000[todo]: # (Title: Be as meaningful as possible) [todo]: # (Title: Try to use 60 or less chars) [todo]: # (This is only a template!) [todo]: # (remove unneeded bits) [todo]: # (use github preview!) ## input.scss [todo]: # (always test and report with scss syntax) [todo]: # (use sass only when results differ from scss) ```scss test { content: bar } ``` ## Actual results [todo]: # (update version info!) [libsass 3.X.y][1] ```css test { content: bar; } ``` ## Expected result [todo]: # (update version info!) ruby sass 3.X.y ```css test { content: bar; } ``` [todo]: # (update version info!) [todo]: # (example for node-sass!) version info: ```cmd $ node-sass --version node-sass 3.X.y (Wrapper) [JavaScript] libsass 3.X.y (Sass Compiler) [C/C++] ``` [todo]: # (Go to http://libsass.ocbnet.ch/srcmap) [todo]: # (Enter your SCSS code and hit compile) [todo]: # (Click `bookmark` and replace the url) [todo]: # (link is used in actual results above) [1]: http://libsass.ocbnet.ch/srcmap/#dGVzdCB7CiAgY29udGVudDogYmFyOyB9Cg== golibsass-1.0.0/libsass_src/.github/workflows/000077500000000000000000000000001405214413600214025ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/.github/workflows/build-and-test.yml000066400000000000000000000212421405214413600247420ustar00rootroot00000000000000name: GitHub CI on: push: branches: - master - develop pull_request: branches: - master - develop jobs: linux-and-mac: # if: ${{ false }} runs-on: ${{ matrix.config.os }} name: ${{ matrix.config.os }} BUILD=${{ matrix.config.build }} CC=${{ matrix.config.cc }} CXX=${{ matrix.config.cxx }} AUTOTOOLS=${{ matrix.config.autotools }} strategy: fail-fast: false matrix: config: #- {os: ubuntu-16.04, build: 'static', cc: 'gcc-4.4', cxx: 'g++-4.4', autotools: 'no', cppstd: 'gnu++0x'} #- {os: ubuntu-16.04, build: 'static', cc: 'gcc-4.6', cxx: 'g++-4.6', autotools: 'no', cppstd: 'gnu++0x'} - {os: ubuntu-16.04, build: 'static', cc: 'gcc-4.7', cxx: 'g++-4.7', autotools: 'no', cppstd: 'gnu++11'} - {os: ubuntu-16.04, build: 'static', cc: 'gcc-4.8', cxx: 'g++-4.8', autotools: 'no', cppstd: 'c++11'} - {os: ubuntu-16.04, build: 'static', cc: 'gcc-5', cxx: 'g++-5', autotools: 'no', cppstd: 'c++11'} - {os: ubuntu-16.04, build: 'static', cc: 'gcc-6', cxx: 'g++-6', autotools: 'no', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'static', cc: 'gcc-7', cxx: 'g++-7', autotools: 'no', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'shared', cc: 'gcc', cxx: 'g++', autotools: 'yes', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'static', cc: 'gcc', cxx: 'g++', autotools: 'yes', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'shared', cc: 'gcc', cxx: 'g++', autotools: 'no', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'static', cc: 'gcc', cxx: 'g++', autotools: 'no', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'shared', cc: 'clang', cxx: 'clang++', autotools: 'yes', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'static', cc: 'clang', cxx: 'clang++', autotools: 'yes', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'shared', cc: 'clang', cxx: 'clang++', autotools: 'no', cppstd: 'c++11'} - {os: ubuntu-latest, build: 'static', cc: 'clang', cxx: 'clang++', autotools: 'no', cppstd: 'c++11'} - {os: macOS-latest, build: 'shared', cc: 'clang', cxx: 'clang++', autotools: 'yes', cppstd: 'c++11'} - {os: macOS-latest, build: 'static', cc: 'clang', cxx: 'clang++', autotools: 'yes', cppstd: 'c++11'} - {os: macOS-latest, build: 'shared', cc: 'clang', cxx: 'clang++', autotools: 'no', cppstd: 'c++11'} - {os: macOS-latest, build: 'static', cc: 'clang', cxx: 'clang++', autotools: 'no', cppstd: 'c++11'} env: ASAN_OPTIONS: detect_odr_violation=0 AUTOTOOLS: ${{ matrix.config.autotools }} COVERAGE: no BUILD: ${{ matrix.config.build }} CXX: ${{ matrix.config.cxx }} CC: ${{ matrix.config.cc }} steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 if: matrix.config.os == 'ubuntu-16.04' with: ruby-version: 2.6 - name: Install ruby hrx module if: matrix.config.os == 'ubuntu-16.04' run: sudo /opt/hostedtoolcache/Ruby/2.6.7/x64/bin/gem install hrx - name: Install ruby minitest module if: matrix.config.os == 'ubuntu-16.04' run: sudo /opt/hostedtoolcache/Ruby/2.6.7/x64/bin/gem install minitest - name: Install automake if needed (MacOS) if: runner.os == 'macOS' run: brew install automake - name: Install gcc 7 if needed if: matrix.config.cc == 'gcc-7' run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g++-7 -y - name: Install gcc 6 if needed if: matrix.config.cc == 'gcc-6' run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g++-6 -y - name: Install gcc 5 if needed if: matrix.config.cc == 'gcc-5' run: | sudo add-apt-repository universe sudo add-apt-repository multiverse sudo apt update sudo apt install g++-5 -y - name: Install gcc 4.8 if needed if: matrix.config.cc == 'gcc-4.8' run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g++-4.8 -y - name: Install gcc 4.7 if needed if: matrix.config.cc == 'gcc-4.7' run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g++-4.7 -y - name: Install gcc 4.6 if needed if: matrix.config.cc == 'gcc-4.6' run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g++-4.6 -y - name: Install gcc 4.5 if needed if: matrix.config.cc == 'gcc-4.5' run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g++-4.5 -y - name: Install gcc 4.4 if needed if: matrix.config.cc == 'gcc-4.4' run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install g++-4.4 -y - name: ./script/ci-install-deps env: MAKE_OPTS: LIBSASS_CPPSTD=${{ matrix.config.cppstd }} run: ./script/ci-install-deps - name: ./script/ci-install-compiler env: MAKE_OPTS: LIBSASS_CPPSTD=${{ matrix.config.cppstd }} run: ./script/ci-install-compiler - name: ./script/ci-build-libsass env: MAKE_OPTS: LIBSASS_CPPSTD=${{ matrix.config.cppstd }} run: ./script/ci-build-libsass windows-msvc: runs-on: windows-latest name: Windows MSVC build strategy: fail-fast: false matrix: config: - {build: Release, platform: Win64} - {build: Debug, platform: Win64} - {build: Release, platform: Win32} - {build: Debug, platform: Win32} steps: - name: Change git config to preserve line-endings run: | git config --system core.autocrlf false git config --system core.eol lf - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 bundler-cache: true - name: Install ruby hrx module run: gem install hrx - name: Install ruby minitest module run: gem install minitest - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - name: Clone and checkout sassc repository run: git clone https://github.com/sass/sassc.git - name: Clone and checkout sass-spec repository run: git clone https://github.com/sass/sass-spec.git - name: Compile libsass ${{ matrix.config.build }} build for ${{ matrix.config.platform }} run: msbuild /m:4 /p:"Configuration=${{ matrix.config.build }};Platform=${{ matrix.config.platform }}" sassc\win\sassc.sln - name: Execute spec test runner run: ruby sass-spec/sass-spec.rb --probe-todo --impl libsass -c sassc/bin/sassc.exe -s sass-spec/spec windows-mingw: runs-on: windows-latest name: Windows MinGW build strategy: fail-fast: false matrix: config: - {build: shared, platform: x64} - {build: static, platform: x64} - {build: shared, platform: x86} - {build: static, platform: x86} steps: - name: Change git config to preserve line-endings run: | git config --system core.autocrlf false git config --system core.eol lf - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 bundler-cache: true - name: Set up MinGW uses: egor-tensin/setup-mingw@v2 with: platform: ${{ matrix.config.platform }} - name: Install ruby hrx module run: gem install hrx - name: Install ruby minitest module run: gem install minitest - name: Clone and checkout sassc repository run: git clone https://github.com/sass/sassc.git - name: Clone and checkout sass-spec repository run: git clone https://github.com/sass/sass-spec.git - name: Add libsass library path to be found if: matrix.config.build == 'shared' run: echo "/d/a/libsass/libsass/lib" >> $GITHUB_PATH - name: Compile libsass ${{ matrix.config.build }} build for ${{ matrix.config.platform }} run: make ${{ matrix.config.build }} BUILD=${{ matrix.config.build }} - name: Copy library over to pass call test if: matrix.config.build == 'shared' run: copy /a/libsass/libsass/lib/libsass.dll sassc/bin/ - name: Compile sassc ${{ matrix.config.build }} build for ${{ matrix.config.platform }} run: make sassc BUILD=${{ matrix.config.build }} - name: Execute spec test runner run: ruby sass-spec/sass-spec.rb --probe-todo --impl libsass -c sassc/bin/sassc.exe -s sass-spec/spec #- name: Install LLVM and Clang # uses: KyleMayes/install-llvm-action@v1.2.2 golibsass-1.0.0/libsass_src/.gitignore000066400000000000000000000020351405214413600177750ustar00rootroot00000000000000# Miscellaneous stuff /sassc /sass-spec /plugins/ VERSION .DS_Store .sass-cache *.gem *.gcno .svn/* .cproject .project .settings/ *.db *.aps # Configuration stuff GNUmakefile.in GNUmakefile /aclocal.m4 /autom4te.cache/ /src/config.h /config.h.in /config.log /config.status /configure /libtool /m4/libtool.m4 /m4/ltoptions.m4 /m4/ltsugar.m4 /m4/ltversion.m4 /m4/lt~obsolete.m4 /script/ar-lib /script/compile /script/config.guess /script/config.sub /script/depcomp /script/install-sh /script/ltmain.sh /script/missing /script/test-driver /src/stamp-h1 /src/Makefile.in /src/Makefile libsass/* # Build stuff *.o *.lo *.so *.dll *.a *.suo *.sdf *.opendb *.opensdf a.out libsass.js tester tester.exe build/ config.h.in* lib/pkgconfig/ bin/* .deps/ .libs/ win/bin *.user win/*.db # Final results sassc++ libsass.la src/support/libsass.pc # Cloned testing dirs sassc/ sass-spec/ installer/ # Visual Studio cache directory .vs/ # Visual Studio Code .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json golibsass-1.0.0/libsass_src/CODE_OF_CONDUCT.md000066400000000000000000000010311405214413600205770ustar00rootroot00000000000000Sass is more than a technology; Sass is driven by the community of individuals that power its development and use every day. As a community, we want to embrace the very differences that have made our collaboration so powerful, and work together to provide the best environment for learning, growing, and sharing of ideas. It is imperative that we keep Sass a fun, welcoming, challenging, and fair place to play. [The full community guidelines can be found on the Sass website.][link] [link]: https://sass-lang.com/community-guidelines golibsass-1.0.0/libsass_src/COPYING000066400000000000000000000023341405214413600170420ustar00rootroot00000000000000 Copyright (C) 2012 by Hampton Catlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following files in the spec were taken from the original Ruby Sass project which is copyright Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein and under the same license. golibsass-1.0.0/libsass_src/GNUmakefile.am000066400000000000000000000051641405214413600204610ustar00rootroot00000000000000ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 -I script AM_COPT = -Wall -O2 AM_COVLDFLAGS = if ENABLE_COVERAGE AM_COPT = -Wall -O1 -fno-omit-frame-pointer --coverage AM_COVLDFLAGS += -lgcov endif AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = $(AM_COPT) AM_CXXFLAGS = $(AM_COPT) AM_LDFLAGS = $(AM_COPT) $(AM_COVLDFLAGS) # only needed to support old source tree # we have moved the files to src folder AM_CPPFLAGS += -I$(top_srcdir) RESOURCES = AM_CXXFLAGS += -std=c++11 if COMPILER_IS_MINGW32 RESOURCES += res/libsass.rc endif TEST_EXTENSIONS = .rb if ENABLE_TESTS SASS_SASSC_PATH ?= $(top_srcdir)/sassc SASS_SPEC_PATH ?= $(top_srcdir)/sass-spec LIBSASS_SPEC_PATH ?= $(top_srcdir)/libsass-spec noinst_PROGRAMS = tester tester_LDADD = src/libsass.la tester_LDFLAGS = $(AM_LDFLAGS) nodist_tester_SOURCES = $(SASS_SASSC_PATH)/sassc.c SASS_SASSC_VERSION ?= `cd "$(SASS_SASSC_PATH)" && ./version.sh` tester_CFLAGS = $(AM_CFLAGS) -DSASSC_VERSION="\"$(SASS_SASSC_VERSION)\"" tester_CXXFLAGS = $(AM_CXXFLAGS) -DSASSC_VERSION="\"$(SASS_SASSC_VERSION)\"" if ENABLE_COVERAGE nodist_EXTRA_tester_SOURCES = non-existent-file-to-force-CXX-linking.cxx endif TESTS = $(SASS_SPEC_PATH)/sass-spec.rb RB_LOG_COMPILER = ./script/tap-runner AM_RB_LOG_FLAGS = $(RUBY) SASS_TEST_FLAGS = --impl libsass SASS_TEST_FLAGS += -r $(SASS_SPEC_PATH)/spec SASS_TEST_FLAGS += -c $(top_srcdir)/tester$(EXEEXT) LIBSASS_TEST_FLAGS = --impl libsass LIBSASS_TEST_FLAGS += -r $(LIBSASS_SPEC_PATH)/spec LIBSASS_TEST_FLAGS += -c $(top_srcdir)/tester$(EXEEXT) COMPRESSED_TEST_FLAGS = --impl libsass COMPRESSED_TEST_FLAGS += -r $(LIBSASS_SPEC_PATH)/styles/compressed COMPRESSED_TEST_FLAGS += -c $(top_srcdir)/tester$(EXEEXT) COMPRESSED_TEST_FLAGS += --cmd-args="-t compressed" AM_TESTS_ENVIRONMENT = TEST_FLAGS='$(SASS_TEST_FLAGS)' SASS_TESTER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb test: $(SASS_TESTER) $(SASS_TEST_FLAGS) $(SASS_TESTER) $(LIBSASS_TEST_FLAGS) $(SASS_TESTER) $(COMPRESSED_TEST_FLAGS) test_build: $(SASS_TESTER) $(SASS_TEST_FLAGS) $(SASS_TESTER) $(LIBSASS_TEST_FLAGS) $(SASS_TESTER) $(COMPRESSED_TEST_FLAGS) test_full: $(SASS_TESTER) --run-todo $(SASS_TEST_FLAGS) $(SASS_TESTER) --run-todo $(LIBSASS_TEST_FLAGS) $(SASS_TESTER) --run-todo $(COMPRESSED_TEST_FLAGS) test_probe: $(SASS_TESTER) --probe-todo $(SASS_TEST_FLAGS) $(SASS_TESTER) --probe-todo $(LIBSASS_TEST_FLAGS) $(SASS_TESTER) --probe-todo $(COMPRESSED_TEST_FLAGS) test_interactive: $(SASS_TESTER) --interactive $(SASS_TEST_FLAGS) $(SASS_TESTER) --interactive $(LIBSASS_TEST_FLAGS) $(SASS_TESTER) --interactive $(COMPRESSED_TEST_FLAGS) .PHONY: test test_build test_full test_probe endif SUBDIRS = src golibsass-1.0.0/libsass_src/INSTALL000066400000000000000000000000611405214413600170330ustar00rootroot00000000000000// Autotools requires us to have this file. Boo. golibsass-1.0.0/libsass_src/LICENSE000066400000000000000000000023621405214413600170150ustar00rootroot00000000000000 Copyright (C) 2012-2016 by the Sass Open Source Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following files in the spec were taken from the original Ruby Sass project which is copyright Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein and under the same license. golibsass-1.0.0/libsass_src/Makefile000066400000000000000000000264141405214413600174540ustar00rootroot00000000000000OS ?= $(shell uname -s) CC ?= cc CXX ?= c++ RM ?= rm -f CP ?= cp -a MKDIR ?= mkdir RMDIR ?= rmdir WINDRES ?= windres # Solaris/Illumos flavors # ginstall from coreutils ifeq ($(OS),SunOS) INSTALL ?= ginstall endif INSTALL ?= install CFLAGS ?= -Wall CXXFLAGS ?= -Wall LDFLAGS ?= -Wall ifndef COVERAGE CFLAGS += -O2 CXXFLAGS += -O2 LDFLAGS += -O2 else CFLAGS += -O1 -fno-omit-frame-pointer CXXFLAGS += -O1 -fno-omit-frame-pointer LDFLAGS += -O1 -fno-omit-frame-pointer endif CAT ?= $(if $(filter $(OS),Windows_NT),type,cat) ifneq (,$(findstring /cygdrive/,$(PATH))) UNAME := Cygwin else ifneq (,$(findstring Windows_NT,$(OS))) UNAME := Windows else ifneq (,$(findstring mingw32,$(MAKE))) UNAME := Windows else ifneq (,$(findstring MINGW32,$(shell uname -s))) UNAME := Windows else UNAME := $(shell uname -s) endif endif endif endif ifndef LIBSASS_VERSION ifneq ($(wildcard ./.git/ ),) LIBSASS_VERSION ?= $(shell git describe --abbrev=4 --dirty --always --tags) endif ifneq ($(wildcard VERSION),) LIBSASS_VERSION ?= $(shell $(CAT) VERSION) endif endif ifdef LIBSASS_VERSION CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" endif LIBSASS_CPPSTD ?= c++11 CXXFLAGS += -std=$(LIBSASS_CPPSTD) LDFLAGS += -std=$(LIBSASS_CPPSTD) ifeq (Windows,$(UNAME)) ifneq ($(BUILD),shared) STATIC_ALL ?= 1 endif STATIC_LIBGCC ?= 1 STATIC_LIBSTDCPP ?= 1 else STATIC_ALL ?= 0 STATIC_LIBGCC ?= 0 STATIC_LIBSTDCPP ?= 0 endif ifndef SASS_LIBSASS_PATH SASS_LIBSASS_PATH = $(CURDIR) endif ifdef SASS_LIBSASS_PATH CFLAGS += -I $(SASS_LIBSASS_PATH)/include CXXFLAGS += -I $(SASS_LIBSASS_PATH)/include else # this is needed for mingw CFLAGS += -I include CXXFLAGS += -I include endif CFLAGS += $(EXTRA_CFLAGS) CXXFLAGS += $(EXTRA_CXXFLAGS) LDFLAGS += $(EXTRA_LDFLAGS) LDLIBS = -lm ifneq ($(BUILD),shared) ifneq ($(STATIC_LIBSTDCPP),1) LDLIBS += -lstdc++ endif endif # link statically into lib # makes it a lot more portable # increases size by about 50KB ifeq ($(STATIC_ALL),1) LDFLAGS += -static endif ifeq ($(STATIC_LIBGCC),1) LDFLAGS += -static-libgcc endif ifeq ($(STATIC_LIBSTDCPP),1) LDFLAGS += -static-libstdc++ endif ifeq ($(UNAME),Darwin) CFLAGS += -stdlib=libc++ CXXFLAGS += -stdlib=libc++ LDFLAGS += -stdlib=libc++ endif ifneq (Windows,$(UNAME)) ifneq (FreeBSD,$(UNAME)) ifneq (OpenBSD,$(UNAME)) LDFLAGS += -ldl LDLIBS += -ldl endif endif endif ifneq ($(BUILD),shared) BUILD := static endif ifeq ($(DEBUG),1) BUILD := debug-$(BUILD) endif ifndef TRAVIS_BUILD_DIR ifeq ($(OS),SunOS) PREFIX ?= /opt/local else PREFIX ?= /usr/local endif else PREFIX ?= $(TRAVIS_BUILD_DIR) endif SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASS_SPEC_SPEC_DIR ?= spec LIBSASS_SPEC_PATH ?= libsass-spec LIBSASS_SPEC_SPEC_DIR ?= spec SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc RUBY_BIN = ruby RESOURCES = STATICLIB = lib/libsass.a SHAREDLIB = lib/libsass.so LIB_STATIC = $(SASS_LIBSASS_PATH)/lib/libsass.a LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.so ifeq ($(UNAME),Darwin) SHAREDLIB = lib/libsass.dylib LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dylib endif ifeq (Windows,$(UNAME)) SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe RESOURCES += res/resource.rc SHAREDLIB = lib/libsass.dll ifeq (shared,$(BUILD)) CFLAGS += -D ADD_EXPORTS CXXFLAGS += -D ADD_EXPORTS LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dll endif else ifneq (Cygwin,$(UNAME)) CFLAGS += -fPIC CXXFLAGS += -fPIC LDFLAGS += -fPIC endif endif include Makefile.conf OBJECTS = $(addprefix src/,$(SOURCES:.cpp=.o)) COBJECTS = $(addprefix src/,$(CSOURCES:.c=.o)) RCOBJECTS = $(RESOURCES:.rc=.o) DEBUG_LVL ?= NONE CLEANUPS ?= CLEANUPS += $(RCOBJECTS) CLEANUPS += $(COBJECTS) CLEANUPS += $(OBJECTS) CLEANUPS += $(LIBSASS_LIB) all: $(BUILD) debug: $(BUILD) debug-static: LDFLAGS := -g $(filter-out -O2,$(LDFLAGS)) debug-static: CFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CFLAGS)) debug-static: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS)) debug-static: static debug-shared: LDFLAGS := -g $(filter-out -O2,$(LDFLAGS)) debug-shared: CFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CFLAGS)) debug-shared: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS)) debug-shared: shared lib: $(MKDIR) lib lib/libsass.a: $(COBJECTS) $(OBJECTS) | lib $(AR) rcvs $@ $(COBJECTS) $(OBJECTS) lib/libsass.so: $(COBJECTS) $(OBJECTS) | lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS) lib/libsass.dylib: $(COBJECTS) $(OBJECTS) | lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS) \ -install_name @rpath/libsass.dylib lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS) | lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) \ -s -Wl,--subsystem,windows,--out-implib,lib/libsass.a %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< %.o: %.rc $(WINDRES) -i $< -o $@ %.o: %.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< %: %.o static $(CXX) $(CXXFLAGS) -o $@ $+ $(LDFLAGS) $(LDLIBS) install: install-$(BUILD) static: $(STATICLIB) shared: $(SHAREDLIB) $(DESTDIR)$(PREFIX): $(MKDIR) $(DESTDIR)$(PREFIX) $(DESTDIR)$(PREFIX)/lib: | $(DESTDIR)$(PREFIX) $(MKDIR) $(DESTDIR)$(PREFIX)/lib $(DESTDIR)$(PREFIX)/include: | $(DESTDIR)$(PREFIX) $(MKDIR) $(DESTDIR)$(PREFIX)/include $(DESTDIR)$(PREFIX)/include/sass: | $(DESTDIR)$(PREFIX)/include $(MKDIR) $(DESTDIR)$(PREFIX)/include/sass $(DESTDIR)$(PREFIX)/include/%.h: include/%.h \ | $(DESTDIR)$(PREFIX)/include/sass $(INSTALL) -v -m0644 "$<" "$@" install-headers: $(DESTDIR)$(PREFIX)/include/sass.h \ $(DESTDIR)$(PREFIX)/include/sass2scss.h \ $(DESTDIR)$(PREFIX)/include/sass/base.h \ $(DESTDIR)$(PREFIX)/include/sass/version.h \ $(DESTDIR)$(PREFIX)/include/sass/values.h \ $(DESTDIR)$(PREFIX)/include/sass/context.h \ $(DESTDIR)$(PREFIX)/include/sass/functions.h $(DESTDIR)$(PREFIX)/lib/%.a: lib/%.a \ | $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" $(DESTDIR)$(PREFIX)/lib/%.so: lib/%.so \ | $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" $(DESTDIR)$(PREFIX)/lib/%.dll: lib/%.dll \ | $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" $(DESTDIR)$(PREFIX)/lib/%.dylib: lib/%.dylib \ | $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" install-static: $(DESTDIR)$(PREFIX)/lib/libsass.a install-shared: $(DESTDIR)$(PREFIX)/$(SHAREDLIB) \ install-headers $(SASSC_BIN): $(BUILD) $(MAKE) -C $(SASS_SASSC_PATH) build-$(BUILD) sassc: $(SASSC_BIN) $(SASSC_BIN) -v version: $(SASSC_BIN) $(SASSC_BIN) -v test: test_build $(SASS_SPEC_PATH): git clone https://github.com/sass/sass-spec $(SASS_SPEC_PATH) $(LIBSASS_SPEC_PATH): git clone https://github.com/mgreter/libsass-spec $(LIBSASS_SPEC_PATH) test_build: $(SASSC_BIN) $(SASS_SPEC_PATH) $(LIBSASS_SPEC_PATH) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \ $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR)" \ $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/compressed -t compressed" \ $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/compressed $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/nested -t nested" \ $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/nested test_full: $(SASSC_BIN) $(SASS_SPEC_PATH) $(LIBSASS_SPEC_PATH) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \ --run-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR)" \ --run-todo $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/compressed -t compressed" \ --run-todo $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/compressed $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/nested -t nested" \ --run-todo $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/nested test_probe: $(SASSC_BIN) $(SASS_SPEC_PATH) $(LIBSASS_SPEC_PATH) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \ --probe-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR)" \ --probe-todo $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/compressed -t compressed" \ --probe-todo $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/compressed $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/nested -t nested" \ --probe-todo $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/nested test_interactive: $(SASSC_BIN) $(SASS_SPEC_PATH) $(LIBSASS_SPEC_PATH) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)" \ --interactive $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR)" \ --interactive $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/$(LIBSASS_SPEC_SPEC_DIR) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/compressed -t compressed" \ --interactive $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/compressed $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) --impl libsass \ --cmd-args "-I $(LIBSASS_SPEC_PATH)/styles/nested -t nested" \ --interactive $(LOG_FLAGS) $(LIBSASS_SPEC_PATH)/styles/nested clean-objects: | lib -$(RM) lib/*.a lib/*.so lib/*.dll lib/*.dylib lib/*.la -$(RMDIR) lib clean: clean-objects $(RM) $(CLEANUPS) clean-all: $(MAKE) -C $(SASS_SASSC_PATH) clean lib-file: lib-file-$(BUILD) lib-opts: lib-opts-$(BUILD) lib-file-static: @echo $(LIB_STATIC) lib-file-shared: @echo $(LIB_SHARED) lib-opts-static: @echo -L"$(SASS_LIBSASS_PATH)/lib" lib-opts-shared: @echo -L"$(SASS_LIBSASS_PATH)/lib -lsass" .PHONY: all static shared sassc \ version install-headers \ clean clean-all clean-objects \ debug debug-static debug-shared \ install install-static install-shared \ lib-opts lib-opts-shared lib-opts-static \ lib-file lib-file-shared lib-file-static \ test test_build test_full test_probe .DELETE_ON_ERROR: golibsass-1.0.0/libsass_src/Makefile.conf000066400000000000000000000054221405214413600203740ustar00rootroot00000000000000# this is merely a common Makefile multiple implementers can use # bigger files (in terms of compile time) tend to go to the top, # so they don't end up as the last compile unit when compiling # in parallel. But we also want to mix them a little too avoid # heavy RAM usage peaks. Other than that the order is arbitrary. INCFILES = \ sass.h \ sass2scss.h \ sass/base.h \ sass/context.h \ sass/functions.h \ sass/values.h \ sass/version.h HPPFILES = \ ast.hpp \ ast2c.hpp \ ast_def_macros.hpp \ ast_fwd_decl.hpp \ ast_helpers.hpp \ ast_selectors.hpp \ ast_supports.hpp \ ast_values.hpp \ backtrace.hpp \ base64vlq.hpp \ bind.hpp \ c2ast.hpp \ check_nesting.hpp \ color_maps.hpp \ constants.hpp \ context.hpp \ cssize.hpp \ dart_helpers.hpp \ debug.hpp \ debugger.hpp \ emitter.hpp \ environment.hpp \ error_handling.hpp \ eval.hpp \ expand.hpp \ extender.hpp \ extension.hpp \ file.hpp \ fn_colors.hpp \ fn_lists.hpp \ fn_maps.hpp \ fn_miscs.hpp \ fn_numbers.hpp \ fn_selectors.hpp \ fn_strings.hpp \ fn_utils.hpp \ inspect.hpp \ json.hpp \ kwd_arg_macros.hpp \ lexer.hpp \ listize.hpp \ mapping.hpp \ memory.hpp \ MurmurHash2.hpp \ operation.hpp \ operators.hpp \ ordered_map.hpp \ output.hpp \ parser.hpp \ permutate.hpp \ plugins.hpp \ position.hpp \ prelexer.hpp \ remove_placeholders.hpp \ sass.hpp \ sass_context.hpp \ sass_functions.hpp \ sass_values.hpp \ settings.hpp \ source.hpp \ source_data.hpp \ source_map.hpp \ stylesheet.hpp \ to_value.hpp \ units.hpp \ utf8_string.hpp \ util.hpp \ util_string.hpp \ values.hpp \ memory/allocator.hpp \ memory/config.hpp \ memory/memory_pool.hpp \ memory/shared_ptr.hpp SOURCES = \ ast.cpp \ ast_values.cpp \ ast_supports.cpp \ ast_sel_cmp.cpp \ ast_sel_unify.cpp \ ast_sel_super.cpp \ ast_sel_weave.cpp \ ast_selectors.cpp \ context.cpp \ constants.cpp \ fn_utils.cpp \ fn_miscs.cpp \ fn_maps.cpp \ fn_lists.cpp \ fn_colors.cpp \ fn_numbers.cpp \ fn_strings.cpp \ fn_selectors.cpp \ color_maps.cpp \ environment.cpp \ ast_fwd_decl.cpp \ bind.cpp \ file.cpp \ util.cpp \ util_string.cpp \ json.cpp \ units.cpp \ values.cpp \ plugins.cpp \ source.cpp \ position.cpp \ lexer.cpp \ parser.cpp \ parser_selectors.cpp \ prelexer.cpp \ eval.cpp \ eval_selectors.cpp \ expand.cpp \ listize.cpp \ cssize.cpp \ extender.cpp \ extension.cpp \ stylesheet.cpp \ output.cpp \ inspect.cpp \ emitter.cpp \ check_nesting.cpp \ remove_placeholders.cpp \ sass.cpp \ sass_values.cpp \ sass_context.cpp \ sass_functions.cpp \ sass2scss.cpp \ backtrace.cpp \ operators.cpp \ ast2c.cpp \ c2ast.cpp \ to_value.cpp \ source_map.cpp \ error_handling.cpp \ memory/allocator.cpp \ memory/shared_ptr.cpp \ utf8_string.cpp \ base64vlq.cpp CSOURCES = \ cencode.c golibsass-1.0.0/libsass_src/Readme.md000066400000000000000000000132751405214413600175340ustar00rootroot00000000000000LibSass - Sass compiler written in C++ ====================================== Currently maintained by Marcel Greter ([@mgreter]) and Michael Mifsud ([@xzyfer]) Originally created by Aaron Leung ([@akhleung]) and Hampton Catlin ([@hcatlin]) [![GitHub CI](https://github.com/sass/libsass/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/sass/libsass/actions/workflows/build-and-test.yml "GitHub CI") [![Windows CI](https://ci.appveyor.com/api/projects/status/github/sass/libsass?svg=true)](https://ci.appveyor.com/project/sass/libsass/branch/master "Appveyor CI") [![Coverage Status](https://img.shields.io/coveralls/sass/libsass.svg)](https://coveralls.io/r/sass/libsass?branch=master "Code coverage of spec tests") [![Percentage of issues still open](http://isitmaintained.com/badge/open/sass/libsass.svg)](http://isitmaintained.com/project/sass/libsass "Percentage of issues still open") [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/sass/libsass.svg)](http://isitmaintained.com/project/sass/libsass "Average time to resolve an issue") [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=283068)](https://www.bountysource.com/trackers/283068-libsass?utm_source=283068&utm_medium=shield&utm_campaign=TRACKER_BADGE "Bountysource") [![Join us](https://libsass-slack.herokuapp.com/badge.svg)](https://libsass-slack.herokuapp.com/ "Slack communication channels") **Warning:** [LibSass is deprecated](https://sass-lang.com/blog/libsass-is-deprecated). While it will continue to receive maintenance releases indefinitely, there are no plans to add additional features or compatibility with any new CSS or Sass features. Projects that still use it should move onto [Dart Sass](https://sass-lang.com/dart-sass). [LibSass](https://github.com/sass/libsass "LibSass GitHub Project") is just a library! If you want to use LibSass to compile Sass, you need an implementer. Some implementations are only bindings into other programming languages. But most also ship with a command line interface (CLI) you can use directly. There is also [SassC](https://github.com/sass/sassc), which is the official lightweight CLI tool built by the same people as LibSass. ### Excerpt of Supported Implementations: - https://github.com/sass/node-sass (Node.js) - https://github.com/sass/perl-libsass (Perl) - https://github.com/sass/libsass-python (Python) - https://github.com/wellington/go-libsass (Go) - https://github.com/sass/sassc-ruby (Ruby) - https://github.com/sass/libsass-net (C#) - https://github.com/medialize/sass.js (JS) - https://github.com/bit3/jsass (Java) - https://github.com/scottdavis/sass.ex (Elixir) - https://github.com/Youimmi/sass_compiler (Elixir) This list does not say anything about the quality of either the listed or not listed [implementations](docs/implementations.md)! The authors of the listed projects above are just known to work regularly together with LibSass developers. About ----- LibSass is a C++ port of the original Ruby Sass CSS compiler with a [C API](docs/api-doc.md). We coded LibSass with portability and efficiency in mind. You can expect LibSass to be a lot faster than Ruby Sass and on par or faster than the best alternative CSS compilers around. Developing ---------- As noted above, the LibSass repository does not contain any binaries or other way to execute LibSass. Therefore, you need an implementer to develop LibSass. Easiest is to start with the official [SassC](http://github.com/sass/sassc) CLI wrapper. It is *guaranteed* to compile with the latest code in LibSass master, since it is also used in the CI process. There is no limitation here, as you may use any other LibSass implementer to test your LibSass branch! Testing ------- Since LibSass is a pure library, tests are run through the [Sass-Spec](https://github.com/sass/sass-spec) project using the [SassC](http://github.com/sass/sassc) CLI wrapper. To run the tests against LibSass while developing, you can run `./script/spec`. This will clone SassC and Sass-Spec under the project folder and then run the Sass-Spec test suite. You may want to update the clones to ensure you have the latest version. Note that the scripts in the `./script` folder are mainly intended for our CI needs. Building -------- To build LibSass you need GCC 4.7+ or Clang/LLVM. If your OS is older, you may need to upgrade them first (or install clang as an alternative). On Windows, you need MinGW with GCC 4.7+ or VS 2013 Update 4+. It is also possible to build LibSass with Clang/LLVM on Windows with various build chains and/or command line interpreters. See the [build docs for further instructions](docs/build.md)! Compatibility ------------- For all intents and purposes LibSass is fully compatible with the Sass language spec. Any known differences can be found as open issues. About Sass ---------- Sass is a CSS pre-processor language to add on exciting, new, awesome features to CSS. Sass was the first language of its kind and by far the most mature and up to date codebase. Sass was originally conceived of by the co-creator of this library, Hampton Catlin ([@hcatlin]). Most of the language has been the result of years of work by Natalie Weizenbaum ([@nex3]) and Chris Eppstein ([@chriseppstein]). For more information about Sass itself, please visit https://sass-lang.com Initial development of LibSass by Aaron Leung and Hampton Catlin was supported by [Moovweb](http://www.moovweb.com). Licensing --------- Our [MIT license](LICENSE) is designed to be as simple and liberal as possible. [@hcatlin]: https://github.com/hcatlin [@akhleung]: https://github.com/akhleung [@chriseppstein]: https://github.com/chriseppstein [@nex3]: https://github.com/nex3 [@mgreter]: https://github.com/mgreter [@xzyfer]: https://github.com/xzyfer golibsass-1.0.0/libsass_src/SECURITY.md000066400000000000000000000005621405214413600176010ustar00rootroot00000000000000Serious about security ====================== The LibSass team recognizes the important contributions the security research community can make. We therefore encourage reporting security issues with the code contained in this repository. If you believe you have discovered a security vulnerability, please report it at https://hackerone.com/libsass instead of GitHub. golibsass-1.0.0/libsass_src/appveyor.yml000066400000000000000000000062531405214413600204030ustar00rootroot00000000000000os: Visual Studio 2013 environment: CTEST_OUTPUT_ON_FAILURE: 1 ruby_version: 24-x64 TargetPath: sassc/bin/sassc.exe matrix: - Compiler: msvc Config: Release Platform: Win32 - Compiler: msvc Config: Debug Platform: Win32 - Compiler: msvc Config: Release Platform: Win64 - Compiler: mingw Build: static - Compiler: mingw Build: shared cache: - C:\Ruby%ruby_version%\lib\ruby\gems - C:\mingw64 # Uncomment to debug hanging builds via RDP, password can be found/set under Environment-Variables in appveyor settings! # # init: # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # # on_finish: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) install: - git clone https://github.com/sass/sassc.git - git clone https://github.com/sass/sass-spec.git - set PATH=C:\Ruby%ruby_version%\bin;%PATH% - ps: | if(!(gem which minitest 2>$nul)) { gem install minitest --no-ri --no-rdoc } if(!(gem which hrx 2>$nul)) { gem install hrx --no-ri --no-rdoc } if ($env:Compiler -eq "mingw" -AND -Not (Test-Path "C:\mingw64")) { # Install MinGW. $file = "x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z" wget https://bintray.com/artifact/download/drewwells/generic/$file -OutFile $file &7z x -oC:\ $file > $null } - set PATH=C:\mingw64\bin;%PATH% - set CC=gcc build_script: - ps: | if ($env:Compiler -eq "mingw") { mingw32-make -j4 sassc } else { msbuild /m:4 /p:"Configuration=$env:Config;Platform=$env:Platform" sassc\win\sassc.sln } # print the branding art mv script/branding script/branding.ps1 script/branding.ps1 # print the version info &$env:TargetPath -v ruby -v test_script: - ps: | $PRNR = $env:APPVEYOR_PULL_REQUEST_NUMBER if ($PRNR) { echo "Fetching info for PR $PRNR" wget https://api.github.com/repos/sass/libsass/pulls/$PRNR -OutFile pr.json $json = cat pr.json -Raw $SPEC_PR = [regex]::match($json,'sass\/sass-spec(#|\/pull\/)([0-9]+)').Groups[2].Value if ($SPEC_PR) { echo "Checkout sass spec PR $SPEC_PR" git -C sass-spec fetch -q -u origin pull/$SPEC_PR/head:ci-spec-pr-$SPEC_PR git -C sass-spec checkout -q --force ci-spec-pr-$SPEC_PR } } $env:TargetPath = Join-Path $pwd.Path $env:TargetPath If (Test-Path "$env:TargetPath") { ruby sass-spec/sass-spec.rb --probe-todo --impl libsass -c $env:TargetPath -s sass-spec/spec if(-not($?)) { echo "sass-spec tests failed" exit 1 } } else { echo "spec runner not found (compile error?)" exit 1 } Write-Host "Explicitly testing the case when cwd has Unicode characters: " -nonewline # See comments in gh-1774 for details. cd test/e2e/unicode-pwd/Sáss-UŢF8/ &$env:TargetPath ./input.scss 2>&1>$null if(-not($?)) { echo "Failed!" exit 1 } else { echo "Success!" } golibsass-1.0.0/libsass_src/configure.ac000066400000000000000000000102541405214413600202750ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) AC_INIT([libsass], m4_esyscmd_s([./version.sh]), [support@moovweb.com]) AC_CONFIG_SRCDIR([src/ast.hpp]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_FILES([include/sass/version.h]) AC_CONFIG_AUX_DIR([script]) # These are flags passed to automake # Though they look like gcc flags! AM_INIT_AUTOMAKE([foreign parallel-tests -Wall]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([no])]) # Checks for programs. AC_PROG_CC AC_PROG_CXX AC_LANG_PUSH([C]) AC_LANG_PUSH([C++]) AC_GNU_SOURCE # Check fails on Travis, but it works fine # AX_CXX_COMPILE_STDCXX_11([ext],[optional]) AC_CHECK_TOOL([AR], [ar], [false]) AC_CHECK_TOOL([DLLTOOL], [dlltool], [false]) AC_CHECK_TOOL([DLLWRAP], [dllwrap], [false]) AC_CHECK_TOOL([WINDRES], [windres], [false]) m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) LT_INIT([dlopen]) # Checks for header files. AC_CHECK_HEADERS([unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # Checks for library functions. AC_FUNC_MALLOC AC_CHECK_FUNCS([floor getcwd strtol]) # Checks for testing. AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests], [enable testing the build]), [enable_tests="$enableval"], [enable_tests=no]) AS_CASE([$host], [*-*-mingw*], [is_mingw32=yes], [is_mingw32=no]) AM_CONDITIONAL(COMPILER_IS_MINGW32, test "x$is_mingw32" = "xyes") dnl The dlopen() function is in the C library for *BSD and in dnl libdl on GLIBC-based systems if test "x$is_mingw32" != "xyes"; then AC_SEARCH_LIBS([dlopen], [dl dld], [], [ AC_MSG_ERROR([unable to find the dlopen() function]) ]) fi if test "x$enable_tests" = "xyes"; then AC_PROG_CC AC_PROG_AWK # test need minitest gem AC_PATH_PROG(RUBY, [ruby]) AC_PATH_PROG(TAPOUT, [tapout]) AC_REQUIRE_AUX_FILE([tap-driver]) AC_REQUIRE_AUX_FILE([tap-runner]) AC_ARG_WITH(sassc-dir, AS_HELP_STRING([--with-sassc-dir=], [specify directory of sassc sources for testing (default: sassc)]), [sassc_dir="$withval"], [sassc_dir="sassc"]) AC_CHECK_FILE([$sassc_dir/sassc.c], [], [ AC_MSG_ERROR([Unable to find sassc directory. You must clone the sassc repository in this directory or specify the --with-sassc-dir= argument. ]) ]) SASS_SASSC_PATH=$sassc_dir AC_SUBST(SASS_SASSC_PATH) AC_ARG_WITH(sass-spec-dir, AS_HELP_STRING([--with-sass-spec-dir=], [specify directory of sass-spec for testing (default: sass-spec)]), [sass_spec_dir="$withval"], [sass_spec_dir="sass-spec"]) AC_CHECK_FILE([$sass_spec_dir/sass-spec.rb], [], [ AC_MSG_ERROR([Unable to find sass-spec directory. You must clone the sass-spec repository in this directory or specify the --with-sass-spec-dir= argument. ]) ]) # Automake doesn't like its tests in an absolute path, so we make it relative. case $sass_spec_dir in /*) SASS_SPEC_PATH=`$RUBY -e "require 'pathname'; puts Pathname.new('$sass_spec_dir').relative_path_from(Pathname.new('$PWD')).to_s"` ;; *) SASS_SPEC_PATH="$sass_spec_dir" ;; esac AC_SUBST(SASS_SPEC_PATH) else # we do not really need these paths for non test build # but automake may error if we do not define them here SASS_SPEC_PATH=sass-spec SASS_SASSC_PATH=sassc AC_SUBST(SASS_SPEC_PATH) AC_SUBST(SASS_SASSC_PATH) fi AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "xyes") AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], [enable coverage report for test suite])], [enable_cov=$enableval], [enable_cov=no]) if test "x$enable_cov" = "xyes"; then AC_CHECK_PROG(GCOV, gcov, gcov) # Remove all optimization flags from C[XX]FLAGS changequote({,}) CFLAGS=`echo "$CFLAGS -O1 -fno-omit-frame-pointer" | $SED -e 's/-O[0-9]*//g'` CXXFLAGS=`echo "$CXXFLAGS -O1 -fno-omit-frame-pointer" | $SED -e 's/-O[0-9]*//g'` changequote([,]) AC_SUBST(GCOV) fi AM_CONDITIONAL(ENABLE_COVERAGE, test "x$enable_cov" = "xyes") AC_SUBST(PACKAGE_VERSION) AC_MSG_NOTICE([Building libsass ($VERSION)]) AC_CONFIG_FILES([GNUmakefile src/GNUmakefile src/support/libsass.pc]) AC_OUTPUT golibsass-1.0.0/libsass_src/contrib/000077500000000000000000000000001405214413600174455ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/contrib/libsass.spec000066400000000000000000000023221405214413600217600ustar00rootroot00000000000000Name: libsass Version: %{version} Release: 1%{?dist} Summary: A C/C++ implementation of a Sass compiler License: MIT URL: http://libsass.org Source0: %{name}-%{version}.tar.gz BuildRequires: gcc-c++ >= 4.7 BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool %description LibSass is a C/C++ port of the Sass engine. The point is to be simple, fast, and easy to integrate. %package devel Summary: Development files for %{name} Requires: %{name}%{?_isa} = %{version}-%{release} %description devel The %{name}-devel package contains libraries and header files for developing applications that use %{name}. %prep %setup -q autoreconf --force --install %build %configure --disable-static \ --disable-tests \ --enable-shared make %{?_smp_mflags} %install %make_install find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %doc Readme.md LICENSE %{_libdir}/*.so.* %files devel %doc %{_includedir}/* %{_libdir}/*.so %{_libdir}/pkgconfig/*.pc %changelog * Tue Feb 10 2015 Gawain Lynch - 3.1.0-1 - Initial SPEC file golibsass-1.0.0/libsass_src/contrib/plugin.cpp000066400000000000000000000041051405214413600214470ustar00rootroot00000000000000#include #include #include #include // gcc: g++ -shared plugin.cpp -o plugin.so -fPIC -Llib -lsass // mingw: g++ -shared plugin.cpp -o plugin.dll -Llib -lsass extern "C" const char* ADDCALL libsass_get_version() { return libsass_version(); } union Sass_Value* custom_function(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Compiler* comp) { // get context/option struct associated with this compiler struct Sass_Context* ctx = sass_compiler_get_context(comp); struct Sass_Options* opts = sass_compiler_get_options(comp); // get the cookie from function descriptor void* cookie = sass_function_get_cookie(cb); // we actually abuse the void* to store an "int" return sass_make_number((intptr_t)cookie, "px"); } extern "C" Sass_Function_List ADDCALL libsass_load_functions() { // allocate a custom function caller Sass_Function_Entry c_func = sass_make_function("foo()", custom_function, (void*)42); // create list of all custom functions Sass_Function_List fn_list = sass_make_function_list(1); // put the only function in this plugin to the list sass_function_set_list_entry(fn_list, 0, c_func); // return the list return fn_list; } Sass_Import_List custom_importer(const char* cur_path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // get the cookie from importer descriptor void* cookie = sass_importer_get_cookie(cb); // create a list to hold our import entries Sass_Import_List incs = sass_make_import_list(1); // create our only import entry (route path back) incs[0] = sass_make_import_entry(cur_path, 0, 0); // return imports return incs; } extern "C" Sass_Importer_List ADDCALL libsass_load_importers() { // allocate a custom function caller Sass_Importer_Entry c_imp = sass_make_importer(custom_importer, - 99, (void*)42); // create list of all custom functions Sass_Importer_List imp_list = sass_make_importer_list(1); // put the only function in this plugin to the list sass_importer_set_list_entry(imp_list, 0, c_imp); // return the list return imp_list; } golibsass-1.0.0/libsass_src/docs/000077500000000000000000000000001405214413600167355ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/docs/README.md000066400000000000000000000032761405214413600202240ustar00rootroot00000000000000## LibSass documentation LibSass is just a library. To run the code locally (i.e. to compile your stylesheets), you need an implementer. SassC is an implementer written in C. There are a number of other implementations of LibSass - for example NodeJS. We encourage you to write your own port - the whole point of LibSass is that we want to bring Sass to many other languages! ## LibSass road-map Since ruby-sass was retired in 2019 in favor of dart-sass, we slowly move toward full compatibility with the latest Sass specifications, although features like the module `@use` system may take a bit longer to add. ### Implementing LibSass If you're interested in implementing LibSass in your own project see the [API Documentation](api-doc.md) which now includes implementing your own [Sass functions](api-function.md). You may wish to [look at other implementations](implementations.md) for your language of choice. ### Contributing to LibSass | Issue Tracker | Issue Triage | Community Guidelines | |-------------------|----------------------------------|-----------------------------| | We're always needing help, so check out our issue tracker, help some people out, and read our article on [Contributing](contributing.md)! It's got all the details on what to do! | To help understand the process of triaging bugs, have a look at our [Issue-Triage](triage.md) document. | Oh, and don't forget we always follow [Sass Community Guidelines](https://sass-lang.com/community-guidelines). Be nice and everyone else will be nice too! | ### Building LibSass Please refer to the steps on [Building LibSass](build.md) ### Developing LibSass Please refer to [Developing LibSass](developing.md) golibsass-1.0.0/libsass_src/docs/allocator.md000066400000000000000000000114661405214413600212470ustar00rootroot00000000000000## Custom memory allocator LibSass comes with a custom memory allocator to improve performance. First included in LibSass 3.6 and currently disabled by default. Needs to be enabled by defining `SASS_CUSTOM_ALLOCATOR`. ### Overview The allocator is a pool/arena allocator with a free-list on top. The memory usage pattern of LibSass fits this implementation very well. Every compilation tends to accumulate memory and only releasing some items from time to time, but the overall memory consumption will mostly grow until the compilation is finished. This helps us to keep the implementation as simple as possible, since we don't need to release much memory back to the system and can re-use it instead. ### Arenas Each arena is allocated in a (compile time) fixed size. Every allocation request is served from the current arena. We basically slice up the arena into different sized chunks. Arenas are never returned to the system until the whole compilation is finished. ### Slices A memory slice is a part of an arena. Once the system requests a sized memory chunk we check the current arena if there is enough space to hold it. If not a new arena is allocated. Then we return a pointer into that arena and mark the space as being used. Each slice also has a header which is invisible to the requester as it lies before the pointer address that we returned. This is used for book-keeping. ### Free-lists (or buckets) Once a memory slice is returned to the allocator it will not be released. It will instead be put on the free list. We keep a fixed number of free lists, one for every possible chunk size. Since chunk sizes are memory aligned, we can get the free-list index (aka `bucket`) very quickly (`size/alignment`). For further readings see https://en.wikipedia.org/wiki/Free_list. ### Chunk-sizes Since arenas are of fixed size we need to make sure that only small enough chunks get served from it. This also helps to keep implementation simple, as we can statically declare some structures for book-keeping. Allocations that are too big to be tracked on a free-list will be patched directly to malloc and free. This is the case when the bucket index would be bigger than `SassAllocatorBuckets`. ### Thread-safety This implementation is not thread-safe by design. Making it thread-safe would certainly be possible, but it would come at a (performance) price. Also it is not needed given the memory usage pattern of LibSass. Instead we should make sure that memory pools are local to each thread. ### Implementation obstacles Since memory allocation is a core part of C++ itself, we get into various difficult territories. This has specially proven true in regard of static variable initialization and destruction order. E.g when we have a static string with custom allocator. It might be that it is initialized before the thread local memory pool. On the other hand it's also possible that the memory pool is destroyed before another static string wants to give back its memory to the pool. I tried hard to work around those issues. Mainly by only using thead local POD (plain old data) objects. See https://isocpp.org/wiki/faq/ctors#static-init-order ### Performance gains My tests indicate that the custom allocator brings around 15% performance enhancement for complex cases (I used the bolt-bench to measure it). Once more get optimized, the custom allocator can bring up to 30% improvement. This comes at a small cost of a few percent of overall memory usage. This can be tweaked, but the sweet spot for now seems to be: ```c // How many buckets should we have for the free-list // Determines when allocations go directly to malloc/free // For maximum size of managed items multiply by alignment #define SassAllocatorBuckets 512 // The size of the memory pool arenas in bytes. #define SassAllocatorArenaSize (1024 * 256) ``` These can be found in `settings.hpp`. ### Memory overhead Both settings `SassAllocatorBuckets` and `SassAllocatorArenaSize` need to be set in relation to each other. Assuming the memory alignment on the platform is 8 bytes, the maximum chunk size that can be handled is 4KB (512*8B). If the arena size is too close to this value, you may leave a lot of RAM unused. Once an arena can't fullfil the current request, it is put away and a new one is allocated. We don't keep track of unused space in previous arenas, as it bloats the code and costs precious cpu time. By setting the values carefully we can avoid the cost and still provide reasonable memory overhead. In the worst scenario we loose around 1.5% for the default settings (4K of 256K). ### Further improvements It is worth to check if we can re-use the space of old arenas somehow without scarifying to much performance. Additionally we could check free-lists of bigger chunks sizes to satisfy an allocation request. But both would need to be checked for performance impact and their actual gain.golibsass-1.0.0/libsass_src/docs/api-context-example.md000066400000000000000000000047051405214413600231510ustar00rootroot00000000000000## Example for `data_context` ```C:data.c #include #include "sass/context.h" int main( int argc, const char* argv[] ) { // LibSass will take control of data you pass in // Therefore we need to make a copy of static data char* text = sass_copy_c_string("a{b:c;}"); // Normally you'll load data into a buffer from i.e. the disk. // Use `sass_alloc_memory` to get a buffer to pass to LibSass // then fill it with data you load from disk or somewhere else. // create the data context and get all related structs struct Sass_Data_Context* data_ctx = sass_make_data_context(text); struct Sass_Context* ctx = sass_data_context_get_context(data_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // configure some options ... sass_option_set_precision(ctx_opt, 10); // context is set up, call the compile step now int status = sass_compile_data_context(data_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_data_context(data_ctx); // exit status return status; } ``` ### Compile data.c ```bash gcc -c data.c -o data.o gcc -o sample data.o -lsass echo "foo { margin: 21px * 2; }" > foo.scss ./sample foo.scss => "foo { margin: 42px }" ``` ## Example for `file_context` ```C:file.c #include #include "sass/context.h" int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // configure some options ... sass_option_set_precision(ctx_opt, 10); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` ### Compile file.c ```bash gcc -c file.c -o file.o gcc -o sample file.o -lsass echo "foo { margin: 21px * 2; }" > foo.scss ./sample foo.scss => "foo { margin: 42px }" ``` golibsass-1.0.0/libsass_src/docs/api-context-internal.md000066400000000000000000000066621405214413600233360ustar00rootroot00000000000000```C // Input behaviours enum Sass_Input_Style { SASS_CONTEXT_NULL, SASS_CONTEXT_FILE, SASS_CONTEXT_DATA, SASS_CONTEXT_FOLDER }; // sass config options structure struct Sass_Inspect_Options { // Output style for the generated css code // A value from above SASS_STYLE_* constants enum Sass_Output_Style output_style; // Precision for fractional numbers int precision; }; // sass config options structure struct Sass_Output_Options : Sass_Inspect_Options { // String to be used for indentation const char* indent; // String to be used to for line feeds const char* linefeed; // Emit comments in the generated CSS indicating // the corresponding source line. bool source_comments; }; // sass config options structure struct Sass_Options : Sass_Output_Options { // embed sourceMappingUrl as data uri bool source_map_embed; // embed include contents in maps bool source_map_contents; // create file urls for sources bool source_map_file_urls; // Disable sourceMappingUrl in css output bool omit_source_map_url; // Treat source_string as sass (as opposed to scss) bool is_indented_syntax_src; // The input path is used for source map // generation. It can be used to define // something with string compilation or to // overload the input file path. It is // set to "stdin" for data contexts and // to the input file on file contexts. char* input_path; // The output path is used for source map // generation. LibSass will not write to // this file, it is just used to create // information in source-maps etc. char* output_path; // Colon-separated list of paths // Semicolon-separated on Windows // Maybe use array interface instead? char* include_path; char* plugin_path; // Include paths (linked string list) struct string_list* include_paths; // Plugin paths (linked string list) struct string_list* plugin_paths; // Path to source map file // Enables source map generation // Used to create sourceMappingUrl char* source_map_file; // Directly inserted in source maps char* source_map_root; // Custom functions that can be called from sccs code Sass_Function_List c_functions; // Callback to overload imports Sass_Importer_List c_importers; // List of custom headers Sass_Importer_List c_headers; }; // base for all contexts struct Sass_Context : Sass_Options { // store context type info enum Sass_Input_Style type; // generated output data char* output_string; // generated source map json char* source_map_string; // error status int error_status; char* error_json; char* error_text; char* error_message; // error position char* error_file; size_t error_line; size_t error_column; char* error_src; // report imported files char** included_files; }; // struct for file compilation struct Sass_File_Context : Sass_Context { // no additional fields required // input_path is already on options }; // struct for data compilation struct Sass_Data_Context : Sass_Context { // provided source string char* source_string; char* srcmap_string; }; // Compiler states enum Sass_Compiler_State { SASS_COMPILER_CREATED, SASS_COMPILER_PARSED, SASS_COMPILER_EXECUTED }; // link c and cpp context struct Sass_Compiler { // progress status Sass_Compiler_State state; // original c context Sass_Context* c_ctx; // Sass::Context Sass::Context* cpp_ctx; // Sass::Block Sass::Block_Obj root; }; ``` golibsass-1.0.0/libsass_src/docs/api-context.md000066400000000000000000000257101405214413600215170ustar00rootroot00000000000000Sass Contexts come in two flavors: - `Sass_File_Context` - `Sass_Data_Context` ### Basic Usage ```C #include "sass/context.h" ``` ***Sass_Options*** ```C // Precision for fractional numbers int precision; ``` ```C // Output style for the generated css code // A value from above SASS_STYLE_* constants int output_style; ``` ```C // Emit comments in the generated CSS indicating // the corresponding source line. bool source_comments; ``` ```C // embed sourceMappingUrl as data uri bool source_map_embed; ``` ```C // embed include contents in maps bool source_map_contents; ``` ```C // create file urls for sources bool source_map_file_urls; ``` ```C // Disable sourceMappingUrl in css output bool omit_source_map_url; ``` ```C // Treat source_string as sass (as opposed to scss) bool is_indented_syntax_src; ``` ```C // The input path is used for source map // generating. It can be used to define // something with string compilation or to // overload the input file path. It is // set to "stdin" for data contexts and // to the input file on file contexts. char* input_path; ``` ```C // The output path is used for source map // generating. LibSass will not write to // this file, it is just used to create // information in source-maps etc. char* output_path; ``` ```C // String to be used for indentation const char* indent; ``` ```C // String to be used to for line feeds const char* linefeed; ``` ```C // Colon-separated list of paths // Semicolon-separated on Windows char* include_path; char* plugin_path; ``` ```C // Additional include paths // Must be null delimited char** include_paths; char** plugin_paths; ``` ```C // Path to source map file // Enables the source map generating // Used to create sourceMappingUrl char* source_map_file; ``` ```C // Directly inserted in source maps char* source_map_root; ``` ```C // Custom functions that can be called from Sass code Sass_C_Function_List c_functions; ``` ```C // Callback to overload imports Sass_C_Import_Callback importer; ``` ***Sass_Context*** ```C // store context type info enum Sass_Input_Style type; ```` ```C // generated output data char* output_string; ``` ```C // generated source map json char* source_map_string; ``` ```C // error status int error_status; char* error_json; char* error_text; char* error_message; // error position char* error_file; size_t error_line; size_t error_column; char* error_src; ``` ```C // report imported files char** included_files; ``` ***Sass_File_Context*** ```C // no additional fields required // input_path is already on options ``` ***Sass_Data_Context*** ```C // provided source string char* source_string; ``` ### Sass Context API ```C // Forward declaration struct Sass_Compiler; // Forward declaration struct Sass_Options; struct Sass_Context; // : Sass_Options struct Sass_File_Context; // : Sass_Context struct Sass_Data_Context; // : Sass_Context // Create and initialize an option struct struct Sass_Options* sass_make_options (void); // Create and initialize a specific context struct Sass_File_Context* sass_make_file_context (const char* input_path); struct Sass_Data_Context* sass_make_data_context (char* source_string); // Call the compilation step for the specific context int sass_compile_file_context (struct Sass_File_Context* ctx); int sass_compile_data_context (struct Sass_Data_Context* ctx); // Create a sass compiler instance for more control struct Sass_Compiler* sass_make_file_compiler (struct Sass_File_Context* file_ctx); struct Sass_Compiler* sass_make_data_compiler (struct Sass_Data_Context* data_ctx); // Execute the different compilation steps individually // Useful if you only want to query the included files int sass_compiler_parse (struct Sass_Compiler* compiler); int sass_compiler_execute (struct Sass_Compiler* compiler); // Release all memory allocated with the compiler // This does _not_ include any contexts or options void sass_delete_compiler (struct Sass_Compiler* compiler); void sass_delete_options(struct Sass_Options* options); // Release all memory allocated and also ourself void sass_delete_file_context (struct Sass_File_Context* ctx); void sass_delete_data_context (struct Sass_Data_Context* ctx); // Getters for Context from specific implementation struct Sass_Context* sass_file_context_get_context (struct Sass_File_Context* file_ctx); struct Sass_Context* sass_data_context_get_context (struct Sass_Data_Context* data_ctx); // Getters for Context_Options from Sass_Context struct Sass_Options* sass_context_get_options (struct Sass_Context* ctx); struct Sass_Options* sass_file_context_get_options (struct Sass_File_Context* file_ctx); struct Sass_Options* sass_data_context_get_options (struct Sass_Data_Context* data_ctx); void sass_file_context_set_options (struct Sass_File_Context* file_ctx, struct Sass_Options* opt); void sass_data_context_set_options (struct Sass_Data_Context* data_ctx, struct Sass_Options* opt); // Getters for Sass_Context values const char* sass_context_get_output_string (struct Sass_Context* ctx); int sass_context_get_error_status (struct Sass_Context* ctx); const char* sass_context_get_error_json (struct Sass_Context* ctx); const char* sass_context_get_error_text (struct Sass_Context* ctx); const char* sass_context_get_error_message (struct Sass_Context* ctx); const char* sass_context_get_error_file (struct Sass_Context* ctx); const char* sass_context_get_error_src (struct Sass_Context* ctx); size_t sass_context_get_error_line (struct Sass_Context* ctx); size_t sass_context_get_error_column (struct Sass_Context* ctx); const char* sass_context_get_source_map_string (struct Sass_Context* ctx); char** sass_context_get_included_files (struct Sass_Context* ctx); // Getters for Sass_Compiler options (query import stack) size_t sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler); Sass_Import_Entry sass_compiler_get_last_import(struct Sass_Compiler* compiler); Sass_Import_Entry sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx); // Getters for Sass_Compiler options (query function stack) size_t sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler); Sass_Callee_Entry sass_compiler_get_last_callee(struct Sass_Compiler* compiler); Sass_Callee_Entry sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx); // Take ownership of memory (value on context is set to 0) char* sass_context_take_error_json (struct Sass_Context* ctx); char* sass_context_take_error_text (struct Sass_Context* ctx); char* sass_context_take_error_message (struct Sass_Context* ctx); char* sass_context_take_error_file (struct Sass_Context* ctx); char* sass_context_take_error_src (struct Sass_Context* ctx); char* sass_context_take_output_string (struct Sass_Context* ctx); char* sass_context_take_source_map_string (struct Sass_Context* ctx); ``` ### Sass Options API ```C // Getters for Context_Option values int sass_option_get_precision (struct Sass_Options* options); enum Sass_Output_Style sass_option_get_output_style (struct Sass_Options* options); bool sass_option_get_source_comments (struct Sass_Options* options); bool sass_option_get_source_map_embed (struct Sass_Options* options); bool sass_option_get_source_map_contents (struct Sass_Options* options); bool sass_option_get_source_map_file_urls (struct Sass_Options* options); bool sass_option_get_omit_source_map_url (struct Sass_Options* options); bool sass_option_get_is_indented_syntax_src (struct Sass_Options* options); const char* sass_option_get_indent (struct Sass_Options* options); const char* sass_option_get_linefeed (struct Sass_Options* options); const char* sass_option_get_input_path (struct Sass_Options* options); const char* sass_option_get_output_path (struct Sass_Options* options); const char* sass_option_get_source_map_file (struct Sass_Options* options); const char* sass_option_get_source_map_root (struct Sass_Options* options); Sass_C_Function_List sass_option_get_c_functions (struct Sass_Options* options); Sass_C_Import_Callback sass_option_get_importer (struct Sass_Options* options); // Getters for Context_Option include path array size_t sass_option_get_include_path_size(struct Sass_Options* options); const char* sass_option_get_include_path(struct Sass_Options* options, size_t i); // Plugin paths to load dynamic libraries work the same size_t sass_option_get_plugin_path_size(struct Sass_Options* options); const char* sass_option_get_plugin_path(struct Sass_Options* options, size_t i); // Setters for Context_Option values void sass_option_set_precision (struct Sass_Options* options, int precision); void sass_option_set_output_style (struct Sass_Options* options, enum Sass_Output_Style output_style); void sass_option_set_source_comments (struct Sass_Options* options, bool source_comments); void sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed); void sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents); void sass_option_set_source_map_file_urls (struct Sass_Options* options, bool source_map_file_urls); void sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url); void sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src); void sass_option_set_indent (struct Sass_Options* options, const char* indent); void sass_option_set_linefeed (struct Sass_Options* options, const char* linefeed); void sass_option_set_input_path (struct Sass_Options* options, const char* input_path); void sass_option_set_output_path (struct Sass_Options* options, const char* output_path); void sass_option_set_plugin_path (struct Sass_Options* options, const char* plugin_path); void sass_option_set_include_path (struct Sass_Options* options, const char* include_path); void sass_option_set_source_map_file (struct Sass_Options* options, const char* source_map_file); void sass_option_set_source_map_root (struct Sass_Options* options, const char* source_map_root); void sass_option_set_c_functions (struct Sass_Options* options, Sass_C_Function_List c_functions); void sass_option_set_importer (struct Sass_Options* options, Sass_C_Import_Callback importer); // Push function for paths (no manipulation support for now) void sass_option_push_plugin_path (struct Sass_Options* options, const char* path); void sass_option_push_include_path (struct Sass_Options* options, const char* path); // Resolve a file via the given include paths in the sass option struct // find_file looks for the exact file name while find_include does a regular sass include char* sass_find_file (const char* path, struct Sass_Options* opt); char* sass_find_include (const char* path, struct Sass_Options* opt); // Resolve a file relative to last import or include paths in the sass option struct // find_file looks for the exact file name while find_include does a regular sass include char* sass_compiler_find_file (const char* path, struct Sass_Compiler* compiler); char* sass_compiler_find_include (const char* path, struct Sass_Compiler* compiler); ``` ### More links - [Sass Context Example](api-context-example.md) - [Sass Context Internal](api-context-internal.md) golibsass-1.0.0/libsass_src/docs/api-doc.md000066400000000000000000000177561405214413600206130ustar00rootroot00000000000000## Introduction LibSass wouldn't be much good without a way to interface with it. These interface documentations describe the various functions and data structures available to implementers. They are split up over three major components, which have all their own source files (plus some common functionality). - [Sass Context](api-context.md) - Trigger and handle the main Sass compilation - [Sass Value](api-value.md) - Exchange values and its format with LibSass - [Sass Function](api-function.md) - Get invoked by LibSass for function statments - [Sass Importer](api-importer.md) - Get invoked by LibSass for @import statments ### Basic usage First you will need to include the header file! This will automatically load all other headers too! ```C #include "sass/context.h" ``` ## Basic C Example ```C #include #include "sass/context.h" int main() { puts(libsass_version()); return 0; } ``` ```bash gcc -Wall version.c -lsass -o version && ./version ``` ## More C Examples - [Sample code for Sass Context](api-context-example.md) - [Sample code for Sass Value](api-value-example.md) - [Sample code for Sass Function](api-function-example.md) - [Sample code for Sass Importer](api-importer-example.md) ## Compiling your code The most important is your sass file (or string of sass code). With this, you will want to start a LibSass compiler. Here is some pseudocode describing the process. The compiler has two different modes: direct input as a string with `Sass_Data_Context` or LibSass will do file reading for you by using `Sass_File_Context`. See the code for a list of options available [Sass_Options](https://github.com/sass/libsass/blob/36feef0/include/sass/interface.h#L18) The general rule is if the API takes `const char*` it will make a copy, but where the API is `char*` it will take over memory ownership, so make sure to pass in memory that is allocated via `sass_copy_c_string` or `sass_alloc_memory`. **Building a file compiler** context = sass_make_file_context("file.scss") options = sass_file_context_get_options(context) sass_option_set_precision(options, 1) sass_option_set_source_comments(options, true) sass_file_context_set_options(context, options) compiler = sass_make_file_compiler(sass_context) sass_compiler_parse(compiler) sass_compiler_execute(compiler) output = sass_context_get_output_string(context) // Retrieve errors during compilation error_status = sass_context_get_error_status(context) json_error = sass_context_get_error_json(context) // Release memory dedicated to the C compiler sass_delete_compiler(compiler) **Building a data compiler** // LibSass takes over memory owenership, make sure to allocate // a buffer via `sass_alloc_memory` or `sass_copy_c_string`. buffer = sass_copy_c_string("div { a { color: blue; } }") context = sass_make_data_context(buffer) options = sass_data_context_get_options(context) sass_option_set_precision(options, 1) sass_option_set_source_comments(options, true) sass_data_context_set_options(context, options) compiler = sass_make_data_compiler(context) sass_compiler_parse(compiler) sass_compiler_execute(compiler) output = sass_context_get_output_string(context) // div a { color: blue; } // Retrieve errors during compilation error_status = sass_context_get_error_status(context) json_error = sass_context_get_error_json(context) // Release memory dedicated to the C compiler sass_delete_compiler(compiler) ## Sass Context Internals Everything is stored in structs: ```C struct Sass_Options; struct Sass_Context : Sass_Options; struct Sass_File_context : Sass_Context; struct Sass_Data_context : Sass_Context; ``` This mirrors very well how `libsass` uses these structures. - `Sass_Options` holds everything you feed in before the compilation. It also hosts `input_path` and `output_path` options, because they are used to generate/calculate relative links in source-maps. The `input_path` is shared with `Sass_File_Context`. - `Sass_Context` holds all the data returned by the compilation step. - `Sass_File_Context` is a specific implementation that requires no additional fields - `Sass_Data_Context` is a specific implementation that adds the `input_source` field Structs can be down-casted to access `context` or `options`! ## Memory handling and life-cycles We keep memory around for as long as the main [context](api-context.md) object is not destroyed (`sass_delete_context`). LibSass will create copies of most inputs/options beside the main sass code. You need to allocate and fill that buffer before passing it to LibSass. You may also overtake memory management from libsass for certain return values (i.e. `sass_context_take_output_string`). Make sure to free it via `sass_free_memory`. ```C // to allocate buffer to be filled void* sass_alloc_memory(size_t size); // to allocate a buffer from existing string char* sass_copy_c_string(const char* str); // to free overtaken memory when done void sass_free_memory(void* ptr); ``` ## Miscellaneous API functions ```C // Some convenient string helper function char* sass_string_unquote (const char* str); char* sass_string_quote (const char* str, const char quote_mark); // Get compiled libsass version const char* libsass_version(void); // Implemented sass language version // Hardcoded version 3.4 for time being const char* libsass_language_version(void); ``` ## Common Pitfalls **input_path** The `input_path` is part of `Sass_Options`, but it also is the main option for `Sass_File_Context`. It is also used to generate relative file links in source- maps. Therefore it is pretty useful to pass this information if you have a `Sass_Data_Context` and know the original path. **output_path** Be aware that `libsass` does not write the output file itself. This option merely exists to give `libsass` the proper information to generate links in source-maps. The file has to be written to the disk by the binding/implementation. If the `output_path` is omitted, `libsass` tries to extrapolate one from the `input_path` by replacing (or adding) the file ending with `.css`. ## Error Codes The `error_code` is integer value which indicates the type of error that occurred inside the LibSass process. Following is the list of error codes along with the short description: * 1: normal errors like parsing or `eval` errors * 2: bad allocation error (memory error) * 3: "untranslated" C++ exception (`throw std::exception`) * 4: legacy string exceptions ( `throw const char*` or `sass::string` ) * 5: Some other unknown exception Although for the API consumer, error codes do not offer much value except indicating whether *any* error occurred during the compilation, it helps debugging the LibSass internal code paths. ## Real-World Implementations The proof is in the pudding, so we have highlighted a few implementations that should be on par with the latest LibSass interface version. Some of them may not have all features implemented! 1. [Perl Example](https://github.com/sass/perl-libsass/blob/master/Sass.xs) 2. [Go Example](https://godoc.org/github.com/wellington/go-libsass#example-Compiler--Stdin) 3. [Node Example](https://github.com/sass/node-sass/blob/master/src/binding.cpp) ## ABI forward compatibility We use a functional API to make dynamic linking more robust and future compatible. The API is not yet 100% stable, so we do not yet guarantee [ABI](https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) forward compatibility. ## Plugins (experimental) LibSass can load plugins from directories. Just define `plugin_path` on context options to load all plugins from the directories. To implement plugins, please consult the following example implementations. - https://github.com/mgreter/libsass-glob - https://github.com/mgreter/libsass-math - https://github.com/mgreter/libsass-digest ## Internal Structs - [Sass Context Internals](api-context-internal.md) - [Sass Value Internals](api-value-internal.md) - [Sass Function Internals](api-function-internal.md) - [Sass Importer Internals](api-importer-internal.md) golibsass-1.0.0/libsass_src/docs/api-function-example.md000066400000000000000000000041451405214413600233100ustar00rootroot00000000000000## Example main.c ```C #include #include #include "sass/context.h" union Sass_Value* call_fn_foo(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Compiler* comp) { // get context/option struct associated with this compiler struct Sass_Context* ctx = sass_compiler_get_context(comp); struct Sass_Options* opts = sass_compiler_get_options(comp); // get information about previous importer entry from the stack Sass_Import_Entry import = sass_compiler_get_last_import(comp); const char* prev_abs_path = sass_import_get_abs_path(import); const char* prev_imp_path = sass_import_get_imp_path(import); // get the cookie from function descriptor void* cookie = sass_function_get_cookie(cb); // we actually abuse the void* to store an "int" return sass_make_number((intptr_t)cookie, "px"); } int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate a custom function caller Sass_Function_Entry fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions Sass_Function_List fn_list = sass_make_function_list(1); sass_function_set_list_entry(fn_list, 0, fn_foo); sass_option_set_c_functions(ctx_opt, fn_list); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` ### Compile main.c ```bash gcc -c main.c -o main.o gcc -o sample main.o -lsass echo "foo { margin: foo(); }" > foo.scss ./sample foo.scss => "foo { margin: 42px }" ``` golibsass-1.0.0/libsass_src/docs/api-function-internal.md000066400000000000000000000002441405214413600234650ustar00rootroot00000000000000```C // Struct to hold custom function callback struct Sass_Function { const char* signature; Sass_Function_Fn function; void* cookie; }; ``` golibsass-1.0.0/libsass_src/docs/api-function.md000066400000000000000000000066171405214413600216650ustar00rootroot00000000000000Sass functions are used to define new custom functions callable by Sass code. They are also used to overload debug or error statements. You can also define a fallback function, which is called for every unknown function found in the Sass code. Functions get passed zero or more `Sass_Values` (a `Sass_List` value) and they must also return a `Sass_Value`. Return a `Sass_Error` if you want to signal an error. ## Special signatures - `*` - Fallback implementation - `@warn` - Overload warn statements - `@error` - Overload error statements - `@debug` - Overload debug statements Note: The fallback implementation will be given the name of the called function as the first argument, before all the original function arguments. These features are pretty new and should be considered experimental. ### Basic Usage ```C #include "sass/functions.h" ``` ## Sass Function API ```C // Forward declaration struct Sass_Compiler; struct Sass_Function; // Typedef helpers for custom functions lists typedef struct Sass_Function (*Sass_Function_Entry); typedef struct Sass_Function* (*Sass_Function_List); // Typedef defining function signature and return type typedef union Sass_Value* (*Sass_Function_Fn) (const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Compiler* compiler); // Creators for sass function list and function descriptors Sass_Function_List sass_make_function_list (size_t length); Sass_Function_Entry sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie); // In case you need to free them yourself void sass_delete_function (Sass_Function_Entry entry); void sass_delete_function_list (Sass_Function_List list); // Setters and getters for callbacks on function lists Sass_Function_Entry sass_function_get_list_entry(Sass_Function_List list, size_t pos); void sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb); // Setters to insert an entry into the import list (you may also use [] access directly) // Since we are dealing with pointers they should have a guaranteed and fixed size void sass_import_set_list_entry (Sass_Import_List list, size_t idx, Sass_Import_Entry entry); Sass_Import_Entry sass_import_get_list_entry (Sass_Import_List list, size_t idx); // Getters for custom function descriptors const char* sass_function_get_signature (Sass_Function_Entry cb); Sass_Function_Fn sass_function_get_function (Sass_Function_Entry cb); void* sass_function_get_cookie (Sass_Function_Entry cb); // Getters for callee entry const char* sass_callee_get_name (Sass_Callee_Entry); const char* sass_callee_get_path (Sass_Callee_Entry); size_t sass_callee_get_line (Sass_Callee_Entry); size_t sass_callee_get_column (Sass_Callee_Entry); enum Sass_Callee_Type sass_callee_get_type (Sass_Callee_Entry); Sass_Env_Frame sass_callee_get_env (Sass_Callee_Entry); // Getters and Setters for environments (lexical, local and global) union Sass_Value* sass_env_get_lexical (Sass_Env_Frame, const char*); void sass_env_set_lexical (Sass_Env_Frame, const char*, union Sass_Value*); union Sass_Value* sass_env_get_local (Sass_Env_Frame, const char*); void sass_env_set_local (Sass_Env_Frame, const char*, union Sass_Value*); union Sass_Value* sass_env_get_global (Sass_Env_Frame, const char*); void sass_env_set_global (Sass_Env_Frame, const char*, union Sass_Value*); ``` ### More links - [Sass Function Example](api-function-example.md) - [Sass Function Internal](api-function-internal.md) golibsass-1.0.0/libsass_src/docs/api-importer-example.md000066400000000000000000000071051405214413600233230ustar00rootroot00000000000000## Example importer.c ```C #include #include #include "sass/context.h" Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // get the cookie from importer descriptor void* cookie = sass_importer_get_cookie(cb); Sass_Import_List list = sass_make_import_list(2); char* local = sass_copy_c_string("local { color: green; }"); char* remote = sass_copy_c_string("remote { color: red; }"); list[0] = sass_make_import_entry("/tmp/styles.scss", local, 0); list[1] = sass_make_import_entry("http://www.example.com", remote, 0); return list; } int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate custom importer Sass_Importer_Entry c_imp = sass_make_importer(sass_importer, 0, 0); // create list for all custom importers Sass_Importer_List imp_list = sass_make_importer_list(1); // put only the importer on to the list sass_importer_set_list_entry(imp_list, 0, c_imp); // register list on to the context options sass_option_set_c_importers(ctx_opt, imp_list); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` Compile importer.c ```bash gcc -c importer.c -o importer.o gcc -o importer importer.o -lsass echo "@import 'foobar';" > importer.scss ./importer importer.scss ``` ## Importer Behavior Examples ```C Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // let LibSass handle the import request return NULL; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // let LibSass handle the request // swallows »@import "http://…"« pass-through // (arguably a bug) Sass_Import_List list = sass_make_import_list(1); list[0] = sass_make_import_entry(path, 0, 0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // return an error to halt execution Sass_Import_List list = sass_make_import_list(1); const char* message = "some error message"; list[0] = sass_make_import_entry(path, 0, 0); sass_import_set_error(list[0], sass_copy_c_string(message), 0, 0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // let LibSass load the file identifed by the importer Sass_Import_List list = sass_make_import_list(1); list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // completely hide the import // (arguably a bug) Sass_Import_List list = sass_make_import_list(0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // completely hide the import // (arguably a bug) Sass_Import_List list = sass_make_import_list(1); list[0] = sass_make_import_entry(0, 0, 0); return list; } ``` golibsass-1.0.0/libsass_src/docs/api-importer-internal.md000066400000000000000000000006451405214413600235060ustar00rootroot00000000000000```C // External import entry struct Sass_Import { char* imp_path; // path as found in the import statement char *abs_path; // path after importer has resolved it char* source; char* srcmap; // error handling char* error; size_t line; size_t column; }; // Struct to hold importer callback struct Sass_Importer { Sass_Importer_Fn importer; double priority; void* cookie; }; ``` golibsass-1.0.0/libsass_src/docs/api-importer.md000066400000000000000000000104071405214413600216710ustar00rootroot00000000000000By using custom importers, Sass stylesheets can be implemented in any possible way, such as by being loaded via a remote server. Please note: this feature is experimental and is implemented differently than importers in Ruby Sass. Imports must be relative to the parent import context and therefore we need to pass this information to the importer callback. This is currently done by passing the complete import string/path of the previous import context. ## Return Imports You actually have to return a list of imports, since some importers may want to import multiple files from one import statement (ie. a glob/star importer). The memory you pass with source and srcmap is taken over by LibSass and freed automatically when the import is done. You are also allowed to return `0` instead of a list, which will tell LibSass to handle the import by itself (as if no custom importer was in use). ```C Sass_Import_Entry* rv = sass_make_import_list(1); rv[0] = sass_make_import(rel, abs, source, srcmap); ``` Every import will then be included in LibSass. You are allowed to only return a file path without any loaded source. This way you can ie. implement rewrite rules for import paths and leave the loading part for LibSass. Please note that LibSass doesn't use the srcmap parameter yet. It has been added to not deprecate the C-API once support has been implemented. It will be used to re-map the actual sourcemap with the provided ones. ### Basic Usage ```C #include "sass/functions.h" ``` ## Sass Importer API ```C // Forward declaration struct Sass_Import; // Forward declaration struct Sass_C_Import_Descriptor; // Typedef defining the custom importer callback typedef struct Sass_C_Import_Descriptor (*Sass_C_Import_Callback); // Typedef defining the importer c function prototype typedef Sass_Import_Entry* (*Sass_C_Import_Fn) (const char* url, const char* prev, void* cookie); // Creators for custom importer callback (with some additional pointer) // The pointer is mostly used to store the callback into the actual function Sass_C_Import_Callback sass_make_importer (Sass_C_Import_Fn, void* cookie); // Getters for import function descriptors Sass_C_Import_Fn sass_import_get_function (Sass_C_Import_Callback fn); void* sass_import_get_cookie (Sass_C_Import_Callback fn); // Deallocator for associated memory void sass_delete_importer (Sass_C_Import_Callback fn); // Creator for sass custom importer return argument list Sass_Import_Entry* sass_make_import_list (size_t length); // Creator for a single import entry returned by the custom importer inside the list Sass_Import_Entry sass_make_import_entry (const char* path, char* source, char* srcmap); Sass_Import_Entry sass_make_import (const char* rel, const char* abs, char* source, char* srcmap); // set error message to abort import and to print out a message (path from existing object is used in output) Sass_Import_Entry sass_import_set_error(Sass_Import_Entry import, const char* message, size_t line, size_t col); // Setters to insert an entry into the import list (you may also use [] access directly) // Since we are dealing with pointers they should have a guaranteed and fixed size void sass_import_set_list_entry (Sass_Import_Entry* list, size_t idx, Sass_Import_Entry entry); Sass_Import_Entry sass_import_get_list_entry (Sass_Import_Entry* list, size_t idx); // Getters for import entry const char* sass_import_get_imp_path (Sass_Import_Entry); const char* sass_import_get_abs_path (Sass_Import_Entry); const char* sass_import_get_source (Sass_Import_Entry); const char* sass_import_get_srcmap (Sass_Import_Entry); // Explicit functions to take ownership of these items // The property on our struct will be reset to NULL char* sass_import_take_source (Sass_Import_Entry); char* sass_import_take_srcmap (Sass_Import_Entry); // Getters for import error entries size_t sass_import_get_error_line (Sass_Import_Entry); size_t sass_import_get_error_column (Sass_Import_Entry); const char* sass_import_get_error_message (Sass_Import_Entry); // Deallocator for associated memory (incl. entries) void sass_delete_import_list (Sass_Import_Entry*); // Just in case we have some stray import structs void sass_delete_import (Sass_Import_Entry); ``` ### More links - [Sass Importer Example](api-importer-example.md) - [Sass Importer Internal](api-importer-internal.md) golibsass-1.0.0/libsass_src/docs/api-value-example.md000066400000000000000000000025761405214413600226050ustar00rootroot00000000000000## Example operation.c ```C #include #include #include "sass/values.h" int main( int argc, const char* argv[] ) { // create two new sass values to be added union Sass_Value* string = sass_make_string("String"); union Sass_Value* number = sass_make_number(42, "nits"); // invoke the add operation which returns a new sass value union Sass_Value* total = sass_value_op(ADD, string, number); // no further use for the two operands sass_delete_value(string); sass_delete_value(number); // this works since libsass will always return a // string for add operations with a string as the // left hand side. But you should never rely on it! puts(sass_string_get_value(total)); // invoke stringification (uncompressed with precision of 5) union Sass_Value* result = sass_value_stringify(total, false, 5); // no further use for the sum sass_delete_value(total); // print the result - you may want to make // sure result is indeed a string, although // stringify guarantees to return a string // if (sass_value_is_string(result)) {} // really depends on your level of paranoia puts(sass_string_get_value(result)); // finally free result sass_delete_value(result); // exit status return 0; } ``` ## Compile operation.c ```bash gcc -c operation.c -o operation.o gcc -o operation operation.o -lsass ./operation # => String42nits ``` golibsass-1.0.0/libsass_src/docs/api-value-internal.md000066400000000000000000000023711405214413600227570ustar00rootroot00000000000000```C struct Sass_Unknown { enum Sass_Tag tag; }; struct Sass_Boolean { enum Sass_Tag tag; bool value; }; struct Sass_Number { enum Sass_Tag tag; double value; char* unit; }; struct Sass_Color { enum Sass_Tag tag; double r; double g; double b; double a; }; struct Sass_String { enum Sass_Tag tag; char* value; }; struct Sass_List { enum Sass_Tag tag; enum Sass_Separator separator; size_t length; // null terminated "array" union Sass_Value** values; }; struct Sass_Map { enum Sass_Tag tag; size_t length; struct Sass_MapPair* pairs; }; struct Sass_Null { enum Sass_Tag tag; }; struct Sass_Error { enum Sass_Tag tag; char* message; }; struct Sass_Warning { enum Sass_Tag tag; char* message; }; union Sass_Value { struct Sass_Unknown unknown; struct Sass_Boolean boolean; struct Sass_Number number; struct Sass_Color color; struct Sass_String string; struct Sass_List list; struct Sass_Map map; struct Sass_Null null; struct Sass_Error error; struct Sass_Warning warning; }; struct Sass_MapPair { union Sass_Value* key; union Sass_Value* value; }; ``` golibsass-1.0.0/libsass_src/docs/api-value.md000066400000000000000000000137701405214413600211520ustar00rootroot00000000000000`Sass_Values` are used to pass values and their types between the implementer and LibSass. Sass knows various different value types (including nested arrays and hash-maps). If you implement a binding to another programming language, you have to find a way to [marshal][1] (convert) `Sass_Values` between the target language and C. `Sass_Values` are currently only used by custom functions, but it should also be possible to use them without a compiler context. [1]: https://en.wikipedia.org/wiki/Marshalling_%28computer_science%29 ### Basic Usage ```C #include "sass/values.h" ``` ```C // Type for Sass values enum Sass_Tag { SASS_BOOLEAN, SASS_NUMBER, SASS_COLOR, SASS_STRING, SASS_LIST, SASS_MAP, SASS_NULL, SASS_ERROR, SASS_WARNING }; // Tags for denoting Sass list separators enum Sass_Separator { SASS_COMMA, SASS_SPACE, // only used internally to represent a hash map before evaluation // otherwise we would be too early to check for duplicate keys SASS_HASH }; // Value Operators enum Sass_OP { AND, OR, // logical connectives EQ, NEQ, GT, GTE, LT, LTE, // arithmetic relations ADD, SUB, MUL, DIV, MOD, // arithmetic functions NUM_OPS // so we know how big to make the op table }; ``` ### Sass Value API ```C // Forward declaration union Sass_Value; // Creator functions for all value types union Sass_Value* sass_make_null (void); union Sass_Value* sass_make_boolean (bool val); union Sass_Value* sass_make_string (const char* val); union Sass_Value* sass_make_qstring (const char* val); union Sass_Value* sass_make_number (double val, const char* unit); union Sass_Value* sass_make_color (double r, double g, double b, double a); union Sass_Value* sass_make_list (size_t len, enum Sass_Separator sep, bool is_bracketed); union Sass_Value* sass_make_map (size_t len); union Sass_Value* sass_make_error (const char* msg); union Sass_Value* sass_make_warning (const char* msg); // Generic destructor function for all types // Will release memory of all associated Sass_Values // Means we will delete recursively for lists and maps void sass_delete_value (union Sass_Value* val); // Make a deep cloned copy of the given sass value union Sass_Value* sass_clone_value (const union Sass_Value* val); // Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING) union Sass_Value* sass_value_stringify (const union Sass_Value* a, bool compressed, int precision); // Execute an operation for two Sass_Values and return the result as a Sass_Value too union Sass_Value* sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b); // Return the sass tag for a generic sass value // Check is needed before accessing specific values! enum Sass_Tag sass_value_get_tag (const union Sass_Value* v); // Check value to be of a specific type // Can also be used before accessing properties! bool sass_value_is_null (const union Sass_Value* v); bool sass_value_is_number (const union Sass_Value* v); bool sass_value_is_string (const union Sass_Value* v); bool sass_value_is_boolean (const union Sass_Value* v); bool sass_value_is_color (const union Sass_Value* v); bool sass_value_is_list (const union Sass_Value* v); bool sass_value_is_map (const union Sass_Value* v); bool sass_value_is_error (const union Sass_Value* v); bool sass_value_is_warning (const union Sass_Value* v); // Getters and setters for Sass_Number double sass_number_get_value (const union Sass_Value* v); void sass_number_set_value (union Sass_Value* v, double value); const char* sass_number_get_unit (const union Sass_Value* v); void sass_number_set_unit (union Sass_Value* v, char* unit); // Getters and setters for Sass_String const char* sass_string_get_value (const union Sass_Value* v); void sass_string_set_value (union Sass_Value* v, char* value); bool sass_string_is_quoted(const union Sass_Value* v); void sass_string_set_quoted(union Sass_Value* v, bool quoted); // Getters and setters for Sass_Boolean bool sass_boolean_get_value (const union Sass_Value* v); void sass_boolean_set_value (union Sass_Value* v, bool value); // Getters and setters for Sass_Color double sass_color_get_r (const union Sass_Value* v); void sass_color_set_r (union Sass_Value* v, double r); double sass_color_get_g (const union Sass_Value* v); void sass_color_set_g (union Sass_Value* v, double g); double sass_color_get_b (const union Sass_Value* v); void sass_color_set_b (union Sass_Value* v, double b); double sass_color_get_a (const union Sass_Value* v); void sass_color_set_a (union Sass_Value* v, double a); // Getter for the number of items in list size_t sass_list_get_length (const union Sass_Value* v); // Getters and setters for Sass_List enum Sass_Separator sass_list_get_separator (const union Sass_Value* v); void sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value); bool sass_list_get_is_bracketed (const union Sass_Value* v); void sass_list_set_is_bracketed (union Sass_Value* v, bool value); // Getters and setters for Sass_List values union Sass_Value* sass_list_get_value (const union Sass_Value* v, size_t i); void sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value); // Getter for the number of items in map size_t sass_map_get_length (const union Sass_Value* v); // Getters and setters for Sass_Map keys and values union Sass_Value* sass_map_get_key (const union Sass_Value* v, size_t i); void sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*); union Sass_Value* sass_map_get_value (const union Sass_Value* v, size_t i); void sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*); // Getters and setters for Sass_Error char* sass_error_get_message (const union Sass_Value* v); void sass_error_set_message (union Sass_Value* v, char* msg); // Getters and setters for Sass_Warning char* sass_warning_get_message (const union Sass_Value* v); void sass_warning_set_message (union Sass_Value* v, char* msg); ``` ### More links - [Sass Value Example](api-value-example.md) - [Sass Value Internal](api-value-internal.md) golibsass-1.0.0/libsass_src/docs/build-on-darwin.md000066400000000000000000000014301405214413600222500ustar00rootroot00000000000000To install LibSass, make sure the OS X build tools are installed: xcode-select --install ## Homebrew To install homebrew, see [http://brew.sh](http://brew.sh) ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" You can install the latest version of LibSass quite easily with brew. brew install --HEAD libsass To update this, do: brew reinstall --HEAD libsass Brew will build static and shared libraries, and a `libsass.pc` file in `/usr/local/lib/pkgconfig`. To use `libsass.pc`, make sure this path is in your `PKG_CONFIG_PATH` export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ## Manually See the linux instructions [Building-with-autotools](build-with-autotools.md) or [Building-with-makefiles](build-with-makefiles.md) golibsass-1.0.0/libsass_src/docs/build-on-gentoo.md000066400000000000000000000024011405214413600222560ustar00rootroot00000000000000Here are two ebuilds to compile LibSass and sassc on gentoo linux. If you do not know how to use these ebuilds, you should probably read the gentoo wiki page about [portage overlays](http://wiki.gentoo.org/wiki/Overlay). ## www-misc/libsass/libsass-9999.ebuild ```ebuild EAPI=4 inherit eutils git-2 autotools DESCRIPTION="A C/C++ implementation of a Sass compiler." HOMEPAGE="http://libsass.org/" EGIT_PROJECT='libsass' EGIT_REPO_URI="https://github.com/sass/libsass.git" LICENSE="MIT" SLOT="0" KEYWORDS="" IUSE="" DEPEND="" RDEPEND="${DEPEND}" DEPEND="${DEPEND}" pkg_pretend() { # older gcc is not supported local major=$(gcc-major-version) local minor=$(gcc-minor-version) [[ "${MERGE_TYPE}" != "binary" && ( $major > 4 || ( $major == 4 && $minor < 5 ) ) ]] && \ die "Sorry, but gcc earlier than 4.5 will not work for LibSass." } src_prepare() { eautoreconf } ``` ## www-misc/sassc/sassc-9999.ebuild ```ebuild EAPI=4 inherit eutils git-2 autotools DESCRIPTION="Command Line Tool for LibSass." HOMEPAGE="http://libsass.org/" EGIT_PROJECT='sassc' EGIT_REPO_URI="https://github.com/sass/sassc.git" LICENSE="MIT" SLOT="0" KEYWORDS="" IUSE="" DEPEND="www-misc/libsass" RDEPEND="${DEPEND}" DEPEND="${DEPEND}" src_prepare() { eautoreconf } ``` golibsass-1.0.0/libsass_src/docs/build-on-windows.md000066400000000000000000000113021405214413600224550ustar00rootroot00000000000000We support builds via MingGW and via Visual Studio Community 2013. Both should be considered experimental (MinGW was better tested)! ## Building via MingGW (makefiles) First grab the latest [MinGW for windows][1] installer. Once it is installed, you can click on continue or open the Installation Manager via `bin\mingw-get.exe`. You need to have the following components installed: ![Visualization of components installed in the interface](https://cloud.githubusercontent.com/assets/282293/5525466/947bf396-89e6-11e4-841d-4aa916f14de1.png) Next we need to install [git for windows][2]. You probably want to check the option to add it to the global path, but you do not need to install the unix tools. If you want to run the spec test-suite you also need [ruby][3] and a few gems available. Grab the [latest installer][3] and make sure to add it the global path. Then install the missing gems: ```bash gem install minitest ``` ### Mount the mingw root directory As mentioned in the [MinGW Getting Started](http://www.mingw.org/wiki/Getting_Started#toc5) guide, you should edit `C:\MinGW\msys\1.0\etc\fstab` to contain the following line: ``` C:\MinGW /mingw ``` ### Starting a "MingGW" console Create a batch file with this content: ```bat @echo off set PATH=C:\MinGW\bin;%PATH% REM only needed if not already available set PATH=%PROGRAMFILES%\git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` Execute it and make sure these commands can be called: `git`, `mingw32-make`, `rm` and `gcc`! Once this is all set, you should be ready to compile `libsass`! ### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Decide for static or shared library `libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable: ```bat set BUILD="shared" ``` ### Compile the library ```bash mingw32-make -C libsass ``` ### Results can be found in ```bash $ ls libsass/lib libsass.a libsass.dll libsass.so ``` ### Run the spec test-suite ```bash mingw32-make -C libsass test_build ``` ## Building via MingGW 64bit (makefiles) Building libass to dll on window 64bit. + downloads [MinGW64 for windows7 64bit](http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v3-rev0.7z/download) , and unzip to "C:\mingw64". + Create a batch file with this content: ```bat @echo off set PATH=C:\mingw64\bin;%PATH% set CC=gcc REM only needed if not already available set PATH=%PROGRAMFILES%\Git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` + By default , mingw64 dll will depends on "​m​i​n​g​w​m​1​0​.​d​l​l​、​ ​l​i​b​g​c​c​_​s​_​d​w​2​-​1​.​d​l​l​" , we can modify Makefile to fix this:(add "-static") ``` bash lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(MKDIR) lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -static -Wl,--subsystem,windows,--out-implib,lib/libsass.a ``` + Compile the library ```bash mingw32-make -C libsass ``` By the way , if you are using java jna , [JNAerator](http://jnaerator.googlecode.com/) is a good tool. ## Building via Visual Studio Community 2013 Open a Visual Studio 2013 command prompt: - `VS2013 x86 Native Tools Command Prompt` Note: When I installed the community edition, I only got the 2012 command prompts. I copied them from the Startmenu to the Desktop and adjusted the paths from `Visual Studio 11.0` to `Visual Studio 12.0`. Since `libsass` uses some `C++11` features, you need at least a MSVC 2013 compiler (v120). ### Get the source ```bash # using git is preferred git clone https://github.com/sass/libsass.git git clone https://github.com/sass/sassc.git libsass/sassc # only needed if you want to run the testsuite git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Compile sassc Sometimes `msbuild` seems not available from the command prompt. Just search for it and add it to the global path. It seems to be included in the .net folders too. ```bat cd libsass REM set PATH=%PATH%;%PROGRAMFILES%\MSBuild\12.0\Bin msbuild /m:4 /p:Configuration=Release win\libsass.sln REM running the spec test-suite manually (needs ruby and minitest gem) ruby sass-spec\sass-spec.rb -c win\bin\sassc.exe -s --impl libsass sass-spec/spec cd .. ``` [1]: http://sourceforge.net/projects/mingw/files/latest/download?source=files [2]: https://msysgit.github.io/ [3]: http://rubyinstaller.org/ golibsass-1.0.0/libsass_src/docs/build-shared-library.md000066400000000000000000000026121405214413600232650ustar00rootroot00000000000000This page is mostly intended for people that want to build a system library that gets distributed via RPMs or other means. This is currently in a experimental phase, as we currently do not really guarantee any ABI forward compatibility. The C API was rewritten to make this possible in the future, but we want to wait some more time till we can call this final and stable. Building via autotools -- You want to build a system library only via autotools, since it will create the proper `libtool` files to make it loadable on multiple systems. We hope this works correctly, but nobody of the `libsass` core team has much knowledge in this area. Therefore we are open for comments or improvements by people that have more experience in that matter (like package maintainers from various linux distributions). ```bash apt-get install autoconf libtool git clone https://github.com/sass/libsass.git cd libsass autoreconf --force --install ./configure \ --disable-tests \ --disable-static \ --enable-shared \ --prefix=/usr make -j5 install cd .. ``` This should install these files ```bash # $ ls -la /usr/lib/libsass.* /usr/lib/libsass.la /usr/lib/libsass.so -> libsass.so.0.0.9 /usr/lib/libsass.so.0 -> libsass.so.0.0.9 /usr/lib/libsass.so.0.0.9 # $ ls -la /usr/include/sass* /usr/include/sass.h /usr/include/sass2scss.h /usr/include/sass/context.h /usr/include/sass/functions.h /usr/include/sass/values.h ``` golibsass-1.0.0/libsass_src/docs/build-with-autotools.md000066400000000000000000000037571405214413600233720ustar00rootroot00000000000000### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Prerequisites In order to run autotools you need a few tools installed on your system. ```bash yum install automake libtool # RedHat Linux emerge -a automake libtool # Gentoo Linux pkgin install automake libtool # SmartOS ``` ### Create configure script ```bash cd libsass autoreconf --force --install cd .. ``` ### Create custom makefiles ```bash cd libsass ./configure \ --disable-tests \ --disable-shared \ --prefix=/usr cd .. ``` ### Build the library ```bash make -C libsass -j5 ``` ### Install the library The library will be installed to the location given as `prefix` to `configure`. This is standard behavior for autotools and not `libsass` specific. ```bash make -C libsass -j5 install ``` ### Configure options The `configure` script is created by autotools. To get an overview of available options you can call `./configure --help`. When you execute this script, it will create specific makefiles, which you then use via the regular make command. There are some `libsass` specific options: ``` Optional Features: --enable-tests enable testing the build --enable-coverage enable coverage report for test suite --enable-shared build shared libraries [default=yes] --enable-static build static libraries [default=yes] Optional Packages: --with-sassc-dir= specify directory of sassc sources for testing (default: sassc) --with-sass-spec-dir= specify directory of sass-spec for testing (default: sass-spec) ``` ### Build sassc and run spec test-suite ```bash cd libsass autoreconf --force --install ./configure \ --enable-tests \ --enable-shared \ --prefix=/usr make -j5 test_build cd .. ``` golibsass-1.0.0/libsass_src/docs/build-with-makefiles.md000066400000000000000000000031001405214413600232570ustar00rootroot00000000000000### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Decide for static or shared library `libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable: ```bash export BUILD="shared" ``` Alternatively you can also define it directly when calling make: ```bash BUILD="shared" make ... ``` ### Compile the library ```bash make -C libsass -j5 ``` ### Results can be found in ```bash $ ls libsass/lib libsass.a libsass.so ``` ### Install onto the system We recommend to use [autotools to install](build-with-autotools.md) libsass onto the system, since that brings all the benefits of using libtools as the main install method. If you still want to install libsass via the makefile, you need to make sure that gnu `install` utility (or compatible) is installed on your system. ```bash yum install coreutils # RedHat Linux emerge -a coreutils # Gentoo Linux pkgin install coreutils # SmartOS ``` You can set the install location by setting `PREFIX` ```bash PREFIX="/opt/local" make install ``` ### Compling sassc ```bash # Let build know library location export SASS_LIBSASS_PATH="`pwd`/libsass" # Invokes the sassc makefile make -C libsass -j5 sassc ``` ### Run the spec test-suite ```bash # needs ruby available # also gem install minitest make -C libsass -j5 test_build ``` golibsass-1.0.0/libsass_src/docs/build-with-mingw.md000066400000000000000000000065501405214413600224540ustar00rootroot00000000000000## Building LibSass with MingGW (makefiles) First grab the latest [MinGW for windows][1] installer. Once it is installed, you can click on continue or open the Installation Manager via `bin\mingw-get.exe`. You need to have the following components installed: ![](https://cloud.githubusercontent.com/assets/282293/5525466/947bf396-89e6-11e4-841d-4aa916f14de1.png) Next we need to install [git for windows][2]. You probably want to check the option to add it to the global path, but you do not need to install the unix tools. If you want to run the spec test-suite you also need [ruby][3] and a few gems available. Grab the [latest installer][3] and make sure to add it the global path. Then install the missing gems: ```bash gem install minitest ``` ### Mount the mingw root directory As mentioned in the [MinGW Getting Started](http://www.mingw.org/wiki/Getting_Started#toc5) guide, you should edit `C:\MinGW\msys\1.0\etc\fstab` to contain the following line: ``` C:\MinGW /mingw ``` ### Starting a "MingGW" console Create a batch file with this content: ```bat @echo off set PATH=C:\MinGW\bin;%PATH% REM only needed if not already available set PATH=%PROGRAMFILES%\git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` Execute it and make sure these commands can be called: `git`, `mingw32-make`, `rm` and `gcc`! Once this is all set, you should be ready to compile `libsass`! ### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Decide for static or shared library `libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable: ```bat set BUILD="shared" ``` ### Compile the library ```bash mingw32-make -C libsass ``` ### Results can be found in ```bash $ ls libsass/lib libsass.a libsass.dll libsass.so ``` ### Run the spec test-suite ```bash mingw32-make -C libsass test_build ``` ## Building via MingGW 64bit (makefiles) Building libass to dll on window 64bit. Download [MinGW64 for windows7 64bit](http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v3-rev0.7z/download) and unzip to "C:\mingw64". Create a batch file with this content: ```bat @echo off set PATH=C:\mingw64\bin;%PATH% set CC=gcc REM only needed if not already available set PATH=%PROGRAMFILES%\Git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` By default, mingw64 dll will depends on "​m​i​n​g​w​m​1​0​.​d​l​l​、​ ​l​i​b​g​c​c​_​s​_​d​w​2​-​1​.​d​l​l​", we can modify Makefile to fix this:(add "-static") ``` bash lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(MKDIR) lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -static -Wl,--subsystem,windows,--out-implib,lib/libsass.a ``` Compile the library ```bash mingw32-make -C libsass ``` By the way, if you are using java jna, [JNAerator](http://jnaerator.googlecode.com/) is a good tool. [1]: http://sourceforge.net/projects/mingw/files/latest/download?source=files [2]: https://msysgit.github.io/ [3]: http://rubyinstaller.org/ golibsass-1.0.0/libsass_src/docs/build-with-visual-studio.md000066400000000000000000000056031405214413600241410ustar00rootroot00000000000000## Building LibSass with Visual Studio ### Requirements: The minimum requirement to build LibSass with Visual Studio is "Visual Studio 2013 Express for Desktop". Additionally, it is recommended to have `git` installed and available in `PATH`, so to deduce the `libsass` version information. For instance, if GitHub for Windows (https://windows.github.com/) is installed, the `PATH` will have an entry resembling: `X:\Users\\AppData\Local\GitHub\PortableGit_\cmd\` (where `X` is the drive letter of system drive). If `git` is not available, inquiring the LibSass version will result in `[NA]`. ### Build Steps: #### From Visual Studio: On opening the `win\libsass.sln` solution and build (Ctrl+Shift+B) to build `libsass.dll`. To Build LibSass as a static Library, it is recommended to set an environment variable `LIBSASS_STATIC_LIB` before launching the project: ```cmd cd path\to\libsass SET LIBSASS_STATIC_LIB=1 :: :: or in PowerShell: :: $env:LIBSASS_STATIC_LIB=1 :: win\libsass.sln ``` Visual Studio will form the filtered source tree as shown below: ![image](https://cloud.githubusercontent.com/assets/3840695/9298985/aae9e072-44bf-11e5-89eb-e7995c098085.png) `Header Files` contains the .h and .hpp files, while `Source Files` covers `.c` and `.cpp`. The other used headers/sources will appear under `External Dependencies`. If there is a LibSass code file appearing under External Dependencies, it can be changed by altering the `win\libsass.vcxproj.filters` file or dragging in Solution Explorer. #### From Command Prompt: Notice that in the following commands: * If the platform is 32-bit Windows, replace `ProgramFiles(x86)` with `ProgramFiles`. * To build with Visual Studio 2015, replace `12.0` with `14.0` in the aforementioned command. Open a command prompt: To build dynamic/shared library (`libsass.dll`): ```cmd :: debug build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln :: release build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^ /p:Configuration=Release ``` To build static library (`libsass.lib`): ```cmd :: debug build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^ /p:LIBSASS_STATIC_LIB=1 :: release build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^ /p:LIBSASS_STATIC_LIB=1 /p:Configuration=Release ``` #### From PowerShell: To build dynamic/shared library (`libsass.dll`): ```powershell # debug build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln # release build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ` /p:Configuration=Release ``` To build static library (`libsass.lib`): ```powershell # build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ` /p:LIBSASS_STATIC_LIB=1 # release build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ` /p:LIBSASS_STATIC_LIB=1 /p:Configuration=Release ``` golibsass-1.0.0/libsass_src/docs/build.md000066400000000000000000000114551405214413600203640ustar00rootroot00000000000000`libsass` is only a library and does not do much on its own. You need an implementation that you can use from the [command line][6]. Or some [bindings|Implementations][9] to use it within your favorite programming language. You should be able to get [`sassc`][6] running by following the instructions in this guide. Before starting, see [setup dev environment](setup-environment.md). Building on different Operating Systems -- We try to keep the code as OS independent and standard compliant as possible. Reading files from the file-system has some OS depending code, but will ultimately fall back to a posix compatible implementation. We do use some `C++11` features, but are so far only committed to use `unordered_map`. This means you will need a pretty recent compiler on most systems (gcc 4.5 seems to be the minimum). ### Building on Linux (and other *nix flavors) Linux is the main target for `libsass` and we support two ways to build `libsass` here. The old plain makefiles should still work on most systems (including MinGW), while the autotools build is preferred if you want to create a [system library] (experimental). - [Building with makefiles][1] - [Building with autotools][2] ### Building on Windows (experimental) Windows build support was added very recently and should be considered experimental. Credits go to @darrenkopp and @am11 for their work on getting `libsass` and `sassc` to compile with visual studio! - [Building with MinGW][3] - [Building with Visual Studio][11] ### Building on Max OS X (untested) Works the same as on linux, but you can also install LibSass via `homebrew`. - [Building on Mac OS X][10] ### Building a system library (experimental) Since `libsass` is a library, it makes sense to install it as a shared library on your system. On linux this means creating a `.so` library via autotools. This should work pretty well already, but we are not yet committed to keep the ABI 100% stable. This should be the case once we increase the version number for the library to 1.0.0 or higher. On Windows you should be able get a `dll` by creating a shared build with MinGW. There is currently no target in the MSVC project files to do this. - [Building shared system library][4] Compiling with clang instead of gcc -- To use clang you just need to set the appropriate environment variables: ```bash export CC=/usr/bin/clang export CXX=/usr/bin/clang++ ``` Running the spec test-suite -- We constantly and automatically test `libsass` against the official [spec test-suite][5]. To do this we need to have a test-runner (which is written in ruby) and a command-line tool ([`sassc`][6]) to run the tests. Therefore we need to additionally compile `sassc`. To do this, the build files of all three projects need to work together. This may not have the same quality for all build flavors. You definitely need to have ruby (2.1?) installed (version 1.9 seems to cause problems at least on windows). You also need some gems installed: ```bash ruby -v gem install minitest # should be optional gem install minitap ``` Including the LibSass version -- There is a function in `libsass` to query the current version. This has to be defined at compile time. We use a C macro for this, which can be defined by calling `g++ -DLIBSASS_VERSION="\"x.y.z.\""`. The two quotes are necessary, since it needs to end up as a valid C string. Normally you do not need to do anything if you use the makefiles or autotools. They will try to fetch the version via git directly. If you only have the sources without the git repo, you can pass the version as an environment variable to `make` or `configure`: ``` export LIBSASS_VERSION="x.y.z." ``` Continuous Integration -- We use two CI services to automatically test all commits against the latest [spec test-suite][5]. - [LibSass on GitHub Actions (linux)][7] [![Build Status](https://github.com/sass/libsass/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/sass/libsass/actions/workflows/build-and-test.yml) - [LibSass on AppVeyor (windows)][8] [![Build status](https://ci.appveyor.com/api/projects/status/github/sass/libsass?svg=true)](https://ci.appveyor.com/project/sass/libsass/branch/master) Why not using CMake? -- There were some efforts to get `libsass` to compile with CMake, which should make it easier to create build files for linux and windows. Unfortunately this was not completed. But we are certainly open for PRs! Miscellaneous -- - [Ebuilds for Gentoo Linux](build-on-gentoo.md) [1]: build-with-makefiles.md [2]: build-with-autotools.md [3]: build-with-mingw.md [4]: build-shared-library.md [5]: https://github.com/sass/sass-spec [6]: https://github.com/sass/sassc [7]: https://github.com/sass/libsass/blob/master/.github/workflows [8]: https://github.com/sass/libsass/blob/master/appveyor.yml [9]: implementations.md [10]: build-on-darwin.md [11]: build-with-visual-studio.md golibsass-1.0.0/libsass_src/docs/contributing.md000066400000000000000000000025751405214413600217770ustar00rootroot00000000000000First of all, welcome! Thanks for even reading this page. If you're here, you're probably wondering what you can do to help make the LibSass project even more awesome. And, even having that feeling means you are awesome! ## I'm a programmer Awesome! We need your help. The best thing to do is go find issues that are tagged with both "bug" and "test written". We do spec driven development here and these issues have a test that's written already in the sass-spec project. Go find the test by going to sass-spec/spec/LibSass-todo-issues/issue_XXX/ where XXX is the issue number. Write the code, and compile, and then issue a pull request referencing the issue. We'll quickly verify it and get it merged in! To get your dev environment setup, check out our article on [Setup-Dev-Environment](setup-environment.md). ## I'm not a backend programmer COOL! We also need your help. Doing [Issue-Triage](triage.md) is a big deal and something we need constant help with. That means helping to verify issues, write tests for them, and make sure they are getting fixed. It's being part of the smiling face of the project. Also, we need help with the Sass-Spec project itself. Just people to organize, refactor, and understand the tests in there. ## I don't know what a computer is? Hmm.... well, it's the thing you are looking at right now. Ummm... check out training courses! Then, come back and join us! golibsass-1.0.0/libsass_src/docs/custom-functions-internal.md000066400000000000000000000102351405214413600244120ustar00rootroot00000000000000# Developer Documentation Custom functions are internally represented by `struct Sass_C_Function_Descriptor`. ## Sass_C_Function_Descriptor ```C struct Sass_C_Function_Descriptor { const char* signature; Sass_C_Function function; void* cookie; }; ``` - `signature`: The function declaration, like `foo($bar, $baz:1)` - `function`: Reference to the C function callback - `cookie`: any pointer you want to attach ### signature The signature defines how the function can be invoked. It also declares which arguments are required and which are optional. Required arguments will be enforced by LibSass and a Sass error is thrown in the event a call as missing an argument. Optional arguments only need to be present when you want to overwrite the default value. foo($bar, $baz: 2) In this example, `$bar` is required and will error if not passed. `$baz` is optional and the default value of it is 2. A call like `foo(10)` is therefore equal to `foo(10, 2)`, while `foo()` will produce an error. ### function The callback function needs to be of the following form: ```C union Sass_Value* call_sass_function( const union Sass_Value* s_args, void* cookie ) { return sass_clone_value(s_args); } ``` ### cookie The cookie can hold any pointer you want. In the `perl-libsass` implementation it holds the structure with the reference of the actual registered callback into the perl interpreter. Before that call `perl-libsass` will convert all `Sass_Values` to corresponding perl data types (so they can be used natively inside the perl interpreter). The callback can also return a `Sass_Value`. In `perl-libsass` the actual function returns a perl value, which has to be converted before `libsass` can work with it again! ## Sass_Values ```C // allocate memory (copies passed strings) union Sass_Value* sass_make_null (void); union Sass_Value* sass_make_boolean (bool val); union Sass_Value* sass_make_string (const char* val); union Sass_Value* sass_make_qstring (const char* val); union Sass_Value* sass_make_number (double val, const char* unit); union Sass_Value* sass_make_color (double r, double g, double b, double a); union Sass_Value* sass_make_list (size_t len, enum Sass_Separator sep, bool is_bracketed); union Sass_Value* sass_make_map (size_t len); union Sass_Value* sass_make_error (const char* msg); union Sass_Value* sass_make_warning (const char* msg); // Make a deep cloned copy of the given sass value union Sass_Value* sass_clone_value (const union Sass_Value* val); // deallocate memory (incl. all copied memory) void sass_delete_value (const union Sass_Value* val); ``` ## Example main.c ```C #include #include #include "sass/context.h" union Sass_Value* call_fn_foo(const union Sass_Value* s_args, void* cookie) { // we actually abuse the void* to store an "int" return sass_make_number((size_t)cookie, "px"); } int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate a custom function caller Sass_C_Function_Callback fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions Sass_C_Function_List fn_list = sass_make_function_list(1); sass_function_set_list_entry(fn_list, 0, fn_foo); sass_option_set_c_functions(ctx_opt, fn_list); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` ## Compile main.c ```bash gcc -c main.c -o main.o gcc -o sample main.o -lsass echo "foo { margin: foo(); }" > foo.scss ./sample foo.scss => "foo { margin: 42px }" ``` golibsass-1.0.0/libsass_src/docs/dev-ast-memory.md000066400000000000000000000171161405214413600221360ustar00rootroot00000000000000# LibSass smart pointer implementation LibSass uses smart pointers very similar to `shared_ptr` known by Boost or C++11. Implementation is a bit less modular since it was not needed. Various compile time debug options are available if you need to debug memory life-cycles. ## Memory Classes ### SharedObj Base class for the actual node implementations. This ensures that every object has a reference counter and other values. ```c++ class AST_Node : public SharedObj { ... }; ``` ### SharedPtr (base class for SharedImpl) Base class that holds on to the pointer. The reference counter is stored inside the pointer object directly (`SharedObj`). ### SharedImpl (inherits from SharedPtr) This is the main base class for objects you use in your code. It will make sure that the memory it points at will be deleted once all copies to the same object/memory go out of scope. ```c++ Class* pointer = new Class(...); SharedImpl obj(pointer); ``` To spare the developer of typing the templated class every time, we created typedefs for each available AST Node specialization. ```c++ typedef SharedImpl Number_Obj; Number_Obj number = SASS_MEMORY_NEW(...); ``` ## Memory life-cycles ### Pointer pickups I often use the terminology of "pickup". This means the moment when a raw pointer not under any control is assigned to a reference counted object (`XYZ_Obj = XYZ_Ptr`). From that point on memory will be automatically released once the object goes out of scope (but only if the reference counter reaches zero). Main point being, you don't have to worry about memory management yourself. ### Object detach Sometimes we can't return reference counted objects directly (see invalid covariant return types problems below). But we often still need to use reference objects inside a function to avoid leaks when something throws. For this you can use `detach`, which basically detaches the pointer memory from the reference counted object. So when the reference counted object goes out of scope, it will not free the attached memory. You are now again in charge of freeing the memory (just assign it to a reference counted object again). ## Circular references Reference counted memory implementations are prone to circular references. This can be addressed by using a multi generation garbage collector. But for our use-case that seems overkill. There is no way so far for users (sass code) to create circular references. Therefore we can code around this possible issue. But developers should be aware of this limitation. There are AFAIR two places where circular references could happen. One is the `sources` member on every `Selector`. The other one can happen in the extend code (Node handling). The easy way to avoid this is to only assign complete object clones to these members. If you know the objects lifetime is longer than the reference you create, you can also just store the raw pointer. Once needed this could be solved with weak pointers. ## Addressing the invalid covariant return types problems If you are not familiar with the mentioned problem, you may want to read up on covariant return types and virtual functions, i.e. - http://stackoverflow.com/questions/6924754/return-type-covariance-with-smart-pointers - http://stackoverflow.com/questions/196733/how-can-i-use-covariant-return-types-with-smart-pointers - http://stackoverflow.com/questions/2687790/how-to-accomplish-covariant-return-types-when-returning-a-shared-ptr We hit this issue at least with the CRTP visitor pattern (eval, expand, listize and so forth). This means we cannot return reference counted objects directly. We are forced to return raw pointers or we would need to have a lot of explicit and expensive upcasts by callers/consumers. ### Simple functions that allocate new AST Nodes In the parser step we often create new objects and can just return a unique pointer (meaning ownership clearly shifts back to the caller). The caller/consumer is responsible that the memory is freed. ```c++ typedef Number* Number_Ptr; int parse_integer() { ... // do the parsing return 42; } Number_Ptr parse_number() { Number_Ptr p_nr = SASS_MEMORY_NEW(...); p_nr->value(parse_integer()); return p_nr; } Number_Obj nr = parse_number(); ``` The above would be the encouraged pattern for such simple cases. ### Allocate new AST Nodes in functions that can throw There is a major caveat with the previous example, considering this more real-life implementation that throws an error. The throw may happen deep down in another function. Holding raw pointers that we need to free would leak in this case. ```c++ int parse_integer() { ... // do the parsing if (error) throw(error); return 42; } ``` With this `parse_integer` function the previous example would leak memory. I guess it is pretty obvious, as the allocated memory will not be freed, as it was never assigned to a SharedObj value. Therefore the above code would better be written as: ```c++ typedef Number* Number_Ptr; int parse_integer() { ... // do the parsing if (error) throw(error); return 42; } // this leaks due to pointer return // should return Number_Obj instead // though not possible for virtuals! Number_Ptr parse_number() { Number_Obj nr = SASS_MEMORY_NEW(...); nr->value(parse_integer()); // throws return &nr; // Ptr from Obj } Number_Obj nr = parse_number(); // will now be freed automatically ``` The example above unfortunately will not work as is, since we return a `Number_Ptr` from that function. Therefore the object allocated inside the function is already gone when it is picked up again by the caller. The easy fix for the given simplified use case would be to change the return type of `parse_number` to `Number_Obj`. Indeed we do it exactly this way in the parser. But as stated above, this will not work for virtual functions due to invalid covariant return types! ### Return managed objects from virtual functions The easy fix would be to just create a new copy on the heap and return that. But this seems like a very inelegant solution to this problem. I mean why can't we just tell the object to treat it like a newly allocated object? And indeed we can. I've added a `detach` method that will tell the object to survive deallocation until the next pickup. This means that it will leak if it is not picked up by consumer. ```c++ typedef Number* Number_Ptr; int parse_integer() { ... // do the parsing if (error) throw(error); return 42; } Number_Ptr parse_number() { Number_Obj nr = SASS_MEMORY_NEW(...); nr->value(parse_integer()); // throws return nr.detach(); } Number_Obj nr = parse_number(); // will now be freed automatically ``` ## Compile time debug options To enable memory debugging you need to define `DEBUG_SHARED_PTR`. This can i.e. be done in `include/sass/base.h` ```c++ define DEBUG_SHARED_PTR ``` This will print lost memory on exit to stderr. You can also use `setDbg(true)` on sepecific variables to emit reference counter increase, decrease and other events. ## Why reinvent the wheel when there is `shared_ptr` from C++11 First, implementing a smart pointer class is not really that hard. It was indeed also a learning experience for myself. But there are more profound advantages: - Better GCC 4.4 compatibility (which most code still has OOTB) - Not thread safe (give us some free performance on some compiler) - Being able to track memory allocations for debugging purposes - Adding additional features if needed (as seen in `detach`) - Optional: optimized weak pointer implementation possible ### Thread Safety As said above, this is not thread safe currently. But we don't need this ATM anyway. And I guess we probably never will share AST Nodes across different threads.golibsass-1.0.0/libsass_src/docs/dev-profiling.md000066400000000000000000000032751405214413600220330ustar00rootroot00000000000000# Profiling LibSass ## Linux perf and pprof On Linux, you can record the profile with `perf` and inspect it with `pprof`. ### Install required tools Pre-requisites: 1. Linux `perf`, commonly found in the `linux-tools-generic` package. 2. [go], for installing `pprof`. 3. [bazel], for installing `perf_to_profile`. [go]: https://golang.org [bazel]: https://bazel.build First, install `pprof` with: ```bash go get -u github.com/google/pprof ``` Then, build and install `perf_to_profile`: ```bash git clone https://github.com/google/perf_data_converter cd perf_data_converter bazel build -c opt src:perf_to_profile sudo cp bazel-bin/src/perf_to_profile /usr/local/bin/ ``` Finally, in your libsass repository, clone and build `sassc`: ```bash git clone https://github.com/sass/sassc.git make sassc ``` ### Record perf data ```bash sudo perf record sassc/bin/sassc input.scss > /dev/null && sudo chown $USER:$USER perf.data ``` This will create a `perf.data` file that you can vizualize with `pprof`. ### Inspect perf data A web server with various visualization options: ```bash pprof -http=localhost:3232 sassc/bin/sassc perf.data ``` Simple text output: ```bash pprof -text sassc/bin/sassc perf.data ``` Example output: ``` flat flat% sum% cum cum% 24651348 6.97% 6.97% 24651348 6.97% [[kernel.kallsyms]] 20746241 5.87% 12.84% 20746241 5.87% Sass::SharedPtr::decRefCount 18401663 5.20% 18.04% 20420896 5.78% __libc_malloc 15205959 4.30% 22.34% 15205959 4.30% [libc-2.27.so] 12974307 3.67% 26.01% 14070189 3.98% _int_malloc 10958857 3.10% 29.11% 10958857 3.10% Sass::SharedPtr::incRefCount 9837672 2.78% 31.89% 18433250 5.21% cfree ``` golibsass-1.0.0/libsass_src/docs/developing.md000066400000000000000000000005711405214413600214160ustar00rootroot00000000000000## Developing LibSass So far this is only a loose collection of developer relevant docs: - [Building LibSass](build.md) - [Profiling LibSass](dev-profiling.md) - [C-API documentation](api-doc.md) - [LibSass and Unicode](unicode.md) - [SourceMap internals](source-map-internals.md) - [Custom memory allocator](allocator.md) - [Smart pointer implementation](dev-ast-memory.md) golibsass-1.0.0/libsass_src/docs/implementations.md000066400000000000000000000040471405214413600224740ustar00rootroot00000000000000There are several implementations of `libsass` for a variety of languages. Here are just a few of them. Note, some implementations may or may not be up to date. We have not verified whether they work. ### C * [sassc](https://github.com/hcatlin/sassc) ### Crystal * [sass.cr](https://github.com/straight-shoota/sass.cr) ### Elixir * [sass.ex](https://github.com/scottdavis/sass.ex) * [sass_compiler](https://github.com/Youimmi/sass_compiler) ### Go * [go-libsass](https://github.com/wellington/go-libsass) * [go_sass](https://github.com/suapapa/go_sass) * [go-sass](https://github.com/SamWhited/go-sass) ### Haskell * [hLibsass](https://github.com/jakubfijalkowski/hlibsass) * [hSass](https://github.com/jakubfijalkowski/hsass) ### Java * [libsass-maven-plugin](https://github.com/warmuuh/libsass-maven-plugin) * [jsass](https://github.com/bit3/jsass) ### JavaScript * [sass.js](https://github.com/medialize/sass.js) ### Lua * [lua-sass](https://github.com/craigbarnes/lua-sass) ### .NET * [libsass-net](https://github.com/darrenkopp/libsass-net) * [NSass](https://github.com/TBAPI-0KA/NSass) * [Sass.Net](https://github.com/andyalm/Sass.Net) * [SharpScss](https://github.com/xoofx/SharpScss) * [LibSassHost](https://github.com/Taritsyn/LibSassHost) ### Nim * [sass](https://github.com/dom96/sass) * [nim-sass](https://github.com/zacharycarter/nim-sass) ### node.js * [node-sass](https://github.com/sass/node-sass) ### Perl * [CSS::Sass](https://github.com/caldwell/CSS-Sass) * [Text::Sass::XS](https://github.com/ysasaki/Text-Sass-XS) ### PHP * [sassphp](https://github.com/sensational/sassphp) * [php-sass](https://github.com/lesstif/php-sass) ### Python * [libsass-python](https://github.com/dahlia/libsass-python) * [SassPython](https://github.com/marianoguerra/SassPython) * [pylibsass](https://github.com/rsenk330/pylibsass) * [python-scss](https://github.com/pistolero/python-scss) ### Ruby * [sassruby](https://github.com/hcatlin/sassruby) ### Scala * [Sass-Scala](https://github.com/kkung/Sass-Scala) ### Tcl * [tclsass](https://github.com/flightaware/tclsass) golibsass-1.0.0/libsass_src/docs/plugins.md000066400000000000000000000035731405214413600207500ustar00rootroot00000000000000Plugins are shared object files (.so on *nix and .dll on win) that can be loaded by LibSass on runtime. Currently we only provide a way to load internal/custom functions from plugins. In the future we probably will also add a way to provide custom importers via plugins (needs more refactoring to [support multiple importers with some kind of priority system](https://github.com/sass/libsass/issues/962)). ## plugin.cpp ```C++ #include #include #include #include "sass_values.h" union Sass_Value* ADDCALL call_fn_foo(const union Sass_Value* s_args, void* cookie) { // we actually abuse the void* to store an "int" return sass_make_number((intptr_t)cookie, "px"); } extern "C" const char* ADDCALL libsass_get_version() { return libsass_version(); } extern "C" Sass_C_Function_List ADDCALL libsass_load_functions() { // allocate a custom function caller Sass_C_Function_Callback fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions Sass_C_Function_List fn_list = sass_make_function_list(1); // put the only function in this plugin to the list sass_function_set_list_entry(fn_list, 0, fn_foo); // return the list return fn_list; } ``` To compile the plugin you need to have LibSass already built as a shared library (to link against it). The commands below expect the shared library in the `lib` sub-directory (`-Llib`). The plugin and the main LibSass process should "consume" the same shared LibSass library on runtime. It will propably also work if they use different LibSass versions. In this case we check if the major versions are compatible (i.e. 3.1.3 and 3.1.1 would be considered compatible). ## Compile with gcc on linux ```bash g++ -O2 -shared plugin.cpp -o plugin.so -fPIC -Llib -lsass ``` ## Compile with mingw on windows ```bash g++ -O2 -shared plugin.cpp -o plugin.dll -Llib -lsass ``` golibsass-1.0.0/libsass_src/docs/setup-environment.md000066400000000000000000000047531405214413600227720ustar00rootroot00000000000000## Requirements In order to install and setup your local development environment, there are some prerequisites: * git * gcc/clang/llvm (Linux: build tools, Mac OS X: XCode w/ Command Line Tools) * ruby w/ bundler OS X: First you'll need to install XCode which you can now get from the AppStore installed on your mac. After you download that and run it, then run this on the command line: ```` xcode-select --install ```` ## Cloning the Projects First, clone the project and then add a line to your `~/.bash_profile` that will let other programs know where the LibSass dev files are. ```` git clone git@github.com:sass/libsass.git cd libsass echo "export SASS_LIBSASS_PATH=$(pwd)" >> ~/.bash_profile ```` Then, if you run the "bootstrap" script, it should clone all the other required projects. ```` ./script/bootstrap ```` You should now have a `sass-spec` and `sassc` folder within the libsass folder. Both of these are clones of their respective git projects. If you want to do a pull request, remember to work in those folders. For instance, if you want to add a test (see other documentation for how to do that), make sure to commit it to your *fork* of the sass-spec github project. Also, whenever you are running tests, make sure to `pull` from the origin! We want to make sure we are testing against the newest libsass, sassc, and sass-spec! Now, try and see if you can build the project. We do that with the `make` command. ```` make ```` At this point, if you get an error, something is most likely wrong with your compiler installation. Yikes. It's hard to cover how to fix this in an article. Feel free to open an issue and we'll try and help! But, remember, before you do that, googling the error message is your friend! Many problems are solved quickly that way. ## Running The Spec Against LibSass Then, to run the spec against LibSass, just run: ```` ./script/spec ```` If you get an error about `SASS_LIBSASS_PATH`, you may still need to set a variable pointing to the libsass folder, like this: ```` export SASS_LIBSASS_PATH=/Users/you/path/libsass ```` ...where the latter part is to the `libsass` directory you've cloned. You can get this path by typing `pwd` in the Terminal ## Running the Spec Against Ruby Sass Go into the sass-spec folder that should have been cloned earlier with the "bootstrap" command. Run the following. ```` bundle install ./sass-spec.rb ```` Voila! Now you are testing against Sass too! ## Profiling Is libsass being slow? See the [Profiling guide](dev-profiling.md). golibsass-1.0.0/libsass_src/docs/source-map-internals.md000066400000000000000000000044261405214413600233350ustar00rootroot00000000000000This document is mainly intended for developers! # Documenting some of the source map internals Since source maps are somewhat a black box to all LibSass maintainers, [I](@mgreter) will try to document my findings with source maps in LibSass, as I come across them. This document will also brievely explain how LibSass parses the source and how it outputs the result. The main storage for SourceMap mappings is the `mappings` vector: ``` # in source_map.hpp vector mappings # in mappings.hpp struct Mapping ... Position original_position; Position generated_position; ``` ## Every parsed token has its source associated LibSass uses a lexical parser. Whenever LibSass finds a token of interest, it creates a specific `AST_Node`, which will hold a reference to the input source with line/column information. `AST_Node` is the base class for all parsed items. They are declared in `ast.hpp` and are used in `parser.hpp`. Here a simple example: ``` if (lex< custom_property_name >()) { Sass::String* prop = new (ctx.mem) String_Constant(path, source_position, lexed); return new (ctx.mem) Declaration(path, prop->position(), prop, ...); } ``` ## How is the `source_position` calculated This is automatically done with `lex` in `parser.hpp`. Whenever something is lexed, the `source_position` is updated. But be aware that `source_position` points to the beginning of the parsed text. If you need a mapping for the position where the parsing ended, you need to add another call to `lex` (to match nothing)! ``` lex< exactly < empty_str > >(); end = new (ctx.mem) String_Constant(path, source_position, lexed); ``` ## How are mappings for the output created So far we have collected all needed data for all tokens in the input stream. We can now use this information to create mappings when we put things into the output stream. Mappings are created via the `add_mappings` method: ``` # in source_map.hpp void add_mapping(AST_Node* node); ``` This method is called in two places: - `Inspect::append_to_buffer` - `Output_[Nested|Compressed]::append_to_buffer` Mappings can only be created for things that have been parsed into a `AST_Node`. Otherwise we do not have the information to create the mappings, which is the reason why LibSass currently only maps the most important tokens in source maps. golibsass-1.0.0/libsass_src/docs/trace.md000066400000000000000000000015741405214413600203640ustar00rootroot00000000000000## This is proposed interface in https://github.com/sass/libsass/pull/1288 Additional debugging macros with low overhead are available, `TRACE()` and `TRACEINST()`. Both macros simulate a string stream, so they can be used like this: TRACE() << "Reached."; produces: [LibSass] parse_value parser.cpp:1384 Reached. `TRACE()` logs function name, source filename, source file name to the standard error and the attached stream to the standard error. `TRACEINST(obj)` logs object instance address, function name, source filename, source file name to the standard error and the attached stream to the standard error, for example: TRACEINST(this) << "String_Constant created " << this; produces: [LibSass] 0x8031ba980:String_Constant ./ast.hpp:1371 String_Constant created (0,"auto") The macros generate output only of `LibSass_TRACE` is set in the environment. golibsass-1.0.0/libsass_src/docs/triage.md000066400000000000000000000033551405214413600205400ustar00rootroot00000000000000This is an article about how to help with LibSass issues. Issue triage is a fancy word for explaining how we deal with incoming issues and make sure that the right problems get worked on. The lifecycle of an issue goes like this: 1. Issue is reported by a user. 2. If the issue seems like a bug, then the "bug" tag is added. 3. If the reporting user didn't also create a spec test over at sass/sass-spec, the "needs test" tag is added. 4. Verify that Ruby Sass *does not* have the same bug. LibSass strives to be an exact replica of how Ruby Sass works. If it's an issue that neither project has solved, please close the ticket with the "not in sass" label. 5. The smallest possible breaking test is created in sass-spec. Cut away any extra information or non-breaking code until the core issue is made clear. 6. Again, verify that the expected output matches the latest Ruby Sass release. Do this by using your own tool OR by running ./sass-spec.rb in the spec folder and making sure that your test passes! 7. Create the test cases in sass-spec with the name spec/LibSass-todo-issues/issue_XXX/input.scss and expected_output.css where the XXX is the issue number here. 8. Commit that test to sass-spec, making sure to reference the issue in the comment message like "Test to demonstrate sass/LibSass#XXX". 9. Once the spec test exists, remove the "needs test" tag and replace it with "test written". 10. A C++ developer will then work on the issue and issue a pull request to fix the issue. 11. A core member verifies that the fix does actually fix the spec tests. 12. The fix is merged into the project. 13. The spec is moved from the LibSass-todo-issues folder into LibSass-closed-issues 14. The issue is closed 15. Have a soda pop or enjoyable beverage of your choice golibsass-1.0.0/libsass_src/docs/unicode.md000066400000000000000000000116531405214413600207130ustar00rootroot00000000000000LibSass currently expects all input to be utf8 encoded (and outputs only utf8), if you actually have any unicode characters at all. We do not support conversion between encodings, even if you declare it with a `@charset` rule. The text below was originally posted as an [issue](https://github.com/sass/libsass/issues/381) on the LibSass tracker. Since then the status is outdated as LibSass now expects your input to be utf8/ascii compatible, as it has been proven that reading ANSI (e.g. single byte encodings) as utf8 can lead to unexpected behavior, which can in the worst case lead to buffer overruns/segfaults. Therefore LibSass now checks your input to be valid utf8 encoded! ### [Declaring character encodings in CSS](http://www.w3.org/International/questions/qa-css-charset.en) This [explains](http://www.w3.org/International/questions/qa-css-charset.en) how the character encoding of a css file is determined. Since we are only dealing with local files, we never have a HTTP header. So the precedence should be 'charset' rule, byte-order mark (BOM) or auto-detection (finally falling back to system default/UTF-8). This may not sound too hard to implement, but what about import rules? The CSS specs do not forbid the mixing of different encodings! I [solved that](https://github.com/mgreter/webmerge/) by converting all files to UTF-8 internally. On writing there is an option to tell the tool what encoding it should be (UTF-8 by default). One can also define if it should write a BOM or not and if it should add the charset declaration. Since my [tool]((https://github.com/mgreter/webmerge/)) is written in perl, I have a lot of utilities at hand to deal with different unicode charsets. I'm pretty sure that most OSS uses [ICU](http://site.icu-project.org/) or [libiconv](https://www.gnu.org/software/libiconv/) to convert between different encodings. But I have now idea how easy/hard this would be to integrate platform independent (it seems doable). ANSII (single byte encoding) to utf8 is basically just a conversion table (for every supported code-page). ### Current status on LibSass unicode support LibSass should/is fully UTF (and therefore plain ASCII) compatible. ~~Currently LibSass seems to handle the common UTF-8 case pretty well. I believe it should correctly support all ASCII compatible encodings (like UTF-8 or Latin-1). If all includes use the same encoding, the output should be correct (in the same encoding). It should also handle unicode chars in [selectors, variable names and other identifiers](https://github.com/hcatlin/libsass/issues/244#issuecomment-34681227). This is true for all ASCII compatible encodings. So the main incompatible encodings (I'm aware of) are UTF-16/UTF-32 (which could be converted to UTF-8 with libiconv).~~ LibSass 3.5 will enforce that your input is either plain ASCII (chars below 127) or utf8. It does not handle anything else, but therefore ensures that the output is in a valid form. Before version 3.5 you were able to mix different code-pages, which yielded unexpected behavior. ### Current encoding auto detection LibSass currently reads all kind of BOMs and will error out if it finds something it doesn't know how to handle! It seems that it throws away the optional UTF-8 BOM (if any is found). IMO it would be nice if users could configure that (also if a charset rule should be added to the output). But it does not really take any `@charset` into account, it always assumes your input is utf8 and ignores any given `@charset`! ### What is currently not supported - Using non ASCII compatible encodings (like UTF-16, Latin-1 etc.) - Using non ASCII characters in different encodings in different includes ### What is missing to support the above cases - A way to convert between encodings (like libiconv/ICU) - Sniffing the charset inside the file (source is available) - Handling the conversion on import (and export) - Optional: Make output encoding configurable - Optional: Add optional/mandatory BOM (configurable) ### Low priority feature I guess the current implementation should handle more than 99% of all real world use cases. A) Unicode characters are still seldom seen (as they can be written escaped) ~~B) It will still work if it's UTF-8 or in any of the most common known western ISO codepages. Although I'm not sure how this applies to asian and other "exotic" codepages!~~ I guess the biggest Problem is to have libiconv/ICU (or some other) library as a dependency. Since it contains a lot of rules for the conversions, I see it as the only way to handle this correctly. Once that is sorted out it should be pretty much straight forward to implement the missing pieces (in parser.cpp - Parser::parse should return encoding and add Parser::sniff_charset, then convert the source byte stream to UTF-8). I hope the statements above all hold true. Unicode is really not the easiest topic to wrap your head around. But since I did all the above recently in Perl, I wanted to document it here. Feel free to extend or criticize. golibsass-1.0.0/libsass_src/include/000077500000000000000000000000001405214413600174305ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/include/sass.h000066400000000000000000000003521405214413600205520ustar00rootroot00000000000000#ifndef SASS_H #define SASS_H // #define DEBUG 1 // include API headers #include #include #include #include #include #include #endif golibsass-1.0.0/libsass_src/include/sass/000077500000000000000000000000001405214413600204015ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/include/sass/base.h000066400000000000000000000045011405214413600214640ustar00rootroot00000000000000#ifndef SASS_BASE_H #define SASS_BASE_H // #define DEBUG // #define DEBUG_SHARED_PTR #ifdef _MSC_VER #pragma warning(disable : 4503) #ifndef _SCL_SECURE_NO_WARNINGS #define _SCL_SECURE_NO_WARNINGS #endif #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif // Work around lack of `noexcept` keyword support in VS2013 #if defined(_MSC_VER) && (_MSC_VER <= 1800) && !defined(_ALLOW_KEYWORD_MACROS) #define _ALLOW_KEYWORD_MACROS 1 #define noexcept throw( ) #endif #include #include #ifdef __GNUC__ #define DEPRECATED(func) func __attribute__ ((deprecated)) #elif defined(_MSC_VER) #define DEPRECATED(func) __declspec(deprecated) func #else #pragma message("WARNING: You need to implement DEPRECATED for this compiler") #define DEPRECATED(func) func #endif #ifdef _WIN32 /* You should define ADD_EXPORTS *only* when building the DLL. */ #ifdef ADD_EXPORTS #define ADDAPI __declspec(dllexport) #define ADDCALL __cdecl #else #define ADDAPI #define ADDCALL #endif #else /* _WIN32 not defined. */ /* Define with no value on non-Windows OSes. */ #define ADDAPI #define ADDCALL #endif /* Make sure functions are exported with C linkage under C++ compilers. */ #ifdef __cplusplus extern "C" { #endif // Different render styles enum Sass_Output_Style { SASS_STYLE_NESTED, SASS_STYLE_EXPANDED, SASS_STYLE_COMPACT, SASS_STYLE_COMPRESSED, // only used internaly SASS_STYLE_INSPECT, SASS_STYLE_TO_SASS, SASS_STYLE_TO_CSS }; // to allocate buffer to be filled ADDAPI void* ADDCALL sass_alloc_memory(size_t size); // to allocate a buffer from existing string ADDAPI char* ADDCALL sass_copy_c_string(const char* str); // to free overtaken memory when done ADDAPI void ADDCALL sass_free_memory(void* ptr); // Some convenient string helper function ADDAPI char* ADDCALL sass_string_quote (const char* str, const char quote_mark); ADDAPI char* ADDCALL sass_string_unquote (const char* str); // Implemented sass language version // Hardcoded version 3.4 for time being ADDAPI const char* ADDCALL libsass_version(void); // Get compiled libsass language ADDAPI const char* ADDCALL libsass_language_version(void); #ifdef __cplusplus } // __cplusplus defined. #endif #endif golibsass-1.0.0/libsass_src/include/sass/context.h000066400000000000000000000247661405214413600222550ustar00rootroot00000000000000#ifndef SASS_C_CONTEXT_H #define SASS_C_CONTEXT_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif // Forward declaration struct Sass_Compiler; // Forward declaration struct Sass_Options; // base struct struct Sass_Context; // : Sass_Options struct Sass_File_Context; // : Sass_Context struct Sass_Data_Context; // : Sass_Context // Compiler states enum Sass_Compiler_State { SASS_COMPILER_CREATED, SASS_COMPILER_PARSED, SASS_COMPILER_EXECUTED }; // Create and initialize an option struct ADDAPI struct Sass_Options* ADDCALL sass_make_options (void); // Create and initialize a specific context ADDAPI struct Sass_File_Context* ADDCALL sass_make_file_context (const char* input_path); ADDAPI struct Sass_Data_Context* ADDCALL sass_make_data_context (char* source_string); // Call the compilation step for the specific context ADDAPI int ADDCALL sass_compile_file_context (struct Sass_File_Context* ctx); ADDAPI int ADDCALL sass_compile_data_context (struct Sass_Data_Context* ctx); // Create a sass compiler instance for more control ADDAPI struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx); ADDAPI struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx); // Execute the different compilation steps individually // Useful if you only want to query the included files ADDAPI int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler); ADDAPI int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler); // Release all memory allocated with the compiler // This does _not_ include any contexts or options ADDAPI void ADDCALL sass_delete_compiler(struct Sass_Compiler* compiler); ADDAPI void ADDCALL sass_delete_options(struct Sass_Options* options); // Release all memory allocated and also ourself ADDAPI void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx); ADDAPI void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx); // Getters for context from specific implementation ADDAPI struct Sass_Context* ADDCALL sass_file_context_get_context (struct Sass_File_Context* file_ctx); ADDAPI struct Sass_Context* ADDCALL sass_data_context_get_context (struct Sass_Data_Context* data_ctx); // Getters for Context_Options from Sass_Context ADDAPI struct Sass_Options* ADDCALL sass_context_get_options (struct Sass_Context* ctx); ADDAPI struct Sass_Options* ADDCALL sass_file_context_get_options (struct Sass_File_Context* file_ctx); ADDAPI struct Sass_Options* ADDCALL sass_data_context_get_options (struct Sass_Data_Context* data_ctx); ADDAPI void ADDCALL sass_file_context_set_options (struct Sass_File_Context* file_ctx, struct Sass_Options* opt); ADDAPI void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* data_ctx, struct Sass_Options* opt); // Getters for Context_Option values ADDAPI int ADDCALL sass_option_get_precision (struct Sass_Options* options); ADDAPI enum Sass_Output_Style ADDCALL sass_option_get_output_style (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_comments (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_embed (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_contents (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_file_urls (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_omit_source_map_url (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_is_indented_syntax_src (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_indent (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_linefeed (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_input_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_output_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_source_map_file (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_source_map_root (struct Sass_Options* options); ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_headers (struct Sass_Options* options); ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_importers (struct Sass_Options* options); ADDAPI Sass_Function_List ADDCALL sass_option_get_c_functions (struct Sass_Options* options); // Setters for Context_Option values ADDAPI void ADDCALL sass_option_set_precision (struct Sass_Options* options, int precision); ADDAPI void ADDCALL sass_option_set_output_style (struct Sass_Options* options, enum Sass_Output_Style output_style); ADDAPI void ADDCALL sass_option_set_source_comments (struct Sass_Options* options, bool source_comments); ADDAPI void ADDCALL sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed); ADDAPI void ADDCALL sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents); ADDAPI void ADDCALL sass_option_set_source_map_file_urls (struct Sass_Options* options, bool source_map_file_urls); ADDAPI void ADDCALL sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url); ADDAPI void ADDCALL sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src); ADDAPI void ADDCALL sass_option_set_indent (struct Sass_Options* options, const char* indent); ADDAPI void ADDCALL sass_option_set_linefeed (struct Sass_Options* options, const char* linefeed); ADDAPI void ADDCALL sass_option_set_input_path (struct Sass_Options* options, const char* input_path); ADDAPI void ADDCALL sass_option_set_output_path (struct Sass_Options* options, const char* output_path); ADDAPI void ADDCALL sass_option_set_plugin_path (struct Sass_Options* options, const char* plugin_path); ADDAPI void ADDCALL sass_option_set_include_path (struct Sass_Options* options, const char* include_path); ADDAPI void ADDCALL sass_option_set_source_map_file (struct Sass_Options* options, const char* source_map_file); ADDAPI void ADDCALL sass_option_set_source_map_root (struct Sass_Options* options, const char* source_map_root); ADDAPI void ADDCALL sass_option_set_c_headers (struct Sass_Options* options, Sass_Importer_List c_headers); ADDAPI void ADDCALL sass_option_set_c_importers (struct Sass_Options* options, Sass_Importer_List c_importers); ADDAPI void ADDCALL sass_option_set_c_functions (struct Sass_Options* options, Sass_Function_List c_functions); // Getters for Sass_Context values ADDAPI const char* ADDCALL sass_context_get_output_string (struct Sass_Context* ctx); ADDAPI int ADDCALL sass_context_get_error_status (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_json (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_text (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_message (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_file (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_src (struct Sass_Context* ctx); ADDAPI size_t ADDCALL sass_context_get_error_line (struct Sass_Context* ctx); ADDAPI size_t ADDCALL sass_context_get_error_column (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_source_map_string (struct Sass_Context* ctx); ADDAPI char** ADDCALL sass_context_get_included_files (struct Sass_Context* ctx); // Getters for options include path array ADDAPI size_t ADDCALL sass_option_get_include_path_size(struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_include_path(struct Sass_Options* options, size_t i); // Plugin paths to load dynamic libraries work the same ADDAPI size_t ADDCALL sass_option_get_plugin_path_size(struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_plugin_path(struct Sass_Options* options, size_t i); // Calculate the size of the stored null terminated array ADDAPI size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx); // Take ownership of memory (value on context is set to 0) ADDAPI char* ADDCALL sass_context_take_error_json (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_text (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_message (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_file (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_error_src (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_output_string (struct Sass_Context* ctx); ADDAPI char* ADDCALL sass_context_take_source_map_string (struct Sass_Context* ctx); ADDAPI char** ADDCALL sass_context_take_included_files (struct Sass_Context* ctx); // Getters for Sass_Compiler options ADDAPI enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler); ADDAPI struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler); ADDAPI struct Sass_Options* ADDCALL sass_compiler_get_options(struct Sass_Compiler* compiler); ADDAPI size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler); ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler); ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx); ADDAPI size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler); ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler); ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx); // Push function for paths (no manipulation support for now) ADDAPI void ADDCALL sass_option_push_plugin_path (struct Sass_Options* options, const char* path); ADDAPI void ADDCALL sass_option_push_include_path (struct Sass_Options* options, const char* path); // Resolve a file via the given include paths in the sass option struct // find_file looks for the exact file name while find_include does a regular sass include ADDAPI char* ADDCALL sass_find_file (const char* path, struct Sass_Options* opt); ADDAPI char* ADDCALL sass_find_include (const char* path, struct Sass_Options* opt); // Resolve a file relative to last import or include paths in the sass option struct // find_file looks for the exact file name while find_include does a regular sass include ADDAPI char* ADDCALL sass_compiler_find_file (const char* path, struct Sass_Compiler* compiler); ADDAPI char* ADDCALL sass_compiler_find_include (const char* path, struct Sass_Compiler* compiler); #ifdef __cplusplus } // __cplusplus defined. #endif #endif golibsass-1.0.0/libsass_src/include/sass/functions.h000066400000000000000000000147501405214413600225710ustar00rootroot00000000000000#ifndef SASS_C_FUNCTIONS_H #define SASS_C_FUNCTIONS_H #include #include #include #ifdef __cplusplus extern "C" { #endif // Forward declaration struct Sass_Env; struct Sass_Callee; struct Sass_Import; struct Sass_Options; struct Sass_Compiler; struct Sass_Importer; struct Sass_Function; // Typedef helpers for callee lists typedef struct Sass_Env (*Sass_Env_Frame); // Typedef helpers for callee lists typedef struct Sass_Callee (*Sass_Callee_Entry); // Typedef helpers for import lists typedef struct Sass_Import (*Sass_Import_Entry); typedef struct Sass_Import* (*Sass_Import_List); // Typedef helpers for custom importer lists typedef struct Sass_Importer (*Sass_Importer_Entry); typedef struct Sass_Importer* (*Sass_Importer_List); // Typedef defining importer signature and return type typedef Sass_Import_List (*Sass_Importer_Fn) (const char* url, Sass_Importer_Entry cb, struct Sass_Compiler* compiler); // Typedef helpers for custom functions lists typedef struct Sass_Function (*Sass_Function_Entry); typedef struct Sass_Function* (*Sass_Function_List); // Typedef defining function signature and return type typedef union Sass_Value* (*Sass_Function_Fn) (const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Compiler* compiler); // Type of function calls enum Sass_Callee_Type { SASS_CALLEE_MIXIN, SASS_CALLEE_FUNCTION, SASS_CALLEE_C_FUNCTION, }; // Creator for sass custom importer return argument list ADDAPI Sass_Importer_List ADDCALL sass_make_importer_list (size_t length); ADDAPI Sass_Importer_Entry ADDCALL sass_importer_get_list_entry (Sass_Importer_List list, size_t idx); ADDAPI void ADDCALL sass_importer_set_list_entry (Sass_Importer_List list, size_t idx, Sass_Importer_Entry entry); ADDAPI void ADDCALL sass_delete_importer_list (Sass_Importer_List list); // Creators for custom importer callback (with some additional pointer) // The pointer is mostly used to store the callback into the actual binding ADDAPI Sass_Importer_Entry ADDCALL sass_make_importer (Sass_Importer_Fn importer, double priority, void* cookie); // Getters for import function descriptors ADDAPI Sass_Importer_Fn ADDCALL sass_importer_get_function (Sass_Importer_Entry cb); ADDAPI double ADDCALL sass_importer_get_priority (Sass_Importer_Entry cb); ADDAPI void* ADDCALL sass_importer_get_cookie (Sass_Importer_Entry cb); // Deallocator for associated memory ADDAPI void ADDCALL sass_delete_importer (Sass_Importer_Entry cb); // Creator for sass custom importer return argument list ADDAPI Sass_Import_List ADDCALL sass_make_import_list (size_t length); // Creator for a single import entry returned by the custom importer inside the list ADDAPI Sass_Import_Entry ADDCALL sass_make_import_entry (const char* path, char* source, char* srcmap); ADDAPI Sass_Import_Entry ADDCALL sass_make_import (const char* imp_path, const char* abs_base, char* source, char* srcmap); // set error message to abort import and to print out a message (path from existing object is used in output) ADDAPI Sass_Import_Entry ADDCALL sass_import_set_error(Sass_Import_Entry import, const char* message, size_t line, size_t col); // Setters to insert an entry into the import list (you may also use [] access directly) // Since we are dealing with pointers they should have a guaranteed and fixed size ADDAPI void ADDCALL sass_import_set_list_entry (Sass_Import_List list, size_t idx, Sass_Import_Entry entry); ADDAPI Sass_Import_Entry ADDCALL sass_import_get_list_entry (Sass_Import_List list, size_t idx); // Getters for callee entry ADDAPI const char* ADDCALL sass_callee_get_name (Sass_Callee_Entry); ADDAPI const char* ADDCALL sass_callee_get_path (Sass_Callee_Entry); ADDAPI size_t ADDCALL sass_callee_get_line (Sass_Callee_Entry); ADDAPI size_t ADDCALL sass_callee_get_column (Sass_Callee_Entry); ADDAPI enum Sass_Callee_Type ADDCALL sass_callee_get_type (Sass_Callee_Entry); ADDAPI Sass_Env_Frame ADDCALL sass_callee_get_env (Sass_Callee_Entry); // Getters and Setters for environments (lexical, local and global) ADDAPI union Sass_Value* ADDCALL sass_env_get_lexical (Sass_Env_Frame, const char*); ADDAPI void ADDCALL sass_env_set_lexical (Sass_Env_Frame, const char*, union Sass_Value*); ADDAPI union Sass_Value* ADDCALL sass_env_get_local (Sass_Env_Frame, const char*); ADDAPI void ADDCALL sass_env_set_local (Sass_Env_Frame, const char*, union Sass_Value*); ADDAPI union Sass_Value* ADDCALL sass_env_get_global (Sass_Env_Frame, const char*); ADDAPI void ADDCALL sass_env_set_global (Sass_Env_Frame, const char*, union Sass_Value*); // Getters for import entry ADDAPI const char* ADDCALL sass_import_get_imp_path (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_abs_path (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_source (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_srcmap (Sass_Import_Entry); // Explicit functions to take ownership of these items // The property on our struct will be reset to NULL ADDAPI char* ADDCALL sass_import_take_source (Sass_Import_Entry); ADDAPI char* ADDCALL sass_import_take_srcmap (Sass_Import_Entry); // Getters from import error entry ADDAPI size_t ADDCALL sass_import_get_error_line (Sass_Import_Entry); ADDAPI size_t ADDCALL sass_import_get_error_column (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_error_message (Sass_Import_Entry); // Deallocator for associated memory (incl. entries) ADDAPI void ADDCALL sass_delete_import_list (Sass_Import_List); // Just in case we have some stray import structs ADDAPI void ADDCALL sass_delete_import (Sass_Import_Entry); // Creators for sass function list and function descriptors ADDAPI Sass_Function_List ADDCALL sass_make_function_list (size_t length); ADDAPI Sass_Function_Entry ADDCALL sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie); ADDAPI void ADDCALL sass_delete_function (Sass_Function_Entry entry); ADDAPI void ADDCALL sass_delete_function_list (Sass_Function_List list); // Setters and getters for callbacks on function lists ADDAPI Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos); ADDAPI void ADDCALL sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb); // Getters for custom function descriptors ADDAPI const char* ADDCALL sass_function_get_signature (Sass_Function_Entry cb); ADDAPI Sass_Function_Fn ADDCALL sass_function_get_function (Sass_Function_Entry cb); ADDAPI void* ADDCALL sass_function_get_cookie (Sass_Function_Entry cb); #ifdef __cplusplus } // __cplusplus defined. #endif #endif golibsass-1.0.0/libsass_src/include/sass/values.h000066400000000000000000000145311405214413600220550ustar00rootroot00000000000000#ifndef SASS_C_VALUES_H #define SASS_C_VALUES_H #include #include #include #ifdef __cplusplus extern "C" { #endif // Forward declaration union Sass_Value; // Type for Sass values enum Sass_Tag { SASS_BOOLEAN, SASS_NUMBER, SASS_COLOR, SASS_STRING, SASS_LIST, SASS_MAP, SASS_NULL, SASS_ERROR, SASS_WARNING }; // Tags for denoting Sass list separators enum Sass_Separator { SASS_COMMA, SASS_SPACE, // only used internally to represent a hash map before evaluation // otherwise we would be too early to check for duplicate keys SASS_HASH }; // Value Operators enum Sass_OP { AND, OR, // logical connectives EQ, NEQ, GT, GTE, LT, LTE, // arithmetic relations ADD, SUB, MUL, DIV, MOD, // arithmetic functions NUM_OPS // so we know how big to make the op table }; // Creator functions for all value types ADDAPI union Sass_Value* ADDCALL sass_make_null (void); ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val); ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val); ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val); ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit); ADDAPI union Sass_Value* ADDCALL sass_make_color (double r, double g, double b, double a); ADDAPI union Sass_Value* ADDCALL sass_make_list (size_t len, enum Sass_Separator sep, bool is_bracketed); ADDAPI union Sass_Value* ADDCALL sass_make_map (size_t len); ADDAPI union Sass_Value* ADDCALL sass_make_error (const char* msg); ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg); // Generic destructor function for all types // Will release memory of all associated Sass_Values // Means we will delete recursively for lists and maps ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val); // Make a deep cloned copy of the given sass value ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val); // Execute an operation for two Sass_Values and return the result as a Sass_Value too ADDAPI union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b); // Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING) ADDAPI union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* a, bool compressed, int precision); // Return the sass tag for a generic sass value // Check is needed before accessing specific values! ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v); // Check value to be of a specific type // Can also be used before accessing properties! ADDAPI bool ADDCALL sass_value_is_null (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_number (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_string (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_boolean (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_color (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_list (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_map (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_error (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_warning (const union Sass_Value* v); // Getters and setters for Sass_Number ADDAPI double ADDCALL sass_number_get_value (const union Sass_Value* v); ADDAPI void ADDCALL sass_number_set_value (union Sass_Value* v, double value); ADDAPI const char* ADDCALL sass_number_get_unit (const union Sass_Value* v); ADDAPI void ADDCALL sass_number_set_unit (union Sass_Value* v, char* unit); // Getters and setters for Sass_String ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v); ADDAPI void ADDCALL sass_string_set_value (union Sass_Value* v, char* value); ADDAPI bool ADDCALL sass_string_is_quoted(const union Sass_Value* v); ADDAPI void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted); // Getters and setters for Sass_Boolean ADDAPI bool ADDCALL sass_boolean_get_value (const union Sass_Value* v); ADDAPI void ADDCALL sass_boolean_set_value (union Sass_Value* v, bool value); // Getters and setters for Sass_Color ADDAPI double ADDCALL sass_color_get_r (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_r (union Sass_Value* v, double r); ADDAPI double ADDCALL sass_color_get_g (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_g (union Sass_Value* v, double g); ADDAPI double ADDCALL sass_color_get_b (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_b (union Sass_Value* v, double b); ADDAPI double ADDCALL sass_color_get_a (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_a (union Sass_Value* v, double a); // Getter for the number of items in list ADDAPI size_t ADDCALL sass_list_get_length (const union Sass_Value* v); // Getters and setters for Sass_List ADDAPI enum Sass_Separator ADDCALL sass_list_get_separator (const union Sass_Value* v); ADDAPI void ADDCALL sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value); ADDAPI bool ADDCALL sass_list_get_is_bracketed (const union Sass_Value* v); ADDAPI void ADDCALL sass_list_set_is_bracketed (union Sass_Value* v, bool value); // Getters and setters for Sass_List values ADDAPI union Sass_Value* ADDCALL sass_list_get_value (const union Sass_Value* v, size_t i); ADDAPI void ADDCALL sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value); // Getter for the number of items in map ADDAPI size_t ADDCALL sass_map_get_length (const union Sass_Value* v); // Getters and setters for Sass_Map keys and values ADDAPI union Sass_Value* ADDCALL sass_map_get_key (const union Sass_Value* v, size_t i); ADDAPI void ADDCALL sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*); ADDAPI union Sass_Value* ADDCALL sass_map_get_value (const union Sass_Value* v, size_t i); ADDAPI void ADDCALL sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*); // Getters and setters for Sass_Error ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v); ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg); // Getters and setters for Sass_Warning ADDAPI char* ADDCALL sass_warning_get_message (const union Sass_Value* v); ADDAPI void ADDCALL sass_warning_set_message (union Sass_Value* v, char* msg); #ifdef __cplusplus } // __cplusplus defined. #endif #endif golibsass-1.0.0/libsass_src/include/sass/version.h000066400000000000000000000003051405214413600222350ustar00rootroot00000000000000#ifndef SASS_VERSION_H #define SASS_VERSION_H #ifndef LIBSASS_VERSION #define LIBSASS_VERSION "[NA]" #endif #ifndef LIBSASS_LANGUAGE_VERSION #define LIBSASS_LANGUAGE_VERSION "3.5" #endif #endif golibsass-1.0.0/libsass_src/include/sass/version.h.in000066400000000000000000000003221405214413600226410ustar00rootroot00000000000000#ifndef SASS_VERSION_H #define SASS_VERSION_H #ifndef LIBSASS_VERSION #define LIBSASS_VERSION "@PACKAGE_VERSION@" #endif #ifndef LIBSASS_LANGUAGE_VERSION #define LIBSASS_LANGUAGE_VERSION "3.5" #endif #endif golibsass-1.0.0/libsass_src/include/sass2scss.h000066400000000000000000000047261405214413600215410ustar00rootroot00000000000000/** * sass2scss * Licensed under the MIT License * Copyright (c) Marcel Greter */ #ifndef SASS2SCSS_H #define SASS2SCSS_H #ifdef _WIN32 /* You should define ADD_EXPORTS *only* when building the DLL. */ #ifdef ADD_EXPORTS #define ADDAPI __declspec(dllexport) #define ADDCALL __cdecl #else #define ADDAPI #define ADDCALL #endif #else /* _WIN32 not defined. */ /* Define with no value on non-Windows OSes. */ #define ADDAPI #define ADDCALL #endif #ifdef __cplusplus #include #include #include #include #include #ifndef SASS2SCSS_VERSION // Hardcode once the file is copied from // https://github.com/mgreter/sass2scss #define SASS2SCSS_VERSION "1.1.1" #endif // add namespace for c++ namespace Sass { // pretty print options const int SASS2SCSS_PRETTIFY_0 = 0; const int SASS2SCSS_PRETTIFY_1 = 1; const int SASS2SCSS_PRETTIFY_2 = 2; const int SASS2SCSS_PRETTIFY_3 = 3; // remove one-line comment const int SASS2SCSS_KEEP_COMMENT = 32; // remove multi-line comments const int SASS2SCSS_STRIP_COMMENT = 64; // convert one-line to multi-line const int SASS2SCSS_CONVERT_COMMENT = 128; // String for finding something interesting const std::string SASS2SCSS_FIND_WHITESPACE = " \t\n\v\f\r"; // converter struct // holding all states struct converter { // bit options int options; // is selector bool selector; // concat lists bool comma; // has property bool property; // has semicolon bool semicolon; // comment context std::string comment; // flag end of file bool end_of_file; // whitespace buffer std::string whitespace; // context/block stack std::stack indents; }; // function only available in c++ code char* sass2scss (const std::string& sass, const int options); } // EO namespace // declare for c extern "C" { #endif // prettyfy print options #define SASS2SCSS_PRETTIFY_0 0 #define SASS2SCSS_PRETTIFY_1 1 #define SASS2SCSS_PRETTIFY_2 2 #define SASS2SCSS_PRETTIFY_3 3 // keep one-line comments #define SASS2SCSS_KEEP_COMMENT 32 // remove multi-line comments #define SASS2SCSS_STRIP_COMMENT 64 // convert one-line to multi-line #define SASS2SCSS_CONVERT_COMMENT 128 // available to c and c++ code ADDAPI char* ADDCALL sass2scss (const char* sass, const int options); // Get compiled sass2scss version ADDAPI const char* ADDCALL sass2scss_version(void); #ifdef __cplusplus } // __cplusplus defined. #endif #endifgolibsass-1.0.0/libsass_src/m4/000077500000000000000000000000001405214413600163255ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/m4/.gitkeep000066400000000000000000000000001405214413600177440ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/m4/m4-ax_cxx_compile_stdcxx_11.m4000066400000000000000000000130001405214413600237770ustar00rootroot00000000000000# ============================================================================ # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html # ============================================================================ # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++11 # standard; if necessary, add switches to CXXFLAGS to enable support. # # The first argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The second argument, if specified 'mandatory' or if left unspecified, # indicates that baseline C++11 support is required and that the macro # should error out if no mode with that support is found. If specified # 'optional', then configuration proceeds regardless, after defining # HAVE_CXX11 if and only if a supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 11 m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; struct Base { virtual void f() {} }; struct Child : public Base { virtual void f() override {} }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = static_cast(c); auto d = a; auto l = [](){}; // Prevent Clang error: unused variable 'l' [-Werror,-Wunused-variable] struct use_l { use_l() { l(); } }; // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } ]]) AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl m4_if([$1], [], [], [$1], [ext], [], [$1], [noext], [], [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], [$2], [optional], [ax_cxx_compile_cxx11_required=false], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) AC_LANG_PUSH([C++])dnl ac_success=no AC_CACHE_CHECK(whether $CXX supports C++11 features by default, ax_cv_cxx_compile_cxx11, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [ax_cv_cxx_compile_cxx11=yes], [ax_cv_cxx_compile_cxx11=no])]) if test x$ax_cv_cxx_compile_cxx11 = xyes; then ac_success=yes fi m4_if([$1], [noext], [], [dnl if test x$ac_success = xno; then for switch in -std=gnu++11 -std=gnu++0x; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, $cachevar, [ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [eval $cachevar=yes], [eval $cachevar=no]) CXXFLAGS="$ac_save_CXXFLAGS"]) if eval test x\$$cachevar = xyes; then CXXFLAGS="$CXXFLAGS $switch" ac_success=yes break fi done fi]) m4_if([$1], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf for switch in -std=c++11 -std=c++0x +std=c++11; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, $cachevar, [ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [eval $cachevar=yes], [eval $cachevar=no]) CXXFLAGS="$ac_save_CXXFLAGS"]) if eval test x\$$cachevar = xyes; then CXXFLAGS="$CXXFLAGS $switch" ac_success=yes break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx11_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) fi else if test x$ac_success = xno; then HAVE_CXX11=0 AC_MSG_NOTICE([No compiler with C++11 support was found]) else HAVE_CXX11=1 AC_DEFINE(HAVE_CXX11,1, [define if the compiler supports basic C++11 syntax]) fi AC_SUBST(HAVE_CXX11) fi ]) golibsass-1.0.0/libsass_src/res/000077500000000000000000000000001405214413600165765ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/res/resource.rc000066400000000000000000000016311405214413600207540ustar00rootroot00000000000000#include // DLL version information. VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG | VS_FF_PRERELEASE #else FILEFLAGS 0 #endif FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080904b0" BEGIN VALUE "CompanyName", "Sass Open Source Foundation" VALUE "FileDescription", "A C/C++ implementation of a Sass compiler" VALUE "FileVersion", "1.0.0.0" VALUE "InternalName", "libsass" VALUE "LegalCopyright", "\251 2017 libsass.org" VALUE "OriginalFilename", "libsass.dll" VALUE "ProductName", "LibSass Library" VALUE "ProductVersion", "1.0.0.0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x809, 1200 END END golibsass-1.0.0/libsass_src/script/000077500000000000000000000000001405214413600173115ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/script/bootstrap000077500000000000000000000007051405214413600212560ustar00rootroot00000000000000#!/bin/bash script/branding : ${LIBSASS_SPEC_PATH:="libsass-spec"} : ${SASS_SPEC_PATH:="sass-spec"} : ${SASS_SASSC_PATH:="sassc" } if [ ! -d $LIBSASS_SPEC_PATH ]; then git clone https://github.com/mgreter/libsass-spec.git $LIBSASS_SPEC_PATH fi if [ ! -d $SASS_SPEC_PATH ]; then git clone https://github.com/sass/sass-spec.git $SASS_SPEC_PATH fi if [ ! -d $SASS_SASSC_PATH ]; then git clone https://github.com/sass/sassc.git $SASS_SASSC_PATH fi golibsass-1.0.0/libsass_src/script/branding000077500000000000000000000005371405214413600210300ustar00rootroot00000000000000#! /bin/bash echo " " echo " _ ___ ____ ____ _ ____ ____ " echo "| | |_ _| __ ) ___| / \ / ___/ ___| " echo "| | | || _ \___ \ / _ \ \___ \___ \ " echo "| |___ | || |_) |__) / ___ \ ___) |__) |" echo "|_____|___|____/____/_/ \_\____/____/ " echo " " golibsass-1.0.0/libsass_src/script/ci-build-libsass000077500000000000000000000077601405214413600223770ustar00rootroot00000000000000#!/bin/bash set -e script/bootstrap echo Building LibSass $MAKE_OPTS # export this path right here (was in script/spec before) export SASS_LIBSASS_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../ && pwd )" # use some defaults if not running under travis ci if [ "x$CONTINUOUS_INTEGRATION" == "x" ]; then export CONTINUOUS_INTEGRATION=true; fi if [ "x$TRAVIS_BUILD_DIR" == "x" ]; then export TRAVIS_BUILD_DIR=$(pwd); fi if [ "x$SASS_SASSC_PATH" == "x" ]; then export SASS_SASSC_PATH=$(pwd)/sassc; fi if [ "x$SASS_SPEC_PATH" == "x" ]; then export SASS_SPEC_PATH=$(pwd)/sass-spec; fi # try to get the os name from uname (and filter via perl - probably not the most portable way?) if [ "x$TRAVIS_OS_NAME" == "x" ]; then export TRAVIS_OS_NAME=`uname -s | perl -ne 'print lc \$1 if\(/^([a-zA-Z]+)/'\)`; fi if [ "x$COVERAGE" == "xyes" ]; then COVERAGE_OPT="--enable-coverage" export EXTRA_CFLAGS="-fprofile-arcs -ftest-coverage" export EXTRA_CXXFLAGS="-fprofile-arcs -ftest-coverage" if [ "$TRAVIS_OS_NAME" == "osx" ]; then # osx doesn't seem to know gcov lib? export EXTRA_LDFLAGS="--coverage" else export EXTRA_LDFLAGS="-lgcov --coverage" fi else COVERAGE_OPT="--disable-coverage" fi if [ "x$BUILD" == "xstatic" ]; then SHARED_OPT="--disable-shared --enable-static" MAKE_TARGET="static" else # Makefile of sassc wants to link to static SHARED_OPT="--enable-shared --enable-static" MAKE_TARGET="shared" fi if [ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then MAKE_OPTS="$MAKE_OPTS -j1 V=1" else MAKE_OPTS="$MAKE_OPTS -j5 V=1" fi if [ "x$PREFIX" == "x" ]; then if [ "x$TRAVIS_BUILD_DIR" == "x" ]; then PREFIX=$SASS_LIBSASS_PATH/build else PREFIX=$TRAVIS_BUILD_DIR/build fi fi # enable address sanitation # https://en.wikipedia.org/wiki/AddressSanitizer if [ "x$CC" == "xclang" ]; then if [ "x$COVERAGE" != "xyes" ]; then if [ "$TRAVIS_OS_NAME" == "linux" ]; then export EXTRA_CFLAGS="$EXTRA_CFLAGS -fsanitize=address" export EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS -fsanitize=address" export EXTRA_LDFLAGS="$EXTRA_LDFLAGS -fsanitize=address" fi fi fi echo SASS_LIBSASS_PATH: $SASS_LIBSASS_PATH echo TRAVIS_BUILD_DIR: $TRAVIS_BUILD_DIR echo SASS_SASSC_PATH: $SASS_SASSC_PATH echo SASS_SPEC_PATH: $SASS_SPEC_PATH echo INSTALL_LOCATION: $PREFIX if [ "x$AUTOTOOLS" == "xyes" ]; then echo -en 'travis_fold:start:configure\r' autoreconf --force --install ./configure --enable-tests $COVERAGE_OPT \ --disable-silent-rules \ --with-sassc-dir=$SASS_SASSC_PATH \ --with-sass-spec-dir=$SASS_SPEC_PATH \ --prefix=$PREFIX \ ${SHARED_OPT} echo -en 'travis_fold:end:configure\r' make $MAKE_OPTS clean else make $MAKE_OPTS clean # Run C++ unit tests make $MAKE_OPTS -C test clean make $MAKE_OPTS -C test test fi # install to prefix directory PREFIX="$PREFIX" make $MAKE_OPTS install ls -la $PREFIX/* echo successfully compiled libsass echo AUTOTOOLS=$AUTOTOOLS COVERAGE=$COVERAGE BUILD=$BUILD if [ "$CONTINUOUS_INTEGRATION" == "true" ] && [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "x$TRAVIS_PULL_REQUEST" != "x" ] && ([ "$TRAVIS_OS_NAME" == "linux" ] || [ "$TRAVIS_OS_NAME" == "osx" ] || [ "$TRAVIS_OS_NAME" == "cygwin" ]); then echo "Fetching PR $TRAVIS_PULL_REQUEST" JSON=$(curl -L -sS https://api.github.com/repos/sass/libsass/pulls/$TRAVIS_PULL_REQUEST) if [[ $JSON =~ "API rate limit exceeded" ]]; then echo "Travis rate limit on github exceeded" echo "Retrying via 'special purpose proxy'" JSON=$(curl -L -sS https://github-api-reverse-proxy.herokuapp.com/repos/sass/libsass/pulls/$TRAVIS_PULL_REQUEST) fi RE_SPEC_PR="sass\/sass-spec(#|\/pull\/)([0-9]+)" if [[ $JSON =~ $RE_SPEC_PR ]]; then SPEC_PR="${BASH_REMATCH[2]}" echo "Fetching Sass Spec PR $SPEC_PR" git -C sass-spec fetch -u origin pull/$SPEC_PR/head:ci-spec-pr-$SPEC_PR git -C sass-spec checkout --force ci-spec-pr-$SPEC_PR make $MAKE_OPTS test_probe else make $MAKE_OPTS test_probe fi else make $MAKE_OPTS test_probe fi golibsass-1.0.0/libsass_src/script/ci-build-plugin000077500000000000000000000042721405214413600222300ustar00rootroot00000000000000#!/bin/bash PLUGIN=$1 RUBY_BIN=ruby SASS_SPEC_PATH=sass-spec SASSC_BIN=sassc/bin/sassc SASS_SPEC_SPEC_DIR=plugins/libsass-${PLUGIN}/test if [ -e ./tester ] ; then SASSC_BIN=./tester fi if [ -d ./build/lib ] ; then cp -a build/lib lib fi if [ "x$1" == "x" ] ; then echo "No plugin name given" exit 1 fi if [ "x$COVERAGE" == "0" ] ; then unset COVERAGE fi export EXTRA_CFLAGS="" export EXTRA_CXXFLAGS="" if [ "$TRAVIS_OS_NAME" == "osx" ]; then # osx doesn't seem to know gcov lib? export EXTRA_LDFLAGS="--coverage" else export EXTRA_LDFLAGS="-lgcov --coverage" fi mkdir -p plugins if [ ! -d plugins/libsass-${PLUGIN} ] ; then if [ "$PLUGIN" == "tests" ]; then git clone https://github.com/mgreter/libsass-${PLUGIN} plugins/libsass-${PLUGIN} --branch master else git clone https://github.com/mgreter/libsass-${PLUGIN} plugins/libsass-${PLUGIN} fi fi if [ ! -d plugins/libsass-${PLUGIN}/build ] ; then mkdir plugins/libsass-${PLUGIN}/build fi RETVAL=$?; if [ "$RETVAL" != "0" ]; then exit $RETVAL; fi cd plugins/libsass-${PLUGIN}/build cmake -G "Unix Makefiles" -D LIBSASS_DIR="../../.." .. RETVAL=$?; if [ "$RETVAL" != "0" ]; then exit $RETVAL; fi make VERBOSE=1 -j2 RETVAL=$?; if [ "$RETVAL" != "0" ]; then exit $RETVAL; fi cd ../../.. # glob only works on paths relative to imports if [ "x$PLUGIN" == "xglob" ]; then ${SASSC_BIN} --precision 5 --plugin-path plugins/libsass-${PLUGIN}/build ${SASS_SPEC_SPEC_DIR}/basic/input.scss > ${SASS_SPEC_SPEC_DIR}/basic/result.css ${SASSC_BIN} --precision 5 --plugin-path plugins/libsass-${PLUGIN}/build ${SASS_SPEC_SPEC_DIR}/basic/input.scss --sourcemap > /dev/null else cat ${SASS_SPEC_SPEC_DIR}/basic/input.scss | ${SASSC_BIN} --precision 5 --plugin-path plugins/libsass-${PLUGIN}/build -I ${SASS_SPEC_SPEC_DIR}/basic > ${SASS_SPEC_SPEC_DIR}/basic/result.css cat ${SASS_SPEC_SPEC_DIR}/basic/input.scss | ${SASSC_BIN} --precision 5 --plugin-path plugins/libsass-${PLUGIN}/build -I ${SASS_SPEC_SPEC_DIR}/basic --sourcemap > /dev/null fi RETVAL=$?; if [ "$RETVAL" != "0" ]; then exit $RETVAL; fi diff ${SASS_SPEC_SPEC_DIR}/basic/expected_output.css ${SASS_SPEC_SPEC_DIR}/basic/result.css RETVAL=$?; if [ "$RETVAL" != "0" ]; then exit $RETVAL; fi golibsass-1.0.0/libsass_src/script/ci-install-compiler000077500000000000000000000002351405214413600231060ustar00rootroot00000000000000#!/bin/bash sudo gem install minitest sudo gem install minitap sudo gem install rspec sudo gem install hrx # sudo pip2 install --user 'requests[security]' golibsass-1.0.0/libsass_src/script/ci-install-deps000077500000000000000000000006341405214413600222320ustar00rootroot00000000000000#!/bin/bash if [ "x$COVERAGE" == "xyes" ]; then pip2 install --user gcovr pip2 install --user cpp-coveralls else echo "no dependencies to install" fi if [ "x$AUTOTOOLS" == "xyes" ]; then AUTOTOOLS=yes if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo add-apt-repository -y ppa:rbose-debianizer/automake &> /dev/null sudo apt-get -qq update sudo apt-get -qq install automake fi fi exit 0 golibsass-1.0.0/libsass_src/script/ci-report-coverage000077500000000000000000000026711405214413600227420ustar00rootroot00000000000000#!/bin/bash if [ "x$COVERAGE" = "xyes" ]; then # find / -name "gcovr" # find / -name "coveralls" # this is only needed for mac os x builds! PATH=$PATH:/Users/travis/Library/Python/2.7/bin/ # exclude some directories from profiling (.libs is from autotools) export EXCLUDE_COVERAGE="--exclude plugins --exclude sassc/sassc.c --exclude src/sass-spec --exclude src/.libs --exclude src/debug.hpp --exclude src/json.cpp --exclude src/json.hpp --exclude src/cencode.c --exclude src/b64 --exclude src/utf8 --exclude src/utf8_string.hpp --exclude src/utf8.h --exclude src/utf8_string.cpp --exclude src/sass2scss.h --exclude src/sass2scss.cpp --exclude src/test --exclude src/posix --exclude src/debugger.hpp" # debug used gcov version # option not available on mac if [ "$TRAVIS_OS_NAME" != "osx" ]; then gcov -v fi # create summarized report gcovr -r . # submit report to coveralls.io coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' else echo "skip coverage reporting" fi golibsass-1.0.0/libsass_src/script/spec000077500000000000000000000000721405214413600201700ustar00rootroot00000000000000#!/bin/bash script/bootstrap make $MAKE_OPTS test_build golibsass-1.0.0/libsass_src/script/tap-driver000077500000000000000000000461011405214413600213160ustar00rootroot00000000000000#!/usr/bin/env sh # Copyright (C) 2011-2013 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . scriptversion=2011-12-27.17; # UTC # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u me=tap-driver.sh fatal () { echo "$me: fatal: $*" >&2 exit 1 } usage_error () { echo "$me: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat < # trap : 1 3 2 13 15 if test $merge -gt 0; then exec 2>&1 else exec 2>&3 fi "$@" echo $? ) | LC_ALL=C ${AM_TAP_AWK-awk} \ -v me="$me" \ -v test_script_name="$test_name" \ -v log_file="$log_file" \ -v trs_file="$trs_file" \ -v expect_failure="$expect_failure" \ -v merge="$merge" \ -v ignore_exit="$ignore_exit" \ -v comments="$comments" \ -v diag_string="$diag_string" \ ' # FIXME: the usages of "cat >&3" below could be optimized when using # FIXME: GNU awk, and/on on systems that supports /dev/fd/. # Implementation note: in what follows, `result_obj` will be an # associative array that (partly) simulates a TAP result object # from the `TAP::Parser` perl module. ## ----------- ## ## FUNCTIONS ## ## ----------- ## function fatal(msg) { print me ": " msg | "cat >&2" exit 1 } function abort(where) { fatal("internal error " where) } # Convert a boolean to a "yes"/"no" string. function yn(bool) { return bool ? "yes" : "no"; } function add_test_result(result) { if (!test_results_index) test_results_index = 0 test_results_list[test_results_index] = result test_results_index += 1 test_results_seen[result] = 1; } # Whether the test script should be re-run by "make recheck". function must_recheck() { for (k in test_results_seen) if (k != "XFAIL" && k != "PASS" && k != "SKIP") return 1 return 0 } # Whether the content of the log file associated to this test should # be copied into the "global" test-suite.log. function copy_in_global_log() { for (k in test_results_seen) if (k != "PASS") return 1 return 0 } # FIXME: this can certainly be improved ... function get_global_test_result() { if ("ERROR" in test_results_seen) return "ERROR" if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) return "FAIL" all_skipped = 1 for (k in test_results_seen) if (k != "SKIP") all_skipped = 0 if (all_skipped) return "SKIP" return "PASS"; } function stringify_result_obj(result_obj) { if (result_obj["is_unplanned"] || result_obj["number"] != testno) return "ERROR" if (plan_seen == LATE_PLAN) return "ERROR" if (result_obj["directive"] == "TODO") return result_obj["is_ok"] ? "XPASS" : "XFAIL" if (result_obj["directive"] == "SKIP") return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; if (length(result_obj["directive"])) abort("in function stringify_result_obj()") return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL } function decorate_result(result) { color_name = color_for_result[result] if (color_name) return color_map[color_name] "" result "" color_map["std"] # If we are not using colorized output, or if we do not know how # to colorize the given result, we should return it unchanged. return result } function report(result, details) { if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) { msg = ": " test_script_name add_test_result(result) } else if (result == "#") { msg = " " test_script_name ":" } else { abort("in function report()") } if (length(details)) msg = msg " " details # Output on console might be colorized. print decorate_result(result) msg # Log the result in the log file too, to help debugging (this is # especially true when said result is a TAP error or "Bail out!"). print result msg | "cat >&3"; } function testsuite_error(error_message) { report("ERROR", "- " error_message) } function handle_tap_result() { details = result_obj["number"]; if (length(result_obj["description"])) details = details " " result_obj["description"] if (plan_seen == LATE_PLAN) { details = details " # AFTER LATE PLAN"; } else if (result_obj["is_unplanned"]) { details = details " # UNPLANNED"; } else if (result_obj["number"] != testno) { details = sprintf("%s # OUT-OF-ORDER (expecting %d)", details, testno); } else if (result_obj["directive"]) { details = details " # " result_obj["directive"]; if (length(result_obj["explanation"])) details = details " " result_obj["explanation"] } report(stringify_result_obj(result_obj), details) } # `skip_reason` should be empty whenever planned > 0. function handle_tap_plan(planned, skip_reason) { planned += 0 # Avoid getting confused if, say, `planned` is "00" if (length(skip_reason) && planned > 0) abort("in function handle_tap_plan()") if (plan_seen) { # Error, only one plan per stream is acceptable. testsuite_error("multiple test plans") return; } planned_tests = planned # The TAP plan can come before or after *all* the TAP results; we speak # respectively of an "early" or a "late" plan. If we see the plan line # after at least one TAP result has been seen, assume we have a late # plan; in this case, any further test result seen after the plan will # be flagged as an error. plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) # If testno > 0, we have an error ("too many tests run") that will be # automatically dealt with later, so do not worry about it here. If # $plan_seen is true, we have an error due to a repeated plan, and that # has already been dealt with above. Otherwise, we have a valid "plan # with SKIP" specification, and should report it as a particular kind # of SKIP result. if (planned == 0 && testno == 0) { if (length(skip_reason)) skip_reason = "- " skip_reason; report("SKIP", skip_reason); } } function extract_tap_comment(line) { if (index(line, diag_string) == 1) { # Strip leading `diag_string` from `line`. line = substr(line, length(diag_string) + 1) # And strip any leading and trailing whitespace left. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # Return what is left (if any). return line; } return ""; } # When this function is called, we know that line is a TAP result line, # so that it matches the (perl) RE "^(not )?ok\b". function setup_result_obj(line) { # Get the result, and remove it from the line. result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) sub("^(not )?ok[ \t]*", "", line) # If the result has an explicit number, get it and strip it; otherwise, # automatically assing the next progresive number to it. if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) { match(line, "^[0-9]+") # The final `+ 0` is to normalize numbers with leading zeros. result_obj["number"] = substr(line, 1, RLENGTH) + 0 line = substr(line, RLENGTH + 1) } else { result_obj["number"] = testno } if (plan_seen == LATE_PLAN) # No further test results are acceptable after a "late" TAP plan # has been seen. result_obj["is_unplanned"] = 1 else if (plan_seen && testno > planned_tests) result_obj["is_unplanned"] = 1 else result_obj["is_unplanned"] = 0 # Strip trailing and leading whitespace. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # This will have to be corrected if we have a "TODO"/"SKIP" directive. result_obj["description"] = line result_obj["directive"] = "" result_obj["explanation"] = "" if (index(line, "#") == 0) return # No possible directive, nothing more to do. # Directives are case-insensitive. rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" # See whether we have the directive, and if yes, where. pos = match(line, rx "$") if (!pos) pos = match(line, rx "[^a-zA-Z0-9_]") # If there was no TAP directive, we have nothing more to do. if (!pos) return # Let`s now see if the TAP directive has been escaped. For example: # escaped: ok \# SKIP # not escaped: ok \\# SKIP # escaped: ok \\\\\# SKIP # not escaped: ok \ # SKIP if (substr(line, pos, 1) == "#") { bslash_count = 0 for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) bslash_count += 1 if (bslash_count % 2) return # Directive was escaped. } # Strip the directive and its explanation (if any) from the test # description. result_obj["description"] = substr(line, 1, pos - 1) # Now remove the test description from the line, that has been dealt # with already. line = substr(line, pos) # Strip the directive, and save its value (normalized to upper case). sub("^[ \t]*#[ \t]*", "", line) result_obj["directive"] = toupper(substr(line, 1, 4)) line = substr(line, 5) # Now get the explanation for the directive (if any), with leading # and trailing whitespace removed. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) result_obj["explanation"] = line } function get_test_exit_message(status) { if (status == 0) return "" if (status !~ /^[1-9][0-9]*$/) abort("getting exit status") if (status < 127) exit_details = "" else if (status == 127) exit_details = " (command not found?)" else if (status >= 128 && status <= 255) exit_details = sprintf(" (terminated by signal %d?)", status - 128) else if (status > 256 && status <= 384) # We used to report an "abnormal termination" here, but some Korn # shells, when a child process die due to signal number n, can leave # in $? an exit status of 256+n instead of the more standard 128+n. # Apparently, both behaviours are allowed by POSIX (2008), so be # prepared to handle them both. See also Austing Group report ID # 0000051 exit_details = sprintf(" (terminated by signal %d?)", status - 256) else # Never seen in practice. exit_details = " (abnormal termination)" return sprintf("exited with status %d%s", status, exit_details) } function write_test_results() { print ":global-test-result: " get_global_test_result() > trs_file print ":recheck: " yn(must_recheck()) > trs_file print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file for (i = 0; i < test_results_index; i += 1) print ":test-result: " test_results_list[i] > trs_file close(trs_file); } BEGIN { ## ------- ## ## SETUP ## ## ------- ## '"$init_colors"' # Properly initialized once the TAP plan is seen. planned_tests = 0 COOKED_PASS = expect_failure ? "XPASS": "PASS"; COOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; # Enumeration-like constants to remember which kind of plan (if any) # has been seen. It is important that NO_PLAN evaluates "false" as # a boolean. NO_PLAN = 0 EARLY_PLAN = 1 LATE_PLAN = 2 testno = 0 # Number of test results seen so far. bailed_out = 0 # Whether a "Bail out!" directive has been seen. # Whether the TAP plan has been seen or not, and if yes, which kind # it is ("early" is seen before any test result, "late" otherwise). plan_seen = NO_PLAN ## --------- ## ## PARSING ## ## --------- ## is_first_read = 1 while (1) { # Involutions required so that we are able to read the exit status # from the last input line. st = getline if (st < 0) # I/O error. fatal("I/O error while reading from input stream") else if (st == 0) # End-of-input { if (is_first_read) abort("in input loop: only one input line") break } if (is_first_read) { is_first_read = 0 nextline = $0 continue } else { curline = nextline nextline = $0 $0 = curline } # Copy any input line verbatim into the log file. print | "cat >&3" # Parsing of TAP input should stop after a "Bail out!" directive. if (bailed_out) continue # TAP test result. if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) { testno += 1 setup_result_obj($0) handle_tap_result() } # TAP plan (normal or "SKIP" without explanation). else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) { # The next two lines will put the number of planned tests in $0. sub("^1\\.\\.", "") sub("[^0-9]*$", "") handle_tap_plan($0, "") continue } # TAP "SKIP" plan, with an explanation. else if ($0 ~ /^1\.\.0+[ \t]*#/) { # The next lines will put the skip explanation in $0, stripping # any leading and trailing whitespace. This is a little more # tricky in truth, since we want to also strip a potential leading # "SKIP" string from the message. sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") sub("[ \t]*$", ""); handle_tap_plan(0, $0) } # "Bail out!" magic. # Older versions of prove and TAP::Harness (e.g., 3.17) did not # recognize a "Bail out!" directive when preceded by leading # whitespace, but more modern versions (e.g., 3.23) do. So we # emulate the latter, "more modern" behaviour. else if ($0 ~ /^[ \t]*Bail out!/) { bailed_out = 1 # Get the bailout message (if any), with leading and trailing # whitespace stripped. The message remains stored in `$0`. sub("^[ \t]*Bail out![ \t]*", ""); sub("[ \t]*$", ""); # Format the error message for the bailout_message = "Bail out!" if (length($0)) bailout_message = bailout_message " " $0 testsuite_error(bailout_message) } # Maybe we have too look for dianogtic comments too. else if (comments != 0) { comment = extract_tap_comment($0); if (length(comment)) report("#", comment); } } ## -------- ## ## FINISH ## ## -------- ## # A "Bail out!" directive should cause us to ignore any following TAP # error, as well as a non-zero exit status from the TAP producer. if (!bailed_out) { if (!plan_seen) { testsuite_error("missing test plan") } else if (planned_tests != testno) { bad_amount = testno > planned_tests ? "many" : "few" testsuite_error(sprintf("too %s tests run (expected %d, got %d)", bad_amount, planned_tests, testno)) } if (!ignore_exit) { # Fetch exit status from the last line. exit_message = get_test_exit_message(nextline) if (exit_message) testsuite_error(exit_message) } } write_test_results() exit 0 } # End of "BEGIN" block. ' # TODO: document that we consume the file descriptor 3 :-( } 3>"$log_file" test $? -eq 0 || fatal "I/O or internal error" # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: golibsass-1.0.0/libsass_src/script/tap-runner000077500000000000000000000000531405214413600213300ustar00rootroot00000000000000$@ $TEST_FLAGS --tap --silent | tapout tap golibsass-1.0.0/libsass_src/script/test-leaks.pl000077500000000000000000000064761405214413600217420ustar00rootroot00000000000000#!/usr/bin/perl ############################################################ # this perl script is meant for developers only! # it will run all spec-tests (without verifying the # results) via valgrind to detect possible leaks. # expect that it takes 1h or more to finish! ############################################################ # Prerequisite install: `cpan Parallel::Runner` # You may also need to install `cpan File::Find` # You may also need to install `cpan IPC::Run3` ############################################################ # usage: `perl test-leaks.pl [threads]` # example: `time perl test-leaks.pl 4` ############################################################ # leaks will be reported in "mem-leaks.log" ############################################################ use strict; use warnings; ############################################################ # configurations (you may adjust) ############################################################ # number of threads to use my $threads = $ARGV[0] || 8; # the github repositories to checkout # if you need other branch, clone manually! my $sassc = "https://www.github.com/sass/sassc"; my $specs = "https://www.github.com/sass/sass-spec"; ############################################################ # load modules ############################################################ use IPC::Run3; use IO::Handle; use Fcntl qw(:flock); use File::Find::Rule; use Parallel::Runner; use List::Util qw(shuffle); ############################################################ # check prerequisites ############################################################ unless (-d "../sassc") { warn "sassc folder not found\n"; warn "trying to checkout via git\n"; system("git", "clone", $sassc, "../sassc"); die "git command did not exit gracefully" if $?; } unless (-d "../sass-spec") { warn "sass-spec folder not found\n"; warn "trying to checkout via git\n"; system("git", "clone", $specs, "../sass-spec"); die "git command did not exit gracefully" if $?; } unless (-f "../sassc/bin/sassc") { warn "sassc executable not found\n"; warn "trying to compile via make\n"; system("make", "-C", "../sassc", "-j", $threads); die "make command did not exit gracefully" if $?; } ############################################################ # main runner code ############################################################ my $root = "../sass-spec/spec"; my @files = File::Find::Rule->file() ->name('input.scss')->in($root); open(my $leaks, ">", "mem-leaks.log"); die "Cannot open log" unless $leaks; my $runner = Parallel::Runner->new($threads); die "Cannot start runner" unless $runner; print "##########################\n"; print "Testing $#files spec files\n"; print "##########################\n"; foreach my $file (shuffle @files) { $runner->run(sub { $| = 1; select STDOUT; my $cmd = sprintf('../sassc/bin/sassc %s', $file); my $check = sprintf('valgrind --leak-check=yes %s', $cmd); run3($check, undef, \ my $out, \ my $err); if ($err =~ m/in use at exit: 0 bytes in 0 blocks/) { print "."; # print success indicator } else { print "F"; # print error indicator flock($leaks, LOCK_EX) or die "Cannot lock log"; $leaks->printflush("#" x 80, "\n", $err, "\n"); flock($leaks, LOCK_UN) or die "Cannot unlock log"; } }); } $runner->finish; golibsass-1.0.0/libsass_src/src/000077500000000000000000000000001405214413600165745ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/src/GNUmakefile.am000066400000000000000000000022711405214413600212440ustar00rootroot00000000000000ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 -I script AM_COPT = -Wall -O2 AM_COVLDFLAGS = if ENABLE_COVERAGE AM_COPT = -O0 --coverage AM_COVLDFLAGS += -lgcov endif AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = $(AM_COPT) AM_CXXFLAGS = $(AM_COPT) AM_LDFLAGS = $(AM_COPT) $(AM_COVLDFLAGS) AM_CXXFLAGS += -std=c++11 EXTRA_DIST = \ COPYING \ INSTALL \ LICENSE \ Readme.md pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = support/libsass.pc lib_LTLIBRARIES = libsass.la include $(top_srcdir)/Makefile.conf libsass_la_SOURCES = ${CSOURCES} ${SOURCES} libsass_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 1:0:0 if ENABLE_TESTS if ENABLE_COVERAGE nodist_EXTRA_libsass_la_SOURCES = non-existent-file-to-force-CXX-linking.cxx endif endif include_HEADERS = $(top_srcdir)/include/sass.h \ $(top_srcdir)/include/sass2scss.h sass_includedir = $(includedir)/sass sass_include_HEADERS = $(top_srcdir)/include/sass/base.h \ $(top_srcdir)/include/sass/values.h \ $(top_srcdir)/include/sass/version.h \ $(top_srcdir)/include/sass/context.h \ $(top_srcdir)/include/sass/functions.h golibsass-1.0.0/libsass_src/src/MurmurHash2.hpp000066400000000000000000000037771405214413600215000ustar00rootroot00000000000000//----------------------------------------------------------------------------- // MurmurHash2 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. //----------------------------------------------------------------------------- // LibSass only needs MurmurHash2, so we made this header only //----------------------------------------------------------------------------- #ifndef _MURMURHASH2_H_ #define _MURMURHASH2_H_ //----------------------------------------------------------------------------- // Platform-specific functions and macros // Microsoft Visual Studio #if defined(_MSC_VER) && (_MSC_VER < 1600) typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t; // Other compilers #else // defined(_MSC_VER) #include #endif // !defined(_MSC_VER) //----------------------------------------------------------------------------- inline uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) { // 'm' and 'r' are mixing constants generated offline. // They're not really 'magic', they just happen to work well. const uint32_t m = 0x5bd1e995; const int r = 24; // Initialize the hash to a 'random' value uint32_t h = seed ^ len; // Mix 4 bytes at a time into the hash const unsigned char * data = (const unsigned char *)key; while(len >= 4) { uint32_t k = *(uint32_t*)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } // Handle the last few bytes of the input array switch(len) { case 3: h ^= data[2] << 16; /* fall through */ case 2: h ^= data[1] << 8; /* fall through */ case 1: h ^= data[0]; h *= m; }; // Do a few final mixes of the hash to ensure the last few // bytes are well-incorporated. h ^= h >> 13; h *= m; h ^= h >> 15; return h; } //----------------------------------------------------------------------------- #endif // _MURMURHASH2_H_ golibsass-1.0.0/libsass_src/src/ast.cpp000066400000000000000000000746161405214413600201050ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" namespace Sass { static Null sass_null(SourceSpan("null")); const char* sass_op_to_name(enum Sass_OP op) { switch (op) { case AND: return "and"; case OR: return "or"; case EQ: return "eq"; case NEQ: return "neq"; case GT: return "gt"; case GTE: return "gte"; case LT: return "lt"; case LTE: return "lte"; case ADD: return "plus"; case SUB: return "minus"; case MUL: return "times"; case DIV: return "div"; case MOD: return "mod"; // this is only used internally! case NUM_OPS: return "[OPS]"; default: return "invalid"; } } const char* sass_op_separator(enum Sass_OP op) { switch (op) { case AND: return "&&"; case OR: return "||"; case EQ: return "=="; case NEQ: return "!="; case GT: return ">"; case GTE: return ">="; case LT: return "<"; case LTE: return "<="; case ADD: return "+"; case SUB: return "-"; case MUL: return "*"; case DIV: return "/"; case MOD: return "%"; // this is only used internally! case NUM_OPS: return "[OPS]"; default: return "invalid"; } } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// void AST_Node::update_pstate(const SourceSpan& pstate) { pstate_.offset += pstate.position - pstate_.position + pstate.offset; } sass::string AST_Node::to_string(Sass_Inspect_Options opt) const { Sass_Output_Options out(opt); Emitter emitter(out); Inspect i(emitter); i.in_declaration = true; // ToDo: inspect should be const const_cast(this)->perform(&i); return i.get_buffer(); } sass::string AST_Node::to_css(Sass_Inspect_Options opt) const { opt.output_style = TO_CSS; Sass_Output_Options out(opt); Emitter emitter(out); Inspect i(emitter); i.in_declaration = true; // ToDo: inspect should be const const_cast(this)->perform(&i); return i.get_buffer(); } sass::string AST_Node::to_string() const { return to_string({ NESTED, 5 }); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Statement::Statement(SourceSpan pstate, Type st, size_t t) : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false) { } Statement::Statement(const Statement* ptr) : AST_Node(ptr), statement_type_(ptr->statement_type_), tabs_(ptr->tabs_), group_end_(ptr->group_end_) { } bool Statement::bubbles() { return false; } bool Statement::has_content() { return statement_type_ == CONTENT; } bool Statement::is_invisible() const { return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Block::Block(SourceSpan pstate, size_t s, bool r) : Statement(pstate), Vectorized(s), is_root_(r) { } Block::Block(const Block* ptr) : Statement(ptr), Vectorized(*ptr), is_root_(ptr->is_root_) { } bool Block::isInvisible() const { for (auto& item : elements()) { if (!item->is_invisible()) return false; } return true; } bool Block::has_content() { for (size_t i = 0, L = elements().size(); i < L; ++i) { if (elements()[i]->has_content()) return true; } return Statement::has_content(); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ParentStatement::ParentStatement(SourceSpan pstate, Block_Obj b) : Statement(pstate), block_(b) { } ParentStatement::ParentStatement(const ParentStatement* ptr) : Statement(ptr), block_(ptr->block_) { } bool ParentStatement::has_content() { return (block_ && block_->has_content()) || Statement::has_content(); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// StyleRule::StyleRule(SourceSpan pstate, SelectorListObj s, Block_Obj b) : ParentStatement(pstate, b), selector_(s), schema_(), is_root_(false) { statement_type(RULESET); } StyleRule::StyleRule(const StyleRule* ptr) : ParentStatement(ptr), selector_(ptr->selector_), schema_(ptr->schema_), is_root_(ptr->is_root_) { statement_type(RULESET); } bool StyleRule::is_invisible() const { if (const SelectorList * sl = Cast(selector())) { for (size_t i = 0, L = sl->length(); i < L; i += 1) if (!(*sl)[i]->isInvisible()) return false; } return true; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Bubble::Bubble(SourceSpan pstate, Statement_Obj n, Statement_Obj g, size_t t) : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == nullptr) { } Bubble::Bubble(const Bubble* ptr) : Statement(ptr), node_(ptr->node_), group_end_(ptr->group_end_) { } bool Bubble::bubbles() { return true; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Trace::Trace(SourceSpan pstate, sass::string n, Block_Obj b, char type) : ParentStatement(pstate, b), type_(type), name_(n) { } Trace::Trace(const Trace* ptr) : ParentStatement(ptr), type_(ptr->type_), name_(ptr->name_) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// AtRule::AtRule(SourceSpan pstate, sass::string kwd, SelectorListObj sel, Block_Obj b, ExpressionObj val) : ParentStatement(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed { statement_type(DIRECTIVE); } AtRule::AtRule(const AtRule* ptr) : ParentStatement(ptr), keyword_(ptr->keyword_), selector_(ptr->selector_), value_(ptr->value_) // set value manually if needed { statement_type(DIRECTIVE); } bool AtRule::bubbles() { return is_keyframes() || is_media(); } bool AtRule::is_media() { return keyword_.compare("@-webkit-media") == 0 || keyword_.compare("@-moz-media") == 0 || keyword_.compare("@-o-media") == 0 || keyword_.compare("@media") == 0; } bool AtRule::is_keyframes() { return keyword_.compare("@-webkit-keyframes") == 0 || keyword_.compare("@-moz-keyframes") == 0 || keyword_.compare("@-o-keyframes") == 0 || keyword_.compare("@keyframes") == 0; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Keyframe_Rule::Keyframe_Rule(SourceSpan pstate, Block_Obj b) : ParentStatement(pstate, b), name_() { statement_type(KEYFRAMERULE); } Keyframe_Rule::Keyframe_Rule(const Keyframe_Rule* ptr) : ParentStatement(ptr), name_(ptr->name_) { statement_type(KEYFRAMERULE); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Declaration::Declaration(SourceSpan pstate, String_Obj prop, ExpressionObj val, bool i, bool c, Block_Obj b) : ParentStatement(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false) { statement_type(DECLARATION); } Declaration::Declaration(const Declaration* ptr) : ParentStatement(ptr), property_(ptr->property_), value_(ptr->value_), is_important_(ptr->is_important_), is_custom_property_(ptr->is_custom_property_), is_indented_(ptr->is_indented_) { statement_type(DECLARATION); } bool Declaration::is_invisible() const { if (is_custom_property()) return false; return !(value_ && !Cast(value_)); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Assignment::Assignment(SourceSpan pstate, sass::string var, ExpressionObj val, bool is_default, bool is_global) : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global) { statement_type(ASSIGNMENT); } Assignment::Assignment(const Assignment* ptr) : Statement(ptr), variable_(ptr->variable_), value_(ptr->value_), is_default_(ptr->is_default_), is_global_(ptr->is_global_) { statement_type(ASSIGNMENT); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Import::Import(SourceSpan pstate) : Statement(pstate), urls_(sass::vector()), incs_(sass::vector()), import_queries_() { statement_type(IMPORT); } Import::Import(const Import* ptr) : Statement(ptr), urls_(ptr->urls_), incs_(ptr->incs_), import_queries_(ptr->import_queries_) { statement_type(IMPORT); } sass::vector& Import::incs() { return incs_; } sass::vector& Import::urls() { return urls_; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Import_Stub::Import_Stub(SourceSpan pstate, Include res) : Statement(pstate), resource_(res) { statement_type(IMPORT_STUB); } Import_Stub::Import_Stub(const Import_Stub* ptr) : Statement(ptr), resource_(ptr->resource_) { statement_type(IMPORT_STUB); } Include Import_Stub::resource() { return resource_; }; sass::string Import_Stub::imp_path() { return resource_.imp_path; }; sass::string Import_Stub::abs_path() { return resource_.abs_path; }; ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// WarningRule::WarningRule(SourceSpan pstate, ExpressionObj msg) : Statement(pstate), message_(msg) { statement_type(WARNING); } WarningRule::WarningRule(const WarningRule* ptr) : Statement(ptr), message_(ptr->message_) { statement_type(WARNING); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ErrorRule::ErrorRule(SourceSpan pstate, ExpressionObj msg) : Statement(pstate), message_(msg) { statement_type(ERROR); } ErrorRule::ErrorRule(const ErrorRule* ptr) : Statement(ptr), message_(ptr->message_) { statement_type(ERROR); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// DebugRule::DebugRule(SourceSpan pstate, ExpressionObj val) : Statement(pstate), value_(val) { statement_type(DEBUGSTMT); } DebugRule::DebugRule(const DebugRule* ptr) : Statement(ptr), value_(ptr->value_) { statement_type(DEBUGSTMT); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Comment::Comment(SourceSpan pstate, String_Obj txt, bool is_important) : Statement(pstate), text_(txt), is_important_(is_important) { statement_type(COMMENT); } Comment::Comment(const Comment* ptr) : Statement(ptr), text_(ptr->text_), is_important_(ptr->is_important_) { statement_type(COMMENT); } bool Comment::is_invisible() const { return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// If::If(SourceSpan pstate, ExpressionObj pred, Block_Obj con, Block_Obj alt) : ParentStatement(pstate, con), predicate_(pred), alternative_(alt) { statement_type(IF); } If::If(const If* ptr) : ParentStatement(ptr), predicate_(ptr->predicate_), alternative_(ptr->alternative_) { statement_type(IF); } bool If::has_content() { return ParentStatement::has_content() || (alternative_ && alternative_->has_content()); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ForRule::ForRule(SourceSpan pstate, sass::string var, ExpressionObj lo, ExpressionObj hi, Block_Obj b, bool inc) : ParentStatement(pstate, b), variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc) { statement_type(FOR); } ForRule::ForRule(const ForRule* ptr) : ParentStatement(ptr), variable_(ptr->variable_), lower_bound_(ptr->lower_bound_), upper_bound_(ptr->upper_bound_), is_inclusive_(ptr->is_inclusive_) { statement_type(FOR); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// EachRule::EachRule(SourceSpan pstate, sass::vector vars, ExpressionObj lst, Block_Obj b) : ParentStatement(pstate, b), variables_(vars), list_(lst) { statement_type(EACH); } EachRule::EachRule(const EachRule* ptr) : ParentStatement(ptr), variables_(ptr->variables_), list_(ptr->list_) { statement_type(EACH); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// WhileRule::WhileRule(SourceSpan pstate, ExpressionObj pred, Block_Obj b) : ParentStatement(pstate, b), predicate_(pred) { statement_type(WHILE); } WhileRule::WhileRule(const WhileRule* ptr) : ParentStatement(ptr), predicate_(ptr->predicate_) { statement_type(WHILE); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Return::Return(SourceSpan pstate, ExpressionObj val) : Statement(pstate), value_(val) { statement_type(RETURN); } Return::Return(const Return* ptr) : Statement(ptr), value_(ptr->value_) { statement_type(RETURN); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ExtendRule::ExtendRule(SourceSpan pstate, SelectorListObj s) : Statement(pstate), isOptional_(false), selector_(s), schema_() { statement_type(EXTEND); } ExtendRule::ExtendRule(SourceSpan pstate, Selector_Schema_Obj s) : Statement(pstate), isOptional_(false), selector_(), schema_(s) { statement_type(EXTEND); } ExtendRule::ExtendRule(const ExtendRule* ptr) : Statement(ptr), isOptional_(ptr->isOptional_), selector_(ptr->selector_), schema_(ptr->schema_) { statement_type(EXTEND); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Definition::Definition(const Definition* ptr) : ParentStatement(ptr), name_(ptr->name_), parameters_(ptr->parameters_), environment_(ptr->environment_), type_(ptr->type_), native_function_(ptr->native_function_), c_function_(ptr->c_function_), cookie_(ptr->cookie_), is_overload_stub_(ptr->is_overload_stub_), signature_(ptr->signature_) { } Definition::Definition(SourceSpan pstate, sass::string n, Parameters_Obj params, Block_Obj b, Type t) : ParentStatement(pstate, b), name_(n), parameters_(params), environment_(0), type_(t), native_function_(0), c_function_(0), cookie_(0), is_overload_stub_(false), signature_(0) { } Definition::Definition(SourceSpan pstate, Signature sig, sass::string n, Parameters_Obj params, Native_Function func_ptr, bool overload_stub) : ParentStatement(pstate, {}), name_(n), parameters_(params), environment_(0), type_(FUNCTION), native_function_(func_ptr), c_function_(0), cookie_(0), is_overload_stub_(overload_stub), signature_(sig) { } Definition::Definition(SourceSpan pstate, Signature sig, sass::string n, Parameters_Obj params, Sass_Function_Entry c_func) : ParentStatement(pstate, {}), name_(n), parameters_(params), environment_(0), type_(FUNCTION), native_function_(0), c_function_(c_func), cookie_(sass_function_get_cookie(c_func)), is_overload_stub_(false), signature_(sig) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Mixin_Call::Mixin_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Parameters_Obj b_params, Block_Obj b) : ParentStatement(pstate, b), name_(n), arguments_(args), block_parameters_(b_params) { } Mixin_Call::Mixin_Call(const Mixin_Call* ptr) : ParentStatement(ptr), name_(ptr->name_), arguments_(ptr->arguments_), block_parameters_(ptr->block_parameters_) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Content::Content(SourceSpan pstate, Arguments_Obj args) : Statement(pstate), arguments_(args) { statement_type(CONTENT); } Content::Content(const Content* ptr) : Statement(ptr), arguments_(ptr->arguments_) { statement_type(CONTENT); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Expression::Expression(SourceSpan pstate, bool d, bool e, bool i, Type ct) : AST_Node(pstate), is_delayed_(d), is_expanded_(e), is_interpolant_(i), concrete_type_(ct) { } Expression::Expression(const Expression* ptr) : AST_Node(ptr), is_delayed_(ptr->is_delayed_), is_expanded_(ptr->is_expanded_), is_interpolant_(ptr->is_interpolant_), concrete_type_(ptr->concrete_type_) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Unary_Expression::Unary_Expression(SourceSpan pstate, Type t, ExpressionObj o) : Expression(pstate), optype_(t), operand_(o), hash_(0) { } Unary_Expression::Unary_Expression(const Unary_Expression* ptr) : Expression(ptr), optype_(ptr->optype_), operand_(ptr->operand_), hash_(ptr->hash_) { } const sass::string Unary_Expression::type_name() { switch (optype_) { case PLUS: return "plus"; case MINUS: return "minus"; case SLASH: return "slash"; case NOT: return "not"; default: return "invalid"; } } bool Unary_Expression::operator==(const Expression& rhs) const { try { const Unary_Expression* m = Cast(&rhs); if (m == 0) return false; return type() == m->type() && *operand() == *m->operand(); } catch (std::bad_cast&) { return false; } catch (...) { throw; } } size_t Unary_Expression::hash() const { if (hash_ == 0) { hash_ = std::hash()(optype_); hash_combine(hash_, operand()->hash()); }; return hash_; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Argument::Argument(SourceSpan pstate, ExpressionObj val, sass::string n, bool rest, bool keyword) : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0) { if (!name_.empty() && is_rest_argument_) { coreError("variable-length argument may not be passed by name", pstate_); } } Argument::Argument(const Argument* ptr) : Expression(ptr), value_(ptr->value_), name_(ptr->name_), is_rest_argument_(ptr->is_rest_argument_), is_keyword_argument_(ptr->is_keyword_argument_), hash_(ptr->hash_) { if (!name_.empty() && is_rest_argument_) { coreError("variable-length argument may not be passed by name", pstate_); } } void Argument::set_delayed(bool delayed) { if (value_) value_->set_delayed(delayed); is_delayed(delayed); } bool Argument::operator==(const Expression& rhs) const { try { const Argument* m = Cast(&rhs); if (!(m && name() == m->name())) return false; return *value() == *m->value(); } catch (std::bad_cast&) { return false; } catch (...) { throw; } } size_t Argument::hash() const { if (hash_ == 0) { hash_ = std::hash()(name()); hash_combine(hash_, value()->hash()); } return hash_; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Arguments::Arguments(SourceSpan pstate) : Expression(pstate), Vectorized(), has_named_arguments_(false), has_rest_argument_(false), has_keyword_argument_(false) { } Arguments::Arguments(const Arguments* ptr) : Expression(ptr), Vectorized(*ptr), has_named_arguments_(ptr->has_named_arguments_), has_rest_argument_(ptr->has_rest_argument_), has_keyword_argument_(ptr->has_keyword_argument_) { } void Arguments::set_delayed(bool delayed) { for (Argument_Obj arg : elements()) { if (arg) arg->set_delayed(delayed); } is_delayed(delayed); } Argument_Obj Arguments::get_rest_argument() { if (this->has_rest_argument()) { for (Argument_Obj arg : this->elements()) { if (arg->is_rest_argument()) { return arg; } } } return {}; } Argument_Obj Arguments::get_keyword_argument() { if (this->has_keyword_argument()) { for (Argument_Obj arg : this->elements()) { if (arg->is_keyword_argument()) { return arg; } } } return {}; } void Arguments::adjust_after_pushing(Argument_Obj a) { if (!a->name().empty()) { if (has_keyword_argument()) { coreError("named arguments must precede variable-length argument", a->pstate()); } has_named_arguments(true); } else if (a->is_rest_argument()) { if (has_rest_argument()) { coreError("functions and mixins may only be called with one variable-length argument", a->pstate()); } if (has_keyword_argument_) { coreError("only keyword arguments may follow variable arguments", a->pstate()); } has_rest_argument(true); } else if (a->is_keyword_argument()) { if (has_keyword_argument()) { coreError("functions and mixins may only be called with one keyword argument", a->pstate()); } has_keyword_argument(true); } else { if (has_rest_argument()) { coreError("ordinal arguments must precede variable-length arguments", a->pstate()); } if (has_named_arguments()) { coreError("ordinal arguments must precede named arguments", a->pstate()); } } } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Media_Query::Media_Query(SourceSpan pstate, String_Obj t, size_t s, bool n, bool r) : Expression(pstate), Vectorized(s), media_type_(t), is_negated_(n), is_restricted_(r) { } Media_Query::Media_Query(const Media_Query* ptr) : Expression(ptr), Vectorized(*ptr), media_type_(ptr->media_type_), is_negated_(ptr->is_negated_), is_restricted_(ptr->is_restricted_) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Media_Query_Expression::Media_Query_Expression(SourceSpan pstate, ExpressionObj f, ExpressionObj v, bool i) : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) { } Media_Query_Expression::Media_Query_Expression(const Media_Query_Expression* ptr) : Expression(ptr), feature_(ptr->feature_), value_(ptr->value_), is_interpolated_(ptr->is_interpolated_) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// At_Root_Query::At_Root_Query(SourceSpan pstate, ExpressionObj f, ExpressionObj v, bool i) : Expression(pstate), feature_(f), value_(v) { } At_Root_Query::At_Root_Query(const At_Root_Query* ptr) : Expression(ptr), feature_(ptr->feature_), value_(ptr->value_) { } bool At_Root_Query::exclude(sass::string str) { bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; List* l = static_cast(value().ptr()); sass::string v; if (with) { if (!l || l->length() == 0) return str.compare("rule") != 0; for (size_t i = 0, L = l->length(); i < L; ++i) { v = unquote((*l)[i]->to_string()); if (v.compare("all") == 0 || v == str) return false; } return true; } else { if (!l || !l->length()) return str.compare("rule") == 0; for (size_t i = 0, L = l->length(); i < L; ++i) { v = unquote((*l)[i]->to_string()); if (v.compare("all") == 0 || v == str) return true; } return false; } } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// AtRootRule::AtRootRule(SourceSpan pstate, Block_Obj b, At_Root_Query_Obj e) : ParentStatement(pstate, b), expression_(e) { statement_type(ATROOT); } AtRootRule::AtRootRule(const AtRootRule* ptr) : ParentStatement(ptr), expression_(ptr->expression_) { statement_type(ATROOT); } bool AtRootRule::bubbles() { return true; } bool AtRootRule::exclude_node(Statement_Obj s) { if (expression() == nullptr) { return s->statement_type() == Statement::RULESET; } if (s->statement_type() == Statement::DIRECTIVE) { if (AtRuleObj dir = Cast(s)) { sass::string keyword(dir->keyword()); if (keyword.length() > 0) keyword.erase(0, 1); return expression()->exclude(keyword); } } if (s->statement_type() == Statement::MEDIA) { return expression()->exclude("media"); } if (s->statement_type() == Statement::RULESET) { return expression()->exclude("rule"); } if (s->statement_type() == Statement::SUPPORTS) { return expression()->exclude("supports"); } if (AtRuleObj dir = Cast(s)) { if (dir->is_keyframes()) return expression()->exclude("keyframes"); } return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Parameter::Parameter(SourceSpan pstate, sass::string n, ExpressionObj def, bool rest) : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest) { } Parameter::Parameter(const Parameter* ptr) : AST_Node(ptr), name_(ptr->name_), default_value_(ptr->default_value_), is_rest_parameter_(ptr->is_rest_parameter_) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Parameters::Parameters(SourceSpan pstate) : AST_Node(pstate), Vectorized(), has_optional_parameters_(false), has_rest_parameter_(false) { } Parameters::Parameters(const Parameters* ptr) : AST_Node(ptr), Vectorized(*ptr), has_optional_parameters_(ptr->has_optional_parameters_), has_rest_parameter_(ptr->has_rest_parameter_) { } void Parameters::adjust_after_pushing(Parameter_Obj p) { if (p->default_value()) { if (has_rest_parameter()) { coreError("optional parameters may not be combined with variable-length parameters", p->pstate()); } has_optional_parameters(true); } else if (p->is_rest_parameter()) { if (has_rest_parameter()) { coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate()); } has_rest_parameter(true); } else { if (has_rest_parameter()) { coreError("required parameters must precede variable-length parameters", p->pstate()); } if (has_optional_parameters()) { coreError("required parameters must precede optional parameters", p->pstate()); } } } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // If you forget to add a class here you will get // undefined reference to `vtable for Sass::Class' IMPLEMENT_AST_OPERATORS(StyleRule); IMPLEMENT_AST_OPERATORS(MediaRule); IMPLEMENT_AST_OPERATORS(CssMediaRule); IMPLEMENT_AST_OPERATORS(CssMediaQuery); IMPLEMENT_AST_OPERATORS(Import); IMPLEMENT_AST_OPERATORS(Import_Stub); IMPLEMENT_AST_OPERATORS(AtRule); IMPLEMENT_AST_OPERATORS(AtRootRule); IMPLEMENT_AST_OPERATORS(WhileRule); IMPLEMENT_AST_OPERATORS(EachRule); IMPLEMENT_AST_OPERATORS(ForRule); IMPLEMENT_AST_OPERATORS(If); IMPLEMENT_AST_OPERATORS(Mixin_Call); IMPLEMENT_AST_OPERATORS(ExtendRule); IMPLEMENT_AST_OPERATORS(Media_Query); IMPLEMENT_AST_OPERATORS(Media_Query_Expression); IMPLEMENT_AST_OPERATORS(DebugRule); IMPLEMENT_AST_OPERATORS(ErrorRule); IMPLEMENT_AST_OPERATORS(WarningRule); IMPLEMENT_AST_OPERATORS(Assignment); IMPLEMENT_AST_OPERATORS(Return); IMPLEMENT_AST_OPERATORS(At_Root_Query); IMPLEMENT_AST_OPERATORS(Comment); IMPLEMENT_AST_OPERATORS(Parameters); IMPLEMENT_AST_OPERATORS(Parameter); IMPLEMENT_AST_OPERATORS(Arguments); IMPLEMENT_AST_OPERATORS(Argument); IMPLEMENT_AST_OPERATORS(Unary_Expression); IMPLEMENT_AST_OPERATORS(Block); IMPLEMENT_AST_OPERATORS(Content); IMPLEMENT_AST_OPERATORS(Trace); IMPLEMENT_AST_OPERATORS(Keyframe_Rule); IMPLEMENT_AST_OPERATORS(Bubble); IMPLEMENT_AST_OPERATORS(Definition); IMPLEMENT_AST_OPERATORS(Declaration); ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// } golibsass-1.0.0/libsass_src/src/ast.hpp000066400000000000000000001072751405214413600201100ustar00rootroot00000000000000#ifndef SASS_AST_H #define SASS_AST_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "sass/base.h" #include "ast_helpers.hpp" #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp" #include "file.hpp" #include "position.hpp" #include "operation.hpp" #include "environment.hpp" #include "fn_utils.hpp" namespace Sass { // ToDo: where does this fit best? // We don't share this with C-API? class Operand { public: Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false) : operand(operand), ws_before(ws_before), ws_after(ws_after) { } public: enum Sass_OP operand; bool ws_before; bool ws_after; }; ////////////////////////////////////////////////////////// // `hash_combine` comes from boost (functional/hash): // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html // Boost Software License - Version 1.0 // http://www.boost.org/users/license.html template void hash_combine (std::size_t& seed, const T& val) { seed ^= std::hash()(val) + 0x9e3779b9 + (seed<<6) + (seed>>2); } ////////////////////////////////////////////////////////// const char* sass_op_to_name(enum Sass_OP op); const char* sass_op_separator(enum Sass_OP op); ////////////////////////////////////////////////////////// // Abstract base class for all abstract syntax tree nodes. ////////////////////////////////////////////////////////// class AST_Node : public SharedObj { ADD_PROPERTY(SourceSpan, pstate) public: AST_Node(SourceSpan pstate) : pstate_(pstate) { } AST_Node(const AST_Node* ptr) : pstate_(ptr->pstate_) { } // allow implicit conversion to string // needed for by SharedPtr implementation operator sass::string() { return to_string(); } // AST_Node(AST_Node& ptr) = delete; virtual ~AST_Node() = 0; virtual size_t hash() const { return 0; } virtual sass::string inspect() const { return to_string({ INSPECT, 5 }); } virtual sass::string to_sass() const { return to_string({ TO_SASS, 5 }); } virtual sass::string to_string(Sass_Inspect_Options opt) const; virtual sass::string to_css(Sass_Inspect_Options opt) const; virtual sass::string to_string() const; virtual void cloneChildren() {}; // generic find function (not fully implemented yet) // ToDo: add specific implementations to all children virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); }; void update_pstate(const SourceSpan& pstate); // Some objects are not meant to be compared // ToDo: maybe fall-back to pointer comparison? virtual bool operator== (const AST_Node& rhs) const { throw std::runtime_error("operator== not implemented"); } // We can give some reasonable implementations by using // invert operators on the specialized implementations virtual bool operator!= (const AST_Node& rhs) const { // Unequal if not equal return !(*this == rhs); } ATTACH_ABSTRACT_AST_OPERATIONS(AST_Node); ATTACH_ABSTRACT_CRTP_PERFORM_METHODS() }; inline AST_Node::~AST_Node() { } ////////////////////////////////////////////////////////////////////// // define cast template now (need complete type) ////////////////////////////////////////////////////////////////////// template T* Cast(AST_Node* ptr) { return ptr && typeid(T) == typeid(*ptr) ? static_cast(ptr) : NULL; }; template const T* Cast(const AST_Node* ptr) { return ptr && typeid(T) == typeid(*ptr) ? static_cast(ptr) : NULL; }; ////////////////////////////////////////////////////////////////////// // Abstract base class for expressions. This side of the AST hierarchy // represents elements in value contexts, which exist primarily to be // evaluated and returned. ////////////////////////////////////////////////////////////////////// class Expression : public AST_Node { public: enum Type { NONE, BOOLEAN, NUMBER, COLOR, STRING, LIST, MAP, SELECTOR, NULL_VAL, FUNCTION_VAL, C_WARNING, C_ERROR, FUNCTION, VARIABLE, PARENT, NUM_TYPES }; private: // expressions in some contexts shouldn't be evaluated ADD_PROPERTY(bool, is_delayed) ADD_PROPERTY(bool, is_expanded) ADD_PROPERTY(bool, is_interpolant) ADD_PROPERTY(Type, concrete_type) public: Expression(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE); virtual operator bool() { return true; } virtual ~Expression() { } virtual bool is_invisible() const { return false; } virtual sass::string type() const { return ""; } static sass::string type_name() { return ""; } virtual bool is_false() { return false; } // virtual bool is_true() { return !is_false(); } virtual bool operator< (const Expression& rhs) const { return false; } virtual bool operator== (const Expression& rhs) const { return false; } inline bool operator>(const Expression& rhs) const { return rhs < *this; } inline bool operator!=(const Expression& rhs) const { return !(rhs == *this); } virtual bool eq(const Expression& rhs) const { return *this == rhs; }; virtual void set_delayed(bool delayed) { is_delayed(delayed); } virtual bool has_interpolant() const { return is_interpolant(); } virtual bool is_left_interpolant() const { return is_interpolant(); } virtual bool is_right_interpolant() const { return is_interpolant(); } ATTACH_VIRTUAL_AST_OPERATIONS(Expression); size_t hash() const override { return 0; } }; } ///////////////////////////////////////////////////////////////////////////////////// // Hash method specializations for std::unordered_map to work with Sass::Expression ///////////////////////////////////////////////////////////////////////////////////// namespace std { template<> struct hash { size_t operator()(Sass::ExpressionObj s) const { return s->hash(); } }; template<> struct equal_to { bool operator()( Sass::ExpressionObj lhs, Sass::ExpressionObj rhs) const { return lhs->hash() == rhs->hash(); } }; } namespace Sass { ///////////////////////////////////////////////////////////////////////////// // Mixin class for AST nodes that should behave like vectors. Uses the // "Template Method" design pattern to allow subclasses to adjust their flags // when certain objects are pushed. ///////////////////////////////////////////////////////////////////////////// template class Vectorized { sass::vector elements_; protected: mutable size_t hash_; void reset_hash() { hash_ = 0; } virtual void adjust_after_pushing(T element) { } public: Vectorized(size_t s = 0) : hash_(0) { elements_.reserve(s); } Vectorized(sass::vector vec) : elements_(std::move(vec)), hash_(0) {} virtual ~Vectorized() = 0; size_t length() const { return elements_.size(); } bool empty() const { return elements_.empty(); } void clear() { return elements_.clear(); } T& last() { return elements_.back(); } T& first() { return elements_.front(); } const T& last() const { return elements_.back(); } const T& first() const { return elements_.front(); } bool operator== (const Vectorized& rhs) const { // Abort early if sizes do not match if (length() != rhs.length()) return false; // Otherwise test each node for object equalicy in order return std::equal(begin(), end(), rhs.begin(), ObjEqualityFn); } bool operator!= (const Vectorized& rhs) const { return !(*this == rhs); } T& operator[](size_t i) { return elements_[i]; } virtual const T& at(size_t i) const { return elements_.at(i); } virtual T& at(size_t i) { return elements_.at(i); } const T& get(size_t i) const { return elements_[i]; } const T& operator[](size_t i) const { return elements_[i]; } // Implicitly get the sass::vector from our object // Makes the Vector directly assignable to sass::vector // You are responsible to make a copy if needed // Note: since this returns the real object, we can't // Note: guarantee that the hash will not get out of sync operator sass::vector&() { return elements_; } operator const sass::vector&() const { return elements_; } // Explicitly request all elements as a real sass::vector // You are responsible to make a copy if needed // Note: since this returns the real object, we can't // Note: guarantee that the hash will not get out of sync sass::vector& elements() { return elements_; } const sass::vector& elements() const { return elements_; } // Insert all items from compatible vector void concat(const sass::vector& v) { if (!v.empty()) reset_hash(); elements().insert(end(), v.begin(), v.end()); } // Syntatic sugar for pointers void concat(const Vectorized* v) { if (v != nullptr) { return concat(*v); } } // Insert one item on the front void unshift(T element) { reset_hash(); elements_.insert(begin(), element); } // Remove and return item on the front // ToDo: handle empty vectors T shift() { reset_hash(); T first = get(0); elements_.erase(begin()); return first; } // Insert one item on the back // ToDo: rename this to push void append(T element) { reset_hash(); elements_.insert(end(), element); // ToDo: Mostly used by parameters and arguments // ToDo: Find a more elegant way to support this adjust_after_pushing(element); } // Check if an item already exists // Uses underlying object `operator==` // E.g. compares the actual objects bool contains(const T& el) const { for (const T& rhs : elements_) { // Test the underlying objects for equality // A std::find checks for pointer equality if (ObjEqualityFn(el, rhs)) { return true; } } return false; } // This might be better implemented as `operator=`? void elements(sass::vector e) { reset_hash(); elements_ = std::move(e); } virtual size_t hash() const { if (hash_ == 0) { for (const T& el : elements_) { hash_combine(hash_, el->hash()); } } return hash_; } template typename sass::vector::iterator insert(P position, const V& val) { reset_hash(); return elements_.insert(position, val); } typename sass::vector::iterator end() { return elements_.end(); } typename sass::vector::iterator begin() { return elements_.begin(); } typename sass::vector::const_iterator end() const { return elements_.end(); } typename sass::vector::const_iterator begin() const { return elements_.begin(); } typename sass::vector::iterator erase(typename sass::vector::iterator el) { reset_hash(); return elements_.erase(el); } typename sass::vector::const_iterator erase(typename sass::vector::const_iterator el) { reset_hash(); return elements_.erase(el); } }; template inline Vectorized::~Vectorized() { } ///////////////////////////////////////////////////////////////////////////// // Mixin class for AST nodes that should behave like a hash table. Uses an // extra internally to maintain insertion order for interation. ///////////////////////////////////////////////////////////////////////////// template class Hashed { private: std::unordered_map< K, T, ObjHash, ObjHashEquality > elements_; sass::vector _keys; sass::vector _values; protected: mutable size_t hash_; K duplicate_key_; void reset_hash() { hash_ = 0; } void reset_duplicate_key() { duplicate_key_ = {}; } virtual void adjust_after_pushing(std::pair p) { } public: Hashed(size_t s = 0) : elements_(), _keys(), _values(), hash_(0), duplicate_key_({}) { _keys.reserve(s); _values.reserve(s); elements_.reserve(s); } virtual ~Hashed(); size_t length() const { return _keys.size(); } bool empty() const { return _keys.empty(); } bool has(K k) const { return elements_.find(k) != elements_.end(); } T at(K k) const { if (elements_.count(k)) { return elements_.at(k); } else { return {}; } } bool has_duplicate_key() const { return duplicate_key_ != nullptr; } K get_duplicate_key() const { return duplicate_key_; } const std::unordered_map< K, T, ObjHash, ObjHashEquality >& elements() { return elements_; } Hashed& operator<<(std::pair p) { reset_hash(); if (!has(p.first)) { _keys.push_back(p.first); _values.push_back(p.second); } else if (!duplicate_key_) { duplicate_key_ = p.first; } elements_[p.first] = p.second; adjust_after_pushing(p); return *this; } Hashed& operator+=(Hashed* h) { if (length() == 0) { this->elements_ = h->elements_; this->_values = h->_values; this->_keys = h->_keys; return *this; } for (auto key : h->keys()) { *this << std::make_pair(key, h->at(key)); } reset_duplicate_key(); return *this; } const std::unordered_map< K, T, ObjHash, ObjHashEquality >& pairs() const { return elements_; } const sass::vector& keys() const { return _keys; } const sass::vector& values() const { return _values; } // std::unordered_map::iterator end() { return elements_.end(); } // std::unordered_map::iterator begin() { return elements_.begin(); } // std::unordered_map::const_iterator end() const { return elements_.end(); } // std::unordered_map::const_iterator begin() const { return elements_.begin(); } }; template inline Hashed::~Hashed() { } ///////////////////////////////////////////////////////////////////////// // Abstract base class for statements. This side of the AST hierarchy // represents elements in expansion contexts, which exist primarily to be // rewritten and macro-expanded. ///////////////////////////////////////////////////////////////////////// class Statement : public AST_Node { public: enum Type { NONE, RULESET, MEDIA, DIRECTIVE, SUPPORTS, ATROOT, BUBBLE, CONTENT, KEYFRAMERULE, DECLARATION, ASSIGNMENT, IMPORT_STUB, IMPORT, COMMENT, WARNING, RETURN, EXTEND, ERROR, DEBUGSTMT, WHILE, EACH, FOR, IF }; private: ADD_PROPERTY(Type, statement_type) ADD_PROPERTY(size_t, tabs) ADD_PROPERTY(bool, group_end) public: Statement(SourceSpan pstate, Type st = NONE, size_t t = 0); virtual ~Statement() = 0; // virtual destructor // needed for rearranging nested rulesets during CSS emission virtual bool bubbles(); virtual bool has_content(); virtual bool is_invisible() const; ATTACH_VIRTUAL_AST_OPERATIONS(Statement) }; inline Statement::~Statement() { } //////////////////////// // Blocks of statements. //////////////////////// class Block final : public Statement, public Vectorized { ADD_PROPERTY(bool, is_root) // needed for properly formatted CSS emission protected: void adjust_after_pushing(Statement_Obj s) override {} public: Block(SourceSpan pstate, size_t s = 0, bool r = false); bool isInvisible() const; bool has_content() override; ATTACH_AST_OPERATIONS(Block) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// // Abstract base class for statements that contain blocks of statements. //////////////////////////////////////////////////////////////////////// class ParentStatement : public Statement { ADD_PROPERTY(Block_Obj, block) public: ParentStatement(SourceSpan pstate, Block_Obj b); ParentStatement(const ParentStatement* ptr); // copy constructor virtual ~ParentStatement() = 0; // virtual destructor virtual bool has_content() override; }; inline ParentStatement::~ParentStatement() { } ///////////////////////////////////////////////////////////////////////////// // Rulesets (i.e., sets of styles headed by a selector and containing a block // of style declarations. ///////////////////////////////////////////////////////////////////////////// class StyleRule final : public ParentStatement { ADD_PROPERTY(SelectorListObj, selector) ADD_PROPERTY(Selector_Schema_Obj, schema) ADD_PROPERTY(bool, is_root); public: StyleRule(SourceSpan pstate, SelectorListObj s = {}, Block_Obj b = {}); bool is_invisible() const override; ATTACH_AST_OPERATIONS(StyleRule) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////// // Bubble. ///////////////// class Bubble final : public Statement { ADD_PROPERTY(Statement_Obj, node) ADD_PROPERTY(bool, group_end) public: Bubble(SourceSpan pstate, Statement_Obj n, Statement_Obj g = {}, size_t t = 0); bool bubbles() override; ATTACH_AST_OPERATIONS(Bubble) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////// // Trace. ///////////////// class Trace final : public ParentStatement { ADD_CONSTREF(char, type) ADD_CONSTREF(sass::string, name) public: Trace(SourceSpan pstate, sass::string n, Block_Obj b = {}, char type = 'm'); ATTACH_AST_OPERATIONS(Trace) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////////////////////////// // At-rules -- arbitrary directives beginning with "@" that may have an // optional statement block. /////////////////////////////////////////////////////////////////////// class AtRule final : public ParentStatement { ADD_CONSTREF(sass::string, keyword) ADD_PROPERTY(SelectorListObj, selector) ADD_PROPERTY(ExpressionObj, value) public: AtRule(SourceSpan pstate, sass::string kwd, SelectorListObj sel = {}, Block_Obj b = {}, ExpressionObj val = {}); bool bubbles() override; bool is_media(); bool is_keyframes(); ATTACH_AST_OPERATIONS(AtRule) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////////////////////////// // Keyframe-rules -- the child blocks of "@keyframes" nodes. /////////////////////////////////////////////////////////////////////// class Keyframe_Rule final : public ParentStatement { // according to css spec, this should be // = | ADD_PROPERTY(SelectorListObj, name) public: Keyframe_Rule(SourceSpan pstate, Block_Obj b); ATTACH_AST_OPERATIONS(Keyframe_Rule) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// // Declarations -- style rules consisting of a property name and values. //////////////////////////////////////////////////////////////////////// class Declaration final : public ParentStatement { ADD_PROPERTY(String_Obj, property) ADD_PROPERTY(ExpressionObj, value) ADD_PROPERTY(bool, is_important) ADD_PROPERTY(bool, is_custom_property) ADD_PROPERTY(bool, is_indented) public: Declaration(SourceSpan pstate, String_Obj prop, ExpressionObj val, bool i = false, bool c = false, Block_Obj b = {}); bool is_invisible() const override; ATTACH_AST_OPERATIONS(Declaration) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////// // Assignments -- variable and value. ///////////////////////////////////// class Assignment final : public Statement { ADD_CONSTREF(sass::string, variable) ADD_PROPERTY(ExpressionObj, value) ADD_PROPERTY(bool, is_default) ADD_PROPERTY(bool, is_global) public: Assignment(SourceSpan pstate, sass::string var, ExpressionObj val, bool is_default = false, bool is_global = false); ATTACH_AST_OPERATIONS(Assignment) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// // Import directives. CSS and Sass import lists can be intermingled, so it's // necessary to store a list of each in an Import node. //////////////////////////////////////////////////////////////////////////// class Import final : public Statement { sass::vector urls_; sass::vector incs_; ADD_PROPERTY(List_Obj, import_queries); public: Import(SourceSpan pstate); sass::vector& incs(); sass::vector& urls(); ATTACH_AST_OPERATIONS(Import) ATTACH_CRTP_PERFORM_METHODS() }; // not yet resolved single import // so far we only know requested name class Import_Stub final : public Statement { Include resource_; public: Import_Stub(SourceSpan pstate, Include res); Include resource(); sass::string imp_path(); sass::string abs_path(); ATTACH_AST_OPERATIONS(Import_Stub) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////// // The Sass `@warn` directive. ////////////////////////////// class WarningRule final : public Statement { ADD_PROPERTY(ExpressionObj, message) public: WarningRule(SourceSpan pstate, ExpressionObj msg); ATTACH_AST_OPERATIONS(WarningRule) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////// // The Sass `@error` directive. /////////////////////////////// class ErrorRule final : public Statement { ADD_PROPERTY(ExpressionObj, message) public: ErrorRule(SourceSpan pstate, ExpressionObj msg); ATTACH_AST_OPERATIONS(ErrorRule) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////// // The Sass `@debug` directive. /////////////////////////////// class DebugRule final : public Statement { ADD_PROPERTY(ExpressionObj, value) public: DebugRule(SourceSpan pstate, ExpressionObj val); ATTACH_AST_OPERATIONS(DebugRule) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////// // CSS comments. These may be interpolated. /////////////////////////////////////////// class Comment final : public Statement { ADD_PROPERTY(String_Obj, text) ADD_PROPERTY(bool, is_important) public: Comment(SourceSpan pstate, String_Obj txt, bool is_important); virtual bool is_invisible() const override; ATTACH_AST_OPERATIONS(Comment) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////// // The Sass `@if` control directive. //////////////////////////////////// class If final : public ParentStatement { ADD_PROPERTY(ExpressionObj, predicate) ADD_PROPERTY(Block_Obj, alternative) public: If(SourceSpan pstate, ExpressionObj pred, Block_Obj con, Block_Obj alt = {}); virtual bool has_content() override; ATTACH_AST_OPERATIONS(If) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////// // The Sass `@for` control directive. ///////////////////////////////////// class ForRule final : public ParentStatement { ADD_CONSTREF(sass::string, variable) ADD_PROPERTY(ExpressionObj, lower_bound) ADD_PROPERTY(ExpressionObj, upper_bound) ADD_PROPERTY(bool, is_inclusive) public: ForRule(SourceSpan pstate, sass::string var, ExpressionObj lo, ExpressionObj hi, Block_Obj b, bool inc); ATTACH_AST_OPERATIONS(ForRule) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////// // The Sass `@each` control directive. ////////////////////////////////////// class EachRule final : public ParentStatement { ADD_PROPERTY(sass::vector, variables) ADD_PROPERTY(ExpressionObj, list) public: EachRule(SourceSpan pstate, sass::vector vars, ExpressionObj lst, Block_Obj b); ATTACH_AST_OPERATIONS(EachRule) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////// // The Sass `@while` control directive. /////////////////////////////////////// class WhileRule final : public ParentStatement { ADD_PROPERTY(ExpressionObj, predicate) public: WhileRule(SourceSpan pstate, ExpressionObj pred, Block_Obj b); ATTACH_AST_OPERATIONS(WhileRule) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////// // The @return directive for use inside SassScript functions. ///////////////////////////////////////////////////////////// class Return final : public Statement { ADD_PROPERTY(ExpressionObj, value) public: Return(SourceSpan pstate, ExpressionObj val); ATTACH_AST_OPERATIONS(Return) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////////////////////// // Definitions for both mixins and functions. The two cases are distinguished // by a type tag. ///////////////////////////////////////////////////////////////////////////// class Definition final : public ParentStatement { public: enum Type { MIXIN, FUNCTION }; ADD_CONSTREF(sass::string, name) ADD_PROPERTY(Parameters_Obj, parameters) ADD_PROPERTY(Env*, environment) ADD_PROPERTY(Type, type) ADD_PROPERTY(Native_Function, native_function) ADD_PROPERTY(Sass_Function_Entry, c_function) ADD_PROPERTY(void*, cookie) ADD_PROPERTY(bool, is_overload_stub) ADD_PROPERTY(Signature, signature) public: Definition(SourceSpan pstate, sass::string n, Parameters_Obj params, Block_Obj b, Type t); Definition(SourceSpan pstate, Signature sig, sass::string n, Parameters_Obj params, Native_Function func_ptr, bool overload_stub = false); Definition(SourceSpan pstate, Signature sig, sass::string n, Parameters_Obj params, Sass_Function_Entry c_func); ATTACH_AST_OPERATIONS(Definition) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////// // Mixin calls (i.e., `@include ...`). ////////////////////////////////////// class Mixin_Call final : public ParentStatement { ADD_CONSTREF(sass::string, name) ADD_PROPERTY(Arguments_Obj, arguments) ADD_PROPERTY(Parameters_Obj, block_parameters) public: Mixin_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Parameters_Obj b_params = {}, Block_Obj b = {}); ATTACH_AST_OPERATIONS(Mixin_Call) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////// // The @content directive for mixin content blocks. /////////////////////////////////////////////////// class Content final : public Statement { ADD_PROPERTY(Arguments_Obj, arguments) public: Content(SourceSpan pstate, Arguments_Obj args); ATTACH_AST_OPERATIONS(Content) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// // Arithmetic negation (logical negation is just an ordinary function call). //////////////////////////////////////////////////////////////////////////// class Unary_Expression final : public Expression { public: enum Type { PLUS, MINUS, NOT, SLASH }; private: HASH_PROPERTY(Type, optype) HASH_PROPERTY(ExpressionObj, operand) mutable size_t hash_; public: Unary_Expression(SourceSpan pstate, Type t, ExpressionObj o); const sass::string type_name(); virtual bool operator==(const Expression& rhs) const override; size_t hash() const override; ATTACH_AST_OPERATIONS(Unary_Expression) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////// // Individual argument objects for mixin and function calls. //////////////////////////////////////////////////////////// class Argument final : public Expression { HASH_PROPERTY(ExpressionObj, value) HASH_CONSTREF(sass::string, name) ADD_PROPERTY(bool, is_rest_argument) ADD_PROPERTY(bool, is_keyword_argument) mutable size_t hash_; public: Argument(SourceSpan pstate, ExpressionObj val, sass::string n = "", bool rest = false, bool keyword = false); void set_delayed(bool delayed) override; bool operator==(const Expression& rhs) const override; size_t hash() const override; ATTACH_AST_OPERATIONS(Argument) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// // Argument lists -- in their own class to facilitate context-sensitive // error checking (e.g., ensuring that all ordinal arguments precede all // named arguments). //////////////////////////////////////////////////////////////////////// class Arguments final : public Expression, public Vectorized { ADD_PROPERTY(bool, has_named_arguments) ADD_PROPERTY(bool, has_rest_argument) ADD_PROPERTY(bool, has_keyword_argument) protected: void adjust_after_pushing(Argument_Obj a) override; public: Arguments(SourceSpan pstate); void set_delayed(bool delayed) override; Argument_Obj get_rest_argument(); Argument_Obj get_keyword_argument(); ATTACH_AST_OPERATIONS(Arguments) ATTACH_CRTP_PERFORM_METHODS() }; // A Media StyleRule before it has been evaluated // Could be already final or an interpolation class MediaRule final : public ParentStatement { ADD_PROPERTY(List_Obj, schema) public: MediaRule(SourceSpan pstate, Block_Obj block = {}); bool bubbles() override { return true; }; bool is_invisible() const override { return false; }; ATTACH_AST_OPERATIONS(MediaRule) ATTACH_CRTP_PERFORM_METHODS() }; // A Media StyleRule after it has been evaluated // Representing the static or resulting css class CssMediaRule final : public ParentStatement, public Vectorized { public: CssMediaRule(SourceSpan pstate, Block_Obj b); bool bubbles() override { return true; }; bool isInvisible() const { return empty(); } bool is_invisible() const override { return false; }; public: // Hash and equality implemtation from vector size_t hash() const override { return Vectorized::hash(); } // Check if two instances are considered equal bool operator== (const CssMediaRule& rhs) const { return Vectorized::operator== (rhs); } bool operator!=(const CssMediaRule& rhs) const { // Invert from equality return !(*this == rhs); } ATTACH_AST_OPERATIONS(CssMediaRule) ATTACH_CRTP_PERFORM_METHODS() }; // Media Queries after they have been evaluated // Representing the static or resulting css class CssMediaQuery final : public AST_Node { // The modifier, probably either "not" or "only". // This may be `null` if no modifier is in use. ADD_PROPERTY(sass::string, modifier); // The media type, for example "screen" or "print". // This may be `null`. If so, [features] will not be empty. ADD_PROPERTY(sass::string, type); // Feature queries, including parentheses. ADD_PROPERTY(sass::vector, features); public: CssMediaQuery(SourceSpan pstate); // Check if two instances are considered equal bool operator== (const CssMediaQuery& rhs) const; bool operator!=(const CssMediaQuery& rhs) const { // Invert from equality return !(*this == rhs); } // Returns true if this query is empty // Meaning it has no type and features bool empty() const { return type_.empty() && modifier_.empty() && features_.empty(); } // Whether this media query matches all media types. bool matchesAllTypes() const { return type_.empty() || Util::equalsLiteral("all", type_); } // Merges this with [other] and adds a query that matches the intersection // of both inputs to [result]. Returns false if the result is unrepresentable CssMediaQuery_Obj merge(CssMediaQuery_Obj& other); ATTACH_AST_OPERATIONS(CssMediaQuery) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////// // Media queries (replaced by MediaRule at al). // ToDo: only used for interpolation case //////////////////////////////////////////////////// class Media_Query final : public Expression, public Vectorized { ADD_PROPERTY(String_Obj, media_type) ADD_PROPERTY(bool, is_negated) ADD_PROPERTY(bool, is_restricted) public: Media_Query(SourceSpan pstate, String_Obj t = {}, size_t s = 0, bool n = false, bool r = false); ATTACH_AST_OPERATIONS(Media_Query) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////// // Media expressions (for use inside media queries). // ToDo: only used for interpolation case //////////////////////////////////////////////////// class Media_Query_Expression final : public Expression { ADD_PROPERTY(ExpressionObj, feature) ADD_PROPERTY(ExpressionObj, value) ADD_PROPERTY(bool, is_interpolated) public: Media_Query_Expression(SourceSpan pstate, ExpressionObj f, ExpressionObj v, bool i = false); ATTACH_AST_OPERATIONS(Media_Query_Expression) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////// // At root expressions (for use inside @at-root). ///////////////////////////////////////////////// class At_Root_Query final : public Expression { private: ADD_PROPERTY(ExpressionObj, feature) ADD_PROPERTY(ExpressionObj, value) public: At_Root_Query(SourceSpan pstate, ExpressionObj f = {}, ExpressionObj v = {}, bool i = false); bool exclude(sass::string str); ATTACH_AST_OPERATIONS(At_Root_Query) ATTACH_CRTP_PERFORM_METHODS() }; /////////// // At-root. /////////// class AtRootRule final : public ParentStatement { ADD_PROPERTY(At_Root_Query_Obj, expression) public: AtRootRule(SourceSpan pstate, Block_Obj b = {}, At_Root_Query_Obj e = {}); bool bubbles() override; bool exclude_node(Statement_Obj s); ATTACH_AST_OPERATIONS(AtRootRule) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////// // Individual parameter objects for mixins and functions. ///////////////////////////////////////////////////////// class Parameter final : public AST_Node { ADD_CONSTREF(sass::string, name) ADD_PROPERTY(ExpressionObj, default_value) ADD_PROPERTY(bool, is_rest_parameter) public: Parameter(SourceSpan pstate, sass::string n, ExpressionObj def = {}, bool rest = false); ATTACH_AST_OPERATIONS(Parameter) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////////////////// // Parameter lists -- in their own class to facilitate context-sensitive // error checking (e.g., ensuring that all optional parameters follow all // required parameters). ///////////////////////////////////////////////////////////////////////// class Parameters final : public AST_Node, public Vectorized { ADD_PROPERTY(bool, has_optional_parameters) ADD_PROPERTY(bool, has_rest_parameter) protected: void adjust_after_pushing(Parameter_Obj p) override; public: Parameters(SourceSpan pstate); ATTACH_AST_OPERATIONS(Parameters) ATTACH_CRTP_PERFORM_METHODS() }; } #include "ast_values.hpp" #include "ast_supports.hpp" #include "ast_selectors.hpp" #ifdef __clang__ // #pragma clang diagnostic pop // #pragma clang diagnostic push #endif #endif golibsass-1.0.0/libsass_src/src/ast2c.cpp000066400000000000000000000043051405214413600203160ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast2c.hpp" #include "ast.hpp" namespace Sass { union Sass_Value* AST2C::operator()(Boolean* b) { return sass_make_boolean(b->value()); } union Sass_Value* AST2C::operator()(Number* n) { return sass_make_number(n->value(), n->unit().c_str()); } union Sass_Value* AST2C::operator()(Custom_Warning* w) { return sass_make_warning(w->message().c_str()); } union Sass_Value* AST2C::operator()(Custom_Error* e) { return sass_make_error(e->message().c_str()); } union Sass_Value* AST2C::operator()(Color_RGBA* c) { return sass_make_color(c->r(), c->g(), c->b(), c->a()); } union Sass_Value* AST2C::operator()(Color_HSLA* c) { Color_RGBA_Obj rgba = c->copyAsRGBA(); return operator()(rgba.ptr()); } union Sass_Value* AST2C::operator()(String_Constant* s) { if (s->quote_mark()) { return sass_make_qstring(s->value().c_str()); } else { return sass_make_string(s->value().c_str()); } } union Sass_Value* AST2C::operator()(String_Quoted* s) { return sass_make_qstring(s->value().c_str()); } union Sass_Value* AST2C::operator()(List* l) { union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { sass_list_set_value(v, i, (*l)[i]->perform(this)); } return v; } union Sass_Value* AST2C::operator()(Map* m) { union Sass_Value* v = sass_make_map(m->length()); int i = 0; for (auto key : m->keys()) { sass_map_set_key(v, i, key->perform(this)); sass_map_set_value(v, i, m->at(key)->perform(this)); i++; } return v; } union Sass_Value* AST2C::operator()(Arguments* a) { union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false); for (size_t i = 0, L = a->length(); i < L; ++i) { sass_list_set_value(v, i, (*a)[i]->perform(this)); } return v; } union Sass_Value* AST2C::operator()(Argument* a) { return a->value()->perform(this); } // not strictly necessary because of the fallback union Sass_Value* AST2C::operator()(Null* n) { return sass_make_null(); } }; golibsass-1.0.0/libsass_src/src/ast2c.hpp000066400000000000000000000017721405214413600203300ustar00rootroot00000000000000#ifndef SASS_AST2C_H #define SASS_AST2C_H #include "ast_fwd_decl.hpp" #include "operation.hpp" #include "sass/values.h" namespace Sass { class AST2C : public Operation_CRTP { public: AST2C() { } ~AST2C() { } union Sass_Value* operator()(Boolean*); union Sass_Value* operator()(Number*); union Sass_Value* operator()(Color_RGBA*); union Sass_Value* operator()(Color_HSLA*); union Sass_Value* operator()(String_Constant*); union Sass_Value* operator()(String_Quoted*); union Sass_Value* operator()(Custom_Warning*); union Sass_Value* operator()(Custom_Error*); union Sass_Value* operator()(List*); union Sass_Value* operator()(Map*); union Sass_Value* operator()(Null*); union Sass_Value* operator()(Arguments*); union Sass_Value* operator()(Argument*); // return sass error if type is not supported union Sass_Value* fallback(AST_Node* x) { return sass_make_error("unknown type for C-API"); } }; } #endif golibsass-1.0.0/libsass_src/src/ast_def_macros.hpp000066400000000000000000000073161405214413600222650ustar00rootroot00000000000000#ifndef SASS_AST_DEF_MACROS_H #define SASS_AST_DEF_MACROS_H // Helper class to switch a flag and revert once we go out of scope template class LocalOption { private: T* var; // pointer to original variable T orig; // copy of the original option public: LocalOption(T& var) { this->var = &var; this->orig = var; } LocalOption(T& var, T orig) { this->var = &var; this->orig = var; *(this->var) = orig; } void reset() { *(this->var) = this->orig; } ~LocalOption() { *(this->var) = this->orig; } }; #define LOCAL_FLAG(name,opt) LocalOption flag_##name(name, opt) #define LOCAL_COUNT(name,opt) LocalOption cnt_##name(name, opt) #define NESTING_GUARD(name) \ LocalOption cnt_##name(name, name + 1); \ if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \ #define ADD_PROPERTY(type, name)\ protected:\ type name##_;\ public:\ type name() const { return name##_; }\ type name(type name##__) { return name##_ = name##__; }\ private: #define HASH_PROPERTY(type, name)\ protected:\ type name##_;\ public:\ type name() const { return name##_; }\ type name(type name##__) { hash_ = 0; return name##_ = name##__; }\ private: #define ADD_CONSTREF(type, name) \ protected: \ type name##_; \ public: \ const type& name() const { return name##_; } \ void name(type name##__) { name##_ = name##__; } \ private: #define HASH_CONSTREF(type, name) \ protected: \ type name##_; \ public: \ const type& name() const { return name##_; } \ void name(type name##__) { hash_ = 0; name##_ = name##__; } \ private: #ifdef DEBUG_SHARED_PTR #define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \ virtual klass* copy(sass::string, size_t) const = 0; \ virtual klass* clone(sass::string, size_t) const = 0; \ #define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \ klass(const klass* ptr); \ virtual klass* copy(sass::string, size_t) const override = 0; \ virtual klass* clone(sass::string, size_t) const override = 0; \ #define ATTACH_AST_OPERATIONS(klass) \ klass(const klass* ptr); \ virtual klass* copy(sass::string, size_t) const override; \ virtual klass* clone(sass::string, size_t) const override; \ #else #define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \ virtual klass* copy() const = 0; \ virtual klass* clone() const = 0; \ #define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \ klass(const klass* ptr); \ virtual klass* copy() const override = 0; \ virtual klass* clone() const override = 0; \ #define ATTACH_AST_OPERATIONS(klass) \ klass(const klass* ptr); \ virtual klass* copy() const override; \ virtual klass* clone() const override; \ #endif #define ATTACH_VIRTUAL_CMP_OPERATIONS(klass) \ virtual bool operator==(const klass& rhs) const = 0; \ virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \ #define ATTACH_CMP_OPERATIONS(klass) \ virtual bool operator==(const klass& rhs) const; \ virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \ #ifdef DEBUG_SHARED_PTR #define IMPLEMENT_AST_OPERATORS(klass) \ klass* klass::copy(sass::string file, size_t line) const { \ klass* cpy = SASS_MEMORY_NEW(klass, this); \ cpy->trace(file, line); \ return cpy; \ } \ klass* klass::clone(sass::string file, size_t line) const { \ klass* cpy = copy(file, line); \ cpy->cloneChildren(); \ return cpy; \ } \ #else #define IMPLEMENT_AST_OPERATORS(klass) \ klass* klass::copy() const { \ return SASS_MEMORY_NEW(klass, this); \ } \ klass* klass::clone() const { \ klass* cpy = copy(); \ cpy->cloneChildren(); \ return cpy; \ } \ #endif #endif golibsass-1.0.0/libsass_src/src/ast_fwd_decl.cpp000066400000000000000000000013561405214413600217230ustar00rootroot00000000000000#include "ast.hpp" namespace Sass { #define IMPLEMENT_BASE_CAST(T) \ template<> \ T* Cast(AST_Node* ptr) { \ return dynamic_cast(ptr); \ }; \ \ template<> \ const T* Cast(const AST_Node* ptr) { \ return dynamic_cast(ptr); \ }; \ IMPLEMENT_BASE_CAST(AST_Node) IMPLEMENT_BASE_CAST(Expression) IMPLEMENT_BASE_CAST(Statement) IMPLEMENT_BASE_CAST(ParentStatement) IMPLEMENT_BASE_CAST(PreValue) IMPLEMENT_BASE_CAST(Value) IMPLEMENT_BASE_CAST(Color) IMPLEMENT_BASE_CAST(List) IMPLEMENT_BASE_CAST(String) IMPLEMENT_BASE_CAST(String_Constant) IMPLEMENT_BASE_CAST(SupportsCondition) IMPLEMENT_BASE_CAST(Selector) IMPLEMENT_BASE_CAST(SelectorComponent) IMPLEMENT_BASE_CAST(SimpleSelector) } golibsass-1.0.0/libsass_src/src/ast_fwd_decl.hpp000066400000000000000000000151321405214413600217250ustar00rootroot00000000000000#ifndef SASS_AST_FWD_DECL_H #define SASS_AST_FWD_DECL_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "memory.hpp" #include "sass/functions.h" ///////////////////////////////////////////// // Forward declarations for the AST visitors. ///////////////////////////////////////////// namespace Sass { class SourceData; class SourceFile; class SynthFile; class ItplFile; class AST_Node; class ParentStatement; class SimpleSelector; class Parent_Reference; class PreValue; class Block; class Expression; class Statement; class Value; class Declaration; class StyleRule; class Bubble; class Trace; class MediaRule; class CssMediaRule; class CssMediaQuery; class SupportsRule; class AtRule; class Keyframe_Rule; class AtRootRule; class Assignment; class Import; class Import_Stub; class WarningRule; class ErrorRule; class DebugRule; class Comment; class If; class ForRule; class EachRule; class WhileRule; class Return; class Content; class ExtendRule; class Definition; class List; class Map; class Function; class Mixin_Call; class Binary_Expression; class Unary_Expression; class Function_Call; class Custom_Warning; class Custom_Error; class Variable; class Number; class Color; class Color_RGBA; class Color_HSLA; class Boolean; class String; class Null; class String_Schema; class String_Constant; class String_Quoted; class Media_Query; class Media_Query_Expression; class SupportsCondition; class SupportsOperation; class SupportsNegation; class SupportsDeclaration; class Supports_Interpolation; class At_Root_Query; class Parameter; class Parameters; class Argument; class Arguments; class Selector; class Selector_Schema; class PlaceholderSelector; class TypeSelector; class ClassSelector; class IDSelector; class AttributeSelector; class PseudoSelector; class SelectorComponent; class SelectorCombinator; class CompoundSelector; class ComplexSelector; class SelectorList; // common classes class Context; class Expand; class Eval; class Extension; // declare classes that are instances of memory nodes // Note: also add a mapping without underscore // ToDo: move to camelCase vars in the future #define IMPL_MEM_OBJ(type) \ typedef SharedImpl type##Obj; \ typedef SharedImpl type##_Obj; \ IMPL_MEM_OBJ(SourceData); IMPL_MEM_OBJ(SourceFile); IMPL_MEM_OBJ(SynthFile); IMPL_MEM_OBJ(ItplFile); IMPL_MEM_OBJ(AST_Node); IMPL_MEM_OBJ(Statement); IMPL_MEM_OBJ(Block); IMPL_MEM_OBJ(StyleRule); IMPL_MEM_OBJ(Bubble); IMPL_MEM_OBJ(Trace); IMPL_MEM_OBJ(MediaRule); IMPL_MEM_OBJ(CssMediaRule); IMPL_MEM_OBJ(CssMediaQuery); IMPL_MEM_OBJ(SupportsRule); IMPL_MEM_OBJ(AtRule); IMPL_MEM_OBJ(Keyframe_Rule); IMPL_MEM_OBJ(AtRootRule); IMPL_MEM_OBJ(Declaration); IMPL_MEM_OBJ(Assignment); IMPL_MEM_OBJ(Import); IMPL_MEM_OBJ(Import_Stub); IMPL_MEM_OBJ(WarningRule); IMPL_MEM_OBJ(ErrorRule); IMPL_MEM_OBJ(DebugRule); IMPL_MEM_OBJ(Comment); IMPL_MEM_OBJ(PreValue); IMPL_MEM_OBJ(ParentStatement); IMPL_MEM_OBJ(If); IMPL_MEM_OBJ(ForRule); IMPL_MEM_OBJ(EachRule); IMPL_MEM_OBJ(WhileRule); IMPL_MEM_OBJ(Return); IMPL_MEM_OBJ(Content); IMPL_MEM_OBJ(ExtendRule); IMPL_MEM_OBJ(Definition); IMPL_MEM_OBJ(Mixin_Call); IMPL_MEM_OBJ(Value); IMPL_MEM_OBJ(Expression); IMPL_MEM_OBJ(List); IMPL_MEM_OBJ(Map); IMPL_MEM_OBJ(Function); IMPL_MEM_OBJ(Binary_Expression); IMPL_MEM_OBJ(Unary_Expression); IMPL_MEM_OBJ(Function_Call); IMPL_MEM_OBJ(Custom_Warning); IMPL_MEM_OBJ(Custom_Error); IMPL_MEM_OBJ(Variable); IMPL_MEM_OBJ(Number); IMPL_MEM_OBJ(Color); IMPL_MEM_OBJ(Color_RGBA); IMPL_MEM_OBJ(Color_HSLA); IMPL_MEM_OBJ(Boolean); IMPL_MEM_OBJ(String_Schema); IMPL_MEM_OBJ(String); IMPL_MEM_OBJ(String_Constant); IMPL_MEM_OBJ(String_Quoted); IMPL_MEM_OBJ(Media_Query); IMPL_MEM_OBJ(Media_Query_Expression); IMPL_MEM_OBJ(SupportsCondition); IMPL_MEM_OBJ(SupportsOperation); IMPL_MEM_OBJ(SupportsNegation); IMPL_MEM_OBJ(SupportsDeclaration); IMPL_MEM_OBJ(Supports_Interpolation); IMPL_MEM_OBJ(At_Root_Query); IMPL_MEM_OBJ(Null); IMPL_MEM_OBJ(Parent_Reference); IMPL_MEM_OBJ(Parameter); IMPL_MEM_OBJ(Parameters); IMPL_MEM_OBJ(Argument); IMPL_MEM_OBJ(Arguments); IMPL_MEM_OBJ(Selector); IMPL_MEM_OBJ(Selector_Schema); IMPL_MEM_OBJ(SimpleSelector); IMPL_MEM_OBJ(PlaceholderSelector); IMPL_MEM_OBJ(TypeSelector); IMPL_MEM_OBJ(ClassSelector); IMPL_MEM_OBJ(IDSelector); IMPL_MEM_OBJ(AttributeSelector); IMPL_MEM_OBJ(PseudoSelector); IMPL_MEM_OBJ(SelectorComponent); IMPL_MEM_OBJ(SelectorCombinator); IMPL_MEM_OBJ(CompoundSelector); IMPL_MEM_OBJ(ComplexSelector); IMPL_MEM_OBJ(SelectorList); // ########################################################################### // some often used typedefs // ########################################################################### typedef sass::vector BlockStack; typedef sass::vector CalleeStack; typedef sass::vector CallStack; typedef sass::vector MediaStack; typedef sass::vector SelectorStack; typedef sass::vector ImporterStack; // only to switch implementations for testing #define environment_map std::map // ########################################################################### // explicit type conversion functions // ########################################################################### template T* Cast(AST_Node* ptr); template const T* Cast(const AST_Node* ptr); // sometimes you know the class you want to cast to is final // in this case a simple typeid check is faster and safe to use #define DECLARE_BASE_CAST(T) \ template<> T* Cast(AST_Node* ptr); \ template<> const T* Cast(const AST_Node* ptr); \ // ########################################################################### // implement specialization for final classes // ########################################################################### DECLARE_BASE_CAST(AST_Node) DECLARE_BASE_CAST(Expression) DECLARE_BASE_CAST(Statement) DECLARE_BASE_CAST(ParentStatement) DECLARE_BASE_CAST(PreValue) DECLARE_BASE_CAST(Value) DECLARE_BASE_CAST(List) DECLARE_BASE_CAST(Color) DECLARE_BASE_CAST(String) DECLARE_BASE_CAST(String_Constant) DECLARE_BASE_CAST(SupportsCondition) DECLARE_BASE_CAST(Selector) DECLARE_BASE_CAST(SimpleSelector) DECLARE_BASE_CAST(SelectorComponent) } #endif golibsass-1.0.0/libsass_src/src/ast_helpers.hpp000066400000000000000000000240731405214413600216240ustar00rootroot00000000000000#ifndef SASS_AST_HELPERS_H #define SASS_AST_HELPERS_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "util_string.hpp" namespace Sass { // ########################################################################### // ########################################################################### // easier to search with name const bool DELAYED = true; // ToDo: should this really be hardcoded // Note: most methods follow precision option const double NUMBER_EPSILON = 1e-12; // macro to test if numbers are equal within a small error margin #define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON // ########################################################################### // We define various functions and functors here. // Functions satisfy the BinaryPredicate requirement // Functors are structs used for e.g. unordered_map // ########################################################################### // ########################################################################### // Implement compare and hashing operations for raw pointers // ########################################################################### template size_t PtrHashFn(const T* ptr) { return std::hash()((size_t)ptr); } struct PtrHash { template size_t operator() (const T* ptr) const { return PtrHashFn(ptr); } }; template bool PtrEqualityFn(const T* lhs, const T* rhs) { return lhs == rhs; // compare raw pointers } struct PtrEquality { template bool operator() (const T* lhs, const T* rhs) const { return PtrEqualityFn(lhs, rhs); } }; // ########################################################################### // Implement compare and hashing operations for AST Nodes // ########################################################################### // TODO: get rid of functions and use ObjEquality template // Hash the raw pointer instead of object size_t ObjPtrHashFn(const T& obj) { return PtrHashFn(obj.ptr()); } struct ObjPtrHash { template // Hash the raw pointer instead of object size_t operator() (const T& obj) const { return ObjPtrHashFn(obj); } }; template // Hash the object and its content size_t ObjHashFn(const T& obj) { return obj ? obj->hash() : 0; } struct ObjHash { template // Hash the object and its content size_t operator() (const T& obj) const { return ObjHashFn(obj); } }; template // Hash the object behind pointer size_t PtrObjHashFn(const T* obj) { return obj ? obj->hash() : 0; } struct PtrObjHash { template // Hash the object behind pointer size_t operator() (const T* obj) const { return PtrObjHashFn(obj); } }; template // Compare raw pointers to the object bool ObjPtrEqualityFn(const T& lhs, const T& rhs) { return PtrEqualityFn(lhs.ptr(), rhs.ptr()); } struct ObjPtrEquality { template // Compare raw pointers to the object bool operator() (const T& lhs, const T& rhs) const { return ObjPtrEqualityFn(lhs, rhs); } }; template // Compare the objects behind the pointers bool PtrObjEqualityFn(const T* lhs, const T* rhs) { if (lhs == nullptr) return rhs == nullptr; else if (rhs == nullptr) return false; else return *lhs == *rhs; } struct PtrObjEquality { template // Compare the objects behind the pointers bool operator() (const T* lhs, const T* rhs) const { return PtrObjEqualityFn(lhs, rhs); } }; template // Compare the objects and its contents bool ObjEqualityFn(const T& lhs, const T& rhs) { return PtrObjEqualityFn(lhs.ptr(), rhs.ptr()); } struct ObjEquality { template // Compare the objects and its contents bool operator() (const T& lhs, const T& rhs) const { return ObjEqualityFn(lhs, rhs); } }; // ########################################################################### // Special compare function only for hashes. // We need to make sure to not have objects equal that // have different hashes. This is currently an issue, // since `1px` is equal to `1` but have different hashes. // This goes away once we remove unitless equality. // ########################################################################### template // Compare the objects and its hashes bool ObjHashEqualityFn(const T& lhs, const T& rhs) { if (lhs == nullptr) return rhs == nullptr; else if (rhs == nullptr) return false; else return lhs->hash() == rhs->hash(); } struct ObjHashEquality { template // Compare the objects and its contents and hashes bool operator() (const T& lhs, const T& rhs) const { return ObjEqualityFn(lhs, rhs) && ObjHashEqualityFn(lhs, rhs); } }; // ########################################################################### // Implement ordering operations for AST Nodes // ########################################################################### template // Compare the objects behind pointers bool PtrObjLessThanFn(const T* lhs, const T* rhs) { if (lhs == nullptr) return rhs != nullptr; else if (rhs == nullptr) return false; else return *lhs < *rhs; } struct PtrObjLessThan { template // Compare the objects behind pointers bool operator() (const T* lhs, const T* rhs) const { return PtrObjLessThanFn(lhs, rhs); } }; template // Compare the objects and its content bool ObjLessThanFn(const T& lhs, const T& rhs) { return PtrObjLessThanFn(lhs.ptr(), rhs.ptr()); }; struct ObjLessThan { template // Compare the objects and its content bool operator() (const T& lhs, const T& rhs) const { return ObjLessThanFn(lhs, rhs); } }; // ########################################################################### // Some STL helper functions // ########################################################################### // Check if all elements are equal template bool ListEquality(const X& lhs, const Y& rhs, bool(*cmp)(const XT*, const YT*)) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin(), cmp); } // Return if Vector is empty template bool listIsEmpty(T* cnt) { return cnt && cnt->empty(); } // Erase items from vector that match predicate template void listEraseItemIf(T& vec, UnaryPredicate* predicate) { vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end()); } // Check that every item in `lhs` is also in `rhs` // Note: this works by comparing the raw pointers template bool listIsSubsetOrEqual(const T& lhs, const T& rhs) { for (const auto& item : lhs) { if (std::find(rhs.begin(), rhs.end(), item) == rhs.end()) return false; } return true; } // ########################################################################## // Returns whether [name] is the name of a pseudo-element // that can be written with pseudo-class syntax (CSS2 vs CSS3): // `:before`, `:after`, `:first-line`, or `:first-letter` // ########################################################################## inline bool isFakePseudoElement(const sass::string& name) { return Util::equalsLiteral("after", name) || Util::equalsLiteral("before", name) || Util::equalsLiteral("first-line", name) || Util::equalsLiteral("first-letter", name); } // ########################################################################## // Names of pseudo selectors that take selectors as arguments, // and that are subselectors of their arguments. // For example, `.foo` is a superselector of `:matches(.foo)`. // ########################################################################## inline bool isSubselectorPseudo(const sass::string& norm) { return Util::equalsLiteral("any", norm) || Util::equalsLiteral("matches", norm) || Util::equalsLiteral("nth-child", norm) || Util::equalsLiteral("nth-last-child", norm); } // EO isSubselectorPseudo // ########################################################################### // Pseudo-class selectors that take unadorned selectors as arguments. // ########################################################################### inline bool isSelectorPseudoClass(const sass::string& test) { return Util::equalsLiteral("not", test) || Util::equalsLiteral("matches", test) || Util::equalsLiteral("current", test) || Util::equalsLiteral("any", test) || Util::equalsLiteral("has", test) || Util::equalsLiteral("host", test) || Util::equalsLiteral("host-context", test); } // EO isSelectorPseudoClass // ########################################################################### // Pseudo-element selectors that take unadorned selectors as arguments. // ########################################################################### inline bool isSelectorPseudoElement(const sass::string& test) { return Util::equalsLiteral("slotted", test); } // EO isSelectorPseudoElement // ########################################################################### // Pseudo-element selectors that has binominals // ########################################################################### inline bool isSelectorPseudoBinominal(const sass::string& test) { return Util::equalsLiteral("nth-child", test) || Util::equalsLiteral("nth-last-child", test); } // isSelectorPseudoBinominal // ########################################################################### // ########################################################################### } #endif golibsass-1.0.0/libsass_src/src/ast_sel_cmp.cpp000066400000000000000000000326001405214413600215720ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast_selectors.hpp" namespace Sass { /*#########################################################################*/ // Compare against base class on right hand side // try to find the most specialized implementation /*#########################################################################*/ // Selector lists can be compared to comma lists bool SelectorList::operator== (const Expression& rhs) const { if (auto l = Cast(&rhs)) { return *this == *l; } if (auto s = Cast(&rhs)) { return *this == *s; } if (Cast(&rhs) || Cast(&rhs)) { return false; } throw std::runtime_error("invalid selector base classes to compare"); } // Selector lists can be compared to comma lists bool SelectorList::operator== (const Selector& rhs) const { if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto list = Cast(&rhs)) { return *this == *list; } throw std::runtime_error("invalid selector base classes to compare"); } bool ComplexSelector::operator== (const Selector& rhs) const { if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *sel == *this; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } throw std::runtime_error("invalid selector base classes to compare"); } bool SelectorCombinator::operator== (const Selector& rhs) const { if (auto cpx = Cast(&rhs)) { return *this == *cpx; } return false; } bool CompoundSelector::operator== (const Selector& rhs) const { if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } throw std::runtime_error("invalid selector base classes to compare"); } bool SimpleSelector::operator== (const Selector& rhs) const { if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) { return *this == *sel; } if (auto sel = Cast(&rhs)) return *this == *sel; throw std::runtime_error("invalid selector base classes to compare"); } /*#########################################################################*/ /*#########################################################################*/ bool SelectorList::operator== (const SelectorList& rhs) const { if (&rhs == this) return true; if (rhs.length() != length()) return false; std::unordered_set lhs_set; lhs_set.reserve(length()); for (const ComplexSelectorObj& element : elements()) { lhs_set.insert(element.ptr()); } for (const ComplexSelectorObj& element : rhs.elements()) { if (lhs_set.find(element.ptr()) == lhs_set.end()) return false; } return true; } /*#########################################################################*/ // Compare SelectorList against all other selector types /*#########################################################################*/ bool SelectorList::operator== (const ComplexSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (length() != 1) return false; // Compare simple selectors return *get(0) == rhs; } bool SelectorList::operator== (const CompoundSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (length() != 1) return false; // Compare simple selectors return *get(0) == rhs; } bool SelectorList::operator== (const SimpleSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (length() != 1) return false; // Compare simple selectors return *get(0) == rhs; } /*#########################################################################*/ // Compare ComplexSelector against itself /*#########################################################################*/ bool ComplexSelector::operator== (const ComplexSelector& rhs) const { size_t len = length(); size_t rlen = rhs.length(); if (len != rlen) return false; for (size_t i = 0; i < len; i += 1) { if (*get(i) != *rhs.get(i)) return false; } return true; } /*#########################################################################*/ // Compare ComplexSelector against all other selector types /*#########################################################################*/ bool ComplexSelector::operator== (const SelectorList& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (rhs.length() != 1) return false; // Compare complex selector return *this == *rhs.get(0); } bool ComplexSelector::operator== (const CompoundSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (length() != 1) return false; // Compare compound selector return *get(0) == rhs; } bool ComplexSelector::operator== (const SimpleSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (length() != 1) return false; // Compare simple selectors return *get(0) == rhs; } /*#########################################################################*/ // Compare SelectorCombinator against itself /*#########################################################################*/ bool SelectorCombinator::operator==(const SelectorCombinator& rhs) const { return combinator() == rhs.combinator(); } /*#########################################################################*/ // Compare SelectorCombinator against SelectorComponent /*#########################################################################*/ bool SelectorCombinator::operator==(const SelectorComponent& rhs) const { if (const SelectorCombinator * sel = rhs.getCombinator()) { return *this == *sel; } return false; } bool CompoundSelector::operator==(const SelectorComponent& rhs) const { if (const CompoundSelector * sel = rhs.getCompound()) { return *this == *sel; } return false; } /*#########################################################################*/ // Compare CompoundSelector against itself /*#########################################################################*/ // ToDo: Verifiy implementation /*#########################################################################*/ bool CompoundSelector::operator== (const CompoundSelector& rhs) const { // std::cerr << "comp vs comp\n"; if (&rhs == this) return true; if (rhs.length() != length()) return false; std::unordered_set lhs_set; lhs_set.reserve(length()); for (const SimpleSelectorObj& element : elements()) { lhs_set.insert(element.ptr()); } // there is no break?! for (const SimpleSelectorObj& element : rhs.elements()) { if (lhs_set.find(element.ptr()) == lhs_set.end()) return false; } return true; } /*#########################################################################*/ // Compare CompoundSelector against all other selector types /*#########################################################################*/ bool CompoundSelector::operator== (const SelectorList& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (rhs.length() != 1) return false; // Compare complex selector return *this == *rhs.get(0); } bool CompoundSelector::operator== (const ComplexSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (rhs.length() != 1) return false; // Compare compound selector return *this == *rhs.get(0); } bool CompoundSelector::operator== (const SimpleSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return false; // Must have exactly one item size_t rlen = length(); if (rlen > 1) return false; if (rlen == 0) return true; // Compare simple selectors return *get(0) < rhs; } /*#########################################################################*/ // Compare SimpleSelector against itself (upcast from abstract base) /*#########################################################################*/ // DOES NOT EXIST FOR ABSTRACT BASE CLASS /*#########################################################################*/ // Compare SimpleSelector against all other selector types /*#########################################################################*/ bool SimpleSelector::operator== (const SelectorList& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (rhs.length() != 1) return false; // Compare complex selector return *this == *rhs.get(0); } bool SimpleSelector::operator== (const ComplexSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return true; // Must have exactly one item if (rhs.length() != 1) return false; // Compare compound selector return *this == *rhs.get(0); } bool SimpleSelector::operator== (const CompoundSelector& rhs) const { // If both are empty they are equal if (empty() && rhs.empty()) return false; // Must have exactly one item if (rhs.length() != 1) return false; // Compare simple selector return *this == *rhs.get(0); } /*#########################################################################*/ /*#########################################################################*/ bool IDSelector::operator== (const SimpleSelector& rhs) const { auto sel = Cast(&rhs); return sel ? *this == *sel : false; } bool TypeSelector::operator== (const SimpleSelector& rhs) const { auto sel = Cast(&rhs); return sel ? *this == *sel : false; } bool ClassSelector::operator== (const SimpleSelector& rhs) const { auto sel = Cast(&rhs); return sel ? *this == *sel : false; } bool PseudoSelector::operator== (const SimpleSelector& rhs) const { auto sel = Cast(&rhs); return sel ? *this == *sel : false; } bool AttributeSelector::operator== (const SimpleSelector& rhs) const { auto sel = Cast(&rhs); return sel ? *this == *sel : false; } bool PlaceholderSelector::operator== (const SimpleSelector& rhs) const { auto sel = Cast(&rhs); return sel ? *this == *sel : false; } /*#########################################################################*/ /*#########################################################################*/ bool IDSelector::operator== (const IDSelector& rhs) const { // ID has no namespacing return name() == rhs.name(); } bool TypeSelector::operator== (const TypeSelector& rhs) const { return is_ns_eq(rhs) && name() == rhs.name(); } bool ClassSelector::operator== (const ClassSelector& rhs) const { // Class has no namespacing return name() == rhs.name(); } bool PlaceholderSelector::operator== (const PlaceholderSelector& rhs) const { // Placeholder has no namespacing return name() == rhs.name(); } bool AttributeSelector::operator== (const AttributeSelector& rhs) const { // smaller return, equal go on, bigger abort if (is_ns_eq(rhs)) { if (name() != rhs.name()) return false; if (matcher() != rhs.matcher()) return false; if (modifier() != rhs.modifier()) return false; const String* lhs_val = value(); const String* rhs_val = rhs.value(); return PtrObjEquality()(lhs_val, rhs_val); } else { return false; } } bool PseudoSelector::operator== (const PseudoSelector& rhs) const { if (is_ns_eq(rhs)) { if (name() != rhs.name()) return false; if (isElement() != rhs.isElement()) return false; const String* lhs_arg = argument(); const String* rhs_arg = rhs.argument(); if (!PtrObjEquality()(lhs_arg, rhs_arg)) return false; const SelectorList* lhs_sel = selector(); const SelectorList* rhs_sel = rhs.selector(); return PtrObjEquality()(lhs_sel, rhs_sel); } else { return false; } } /*#########################################################################*/ /*#########################################################################*/ } golibsass-1.0.0/libsass_src/src/ast_sel_super.cpp000066400000000000000000000524271405214413600221620ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "util_string.hpp" namespace Sass { // ########################################################################## // To compare/debug against libsass you can use debugger.hpp: // c++: std::cerr << "result " << debug_vec(compound) << "\n"; // dart: stderr.writeln("result " + compound.toString()); // ########################################################################## // ########################################################################## // Returns whether [list1] is a superselector of [list2]. // That is, whether [list1] matches every element that // [list2] matches, as well as possibly additional elements. // ########################################################################## bool listIsSuperslector( const sass::vector& list1, const sass::vector& list2); // ########################################################################## // Returns whether [complex1] is a superselector of [complex2]. // That is, whether [complex1] matches every element that // [complex2] matches, as well as possibly additional elements. // ########################################################################## bool complexIsSuperselector( const sass::vector& complex1, const sass::vector& complex2); // ########################################################################## // Returns all pseudo selectors in [compound] that have // a selector argument, and that have the given [name]. // ########################################################################## sass::vector selectorPseudoNamed( CompoundSelectorObj compound, sass::string name) { sass::vector rv; for (SimpleSelectorObj sel : compound->elements()) { if (PseudoSelectorObj pseudo = Cast(sel)) { if (pseudo->isClass() && pseudo->selector()) { if (sel->name() == name) { rv.push_back(sel); } } } } return rv; } // EO selectorPseudoNamed // ########################################################################## // Returns whether [simple1] is a superselector of [simple2]. // That is, whether [simple1] matches every element that // [simple2] matches, as well as possibly additional elements. // ########################################################################## bool simpleIsSuperselector( const SimpleSelectorObj& simple1, const SimpleSelectorObj& simple2) { // If they are equal they are superselectors if (ObjEqualityFn(simple1, simple2)) { return true; } // Some selector pseudoclasses can match normal selectors. if (const PseudoSelector* pseudo = Cast(simple2)) { if (pseudo->selector() && isSubselectorPseudo(pseudo->normalized())) { for (auto complex : pseudo->selector()->elements()) { // Make sure we have exacly one items if (complex->length() != 1) { return false; } // That items must be a compound selector if (auto compound = Cast(complex->at(0))) { // It must contain the lhs simple selector if (!compound->contains(simple1)) { return false; } } } return true; } } return false; } // EO simpleIsSuperselector // ########################################################################## // Returns whether [simple] is a superselector of [compound]. // That is, whether [simple] matches every element that // [compound] matches, as well as possibly additional elements. // ########################################################################## bool simpleIsSuperselectorOfCompound( const SimpleSelectorObj& simple, const CompoundSelectorObj& compound) { for (SimpleSelectorObj simple2 : compound->elements()) { if (simpleIsSuperselector(simple, simple2)) { return true; } } return false; } // EO simpleIsSuperselectorOfCompound // ########################################################################## // ########################################################################## bool typeIsSuperselectorOfCompound( const TypeSelectorObj& type, const CompoundSelectorObj& compound) { for (const SimpleSelectorObj& simple : compound->elements()) { if (const TypeSelectorObj& rhs = Cast(simple)) { if (*type != *rhs) return true; } } return false; } // EO typeIsSuperselectorOfCompound // ########################################################################## // ########################################################################## bool idIsSuperselectorOfCompound( const IDSelectorObj& id, const CompoundSelectorObj& compound) { for (const SimpleSelectorObj& simple : compound->elements()) { if (const IDSelectorObj& rhs = Cast(simple)) { if (*id != *rhs) return true; } } return false; } // EO idIsSuperselectorOfCompound // ########################################################################## // ########################################################################## bool pseudoIsSuperselectorOfPseudo( const PseudoSelectorObj& pseudo1, const PseudoSelectorObj& pseudo2, const ComplexSelectorObj& parent ) { if (!pseudo2->selector()) return false; if (pseudo1->name() == pseudo2->name()) { SelectorListObj list = pseudo2->selector(); return listIsSuperslector(list->elements(), { parent }); } return false; } // EO pseudoIsSuperselectorOfPseudo // ########################################################################## // ########################################################################## bool pseudoNotIsSuperselectorOfCompound( const PseudoSelectorObj& pseudo1, const CompoundSelectorObj& compound2, const ComplexSelectorObj& parent) { for (const SimpleSelectorObj& simple2 : compound2->elements()) { if (const TypeSelectorObj& type2 = Cast(simple2)) { if (const CompoundSelectorObj& compound1 = Cast(parent->last())) { if (typeIsSuperselectorOfCompound(type2, compound1)) return true; } } else if (const IDSelectorObj& id2 = Cast(simple2)) { if (const CompoundSelectorObj& compound1 = Cast(parent->last())) { if (idIsSuperselectorOfCompound(id2, compound1)) return true; } } else if (const PseudoSelectorObj& pseudo2 = Cast(simple2)) { if (pseudoIsSuperselectorOfPseudo(pseudo1, pseudo2, parent)) return true; } } return false; } // pseudoNotIsSuperselectorOfCompound // ########################################################################## // Returns whether [pseudo1] is a superselector of [compound2]. // That is, whether [pseudo1] matches every element that [compound2] // matches, as well as possibly additional elements. This assumes that // [pseudo1]'s `selector` argument is not `null`. If [parents] is passed, // it represents the parents of [compound2]. This is relevant for pseudo // selectors with selector arguments, where we may need to know if the // parent selectors in the selector argument match [parents]. // ########################################################################## bool selectorPseudoIsSuperselector( const PseudoSelectorObj& pseudo1, const CompoundSelectorObj& compound2, // ToDo: is this really the most convenient way to do this? sass::vector::const_iterator parents_from, sass::vector::const_iterator parents_to) { // ToDo: move normalization function sass::string name(Util::unvendor(pseudo1->name())); if (name == "matches" || name == "any") { sass::vector pseudos = selectorPseudoNamed(compound2, pseudo1->name()); SelectorListObj selector1 = pseudo1->selector(); for (PseudoSelectorObj pseudo2 : pseudos) { SelectorListObj selector = pseudo2->selector(); if (selector1->isSuperselectorOf(selector)) { return true; } } for (ComplexSelectorObj complex1 : selector1->elements()) { sass::vector parents; for (auto cur = parents_from; cur != parents_to; cur++) { parents.push_back(*cur); } parents.push_back(compound2); if (complexIsSuperselector(complex1->elements(), parents)) { return true; } } } else if (name == "has" || name == "host" || name == "host-context" || name == "slotted") { sass::vector pseudos = selectorPseudoNamed(compound2, pseudo1->name()); SelectorListObj selector1 = pseudo1->selector(); for (PseudoSelectorObj pseudo2 : pseudos) { SelectorListObj selector = pseudo2->selector(); if (selector1->isSuperselectorOf(selector)) { return true; } } } else if (name == "not") { for (ComplexSelectorObj complex : pseudo1->selector()->elements()) { if (!pseudoNotIsSuperselectorOfCompound(pseudo1, compound2, complex)) return false; } return true; } else if (name == "current") { sass::vector pseudos = selectorPseudoNamed(compound2, "current"); for (PseudoSelectorObj pseudo2 : pseudos) { if (ObjEqualityFn(pseudo1, pseudo2)) return true; } } else if (name == "nth-child" || name == "nth-last-child") { for (auto simple2 : compound2->elements()) { if (PseudoSelectorObj pseudo2 = simple2->getPseudoSelector()) { if (pseudo1->name() != pseudo2->name()) continue; if (!ObjEqualityFn(pseudo1->argument(), pseudo2->argument())) continue; if (pseudo1->selector()->isSuperselectorOf(pseudo2->selector())) return true; } } return false; } return false; } // EO selectorPseudoIsSuperselector // ########################################################################## // Returns whether [compound1] is a superselector of [compound2]. // That is, whether [compound1] matches every element that [compound2] // matches, as well as possibly additional elements. If [parents] is // passed, it represents the parents of [compound2]. This is relevant // for pseudo selectors with selector arguments, where we may need to // know if the parent selectors in the selector argument match [parents]. // ########################################################################## bool compoundIsSuperselector( const CompoundSelectorObj& compound1, const CompoundSelectorObj& compound2, // ToDo: is this really the most convenient way to do this? const sass::vector::const_iterator parents_from, const sass::vector::const_iterator parents_to) { // Every selector in [compound1.components] must have // a matching selector in [compound2.components]. for (SimpleSelectorObj simple1 : compound1->elements()) { PseudoSelectorObj pseudo1 = Cast(simple1); if (pseudo1 && pseudo1->selector()) { if (!selectorPseudoIsSuperselector(pseudo1, compound2, parents_from, parents_to)) { return false; } } else if (!simpleIsSuperselectorOfCompound(simple1, compound2)) { return false; } } // [compound1] can't be a superselector of a selector // with pseudo-elements that [compound2] doesn't share. for (SimpleSelectorObj simple2 : compound2->elements()) { PseudoSelectorObj pseudo2 = Cast(simple2); if (pseudo2 && pseudo2->isElement()) { if (!simpleIsSuperselectorOfCompound(pseudo2, compound1)) { return false; } } } return true; } // EO compoundIsSuperselector // ########################################################################## // Returns whether [compound1] is a superselector of [compound2]. // That is, whether [compound1] matches every element that [compound2] // matches, as well as possibly additional elements. If [parents] is // passed, it represents the parents of [compound2]. This is relevant // for pseudo selectors with selector arguments, where we may need to // know if the parent selectors in the selector argument match [parents]. // ########################################################################## bool compoundIsSuperselector( const CompoundSelectorObj& compound1, const CompoundSelectorObj& compound2, const sass::vector& parents) { return compoundIsSuperselector( compound1, compound2, parents.begin(), parents.end() ); } // EO compoundIsSuperselector // ########################################################################## // Returns whether [complex1] is a superselector of [complex2]. // That is, whether [complex1] matches every element that // [complex2] matches, as well as possibly additional elements. // ########################################################################## bool complexIsSuperselector( const sass::vector& complex1, const sass::vector& complex2) { // Selectors with trailing operators are neither superselectors nor subselectors. if (!complex1.empty() && Cast(complex1.back())) return false; if (!complex2.empty() && Cast(complex2.back())) return false; size_t i1 = 0, i2 = 0; while (true) { size_t remaining1 = complex1.size() - i1; size_t remaining2 = complex2.size() - i2; if (remaining1 == 0 || remaining2 == 0) { return false; } // More complex selectors are never // superselectors of less complex ones. if (remaining1 > remaining2) { return false; } // Selectors with leading operators are // neither superselectors nor subselectors. if (Cast(complex1[i1])) { return false; } if (Cast(complex2[i2])) { return false; } CompoundSelectorObj compound1 = Cast(complex1[i1]); CompoundSelectorObj compound2 = Cast(complex2.back()); if (remaining1 == 1) { sass::vector::const_iterator parents_to = complex2.end(); sass::vector::const_iterator parents_from = complex2.begin(); std::advance(parents_from, i2 + 1); // equivalent to dart `.skip(i2 + 1)` bool rv = compoundIsSuperselector(compound1, compound2, parents_from, parents_to); sass::vector pp; sass::vector::const_iterator end = parents_to; sass::vector::const_iterator beg = parents_from; while (beg != end) { pp.push_back(*beg); beg++; } return rv; } // Find the first index where `complex2.sublist(i2, afterSuperselector)` // is a subselector of [compound1]. We stop before the superselector // would encompass all of [complex2] because we know [complex1] has // more than one element, and consuming all of [complex2] wouldn't // leave anything for the rest of [complex1] to match. size_t afterSuperselector = i2 + 1; for (; afterSuperselector < complex2.size(); afterSuperselector++) { SelectorComponentObj component2 = complex2[afterSuperselector - 1]; if (CompoundSelectorObj compound2 = Cast(component2)) { sass::vector::const_iterator parents_to = complex2.begin(); sass::vector::const_iterator parents_from = complex2.begin(); // complex2.take(afterSuperselector - 1).skip(i2 + 1) std::advance(parents_from, i2 + 1); // equivalent to dart `.skip` std::advance(parents_to, afterSuperselector); // equivalent to dart `.take` if (compoundIsSuperselector(compound1, compound2, parents_from, parents_to)) { break; } } } if (afterSuperselector == complex2.size()) { return false; } SelectorComponentObj component1 = complex1[i1 + 1], component2 = complex2[afterSuperselector]; SelectorCombinatorObj combinator1 = Cast(component1); SelectorCombinatorObj combinator2 = Cast(component2); if (!combinator1.isNull()) { if (combinator2.isNull()) { return false; } // `.a ~ .b` is a superselector of `.a + .b`, // but otherwise the combinators must match. if (combinator1->isGeneralCombinator()) { if (combinator2->isChildCombinator()) { return false; } } else if (*combinator1 != *combinator2) { return false; } // `.foo > .baz` is not a superselector of `.foo > .bar > .baz` or // `.foo > .bar .baz`, despite the fact that `.baz` is a superselector of // `.bar > .baz` and `.bar .baz`. Same goes for `+` and `~`. if (remaining1 == 3 && remaining2 > 3) { return false; } i1 += 2; i2 = afterSuperselector + 1; } else if (!combinator2.isNull()) { if (!combinator2->isChildCombinator()) { return false; } i1 += 1; i2 = afterSuperselector + 1; } else { i1 += 1; i2 = afterSuperselector; } } return false; } // EO complexIsSuperselector // ########################################################################## // Like [complexIsSuperselector], but compares [complex1] // and [complex2] as though they shared an implicit base // [SimpleSelector]. For example, `B` is not normally a // superselector of `B A`, since it doesn't match elements // that match `A`. However, it *is* a parent superselector, // since `B X` is a superselector of `B A X`. // ########################################################################## bool complexIsParentSuperselector( const sass::vector& complex1, const sass::vector& complex2) { // Try some simple heuristics to see if we can avoid allocations. if (complex1.empty() && complex2.empty()) return false; if (Cast(complex1.front())) return false; if (Cast(complex2.front())) return false; if (complex1.size() > complex2.size()) return false; // TODO(nweiz): There's got to be a way to do this without a bunch of extra allocations... sass::vector cplx1(complex1); sass::vector cplx2(complex2); CompoundSelectorObj base = SASS_MEMORY_NEW(CompoundSelector, "[tmp]"); cplx1.push_back(base); cplx2.push_back(base); return complexIsSuperselector(cplx1, cplx2); } // EO complexIsParentSuperselector // ########################################################################## // Returns whether [list] has a superselector for [complex]. // That is, whether an item in [list] matches every element that // [complex] matches, as well as possibly additional elements. // ########################################################################## bool listHasSuperslectorForComplex( sass::vector list, ComplexSelectorObj complex) { // Return true if every [complex] selector on [list2] // is a super selector of the full selector [list1]. for (ComplexSelectorObj lhs : list) { if (complexIsSuperselector(lhs->elements(), complex->elements())) { return true; } } return false; } // listIsSuperslectorOfComplex // ########################################################################## // Returns whether [list1] is a superselector of [list2]. // That is, whether [list1] matches every element that // [list2] matches, as well as possibly additional elements. // ########################################################################## bool listIsSuperslector( const sass::vector& list1, const sass::vector& list2) { // Return true if every [complex] selector on [list2] // is a super selector of the full selector [list1]. for (ComplexSelectorObj complex : list2) { if (!listHasSuperslectorForComplex(list1, complex)) { return false; } } return true; } // EO listIsSuperslector // ########################################################################## // Implement selector methods (dispatch to functions) // ########################################################################## bool SelectorList::isSuperselectorOf(const SelectorList* sub) const { return listIsSuperslector(elements(), sub->elements()); } bool ComplexSelector::isSuperselectorOf(const ComplexSelector* sub) const { return complexIsSuperselector(elements(), sub->elements()); } // ########################################################################## // ########################################################################## } golibsass-1.0.0/libsass_src/src/ast_sel_unify.cpp000066400000000000000000000237641405214413600221600ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" namespace Sass { // ########################################################################## // Returns the contents of a [SelectorList] that matches only // elements that are matched by both [complex1] and [complex2]. // If no such list can be produced, returns `null`. // ########################################################################## // ToDo: fine-tune API to avoid unnecessary wrapper allocations // ########################################################################## sass::vector> unifyComplex( const sass::vector>& complexes) { SASS_ASSERT(!complexes.empty(), "Can't unify empty list"); if (complexes.size() == 1) return complexes; CompoundSelectorObj unifiedBase = SASS_MEMORY_NEW(CompoundSelector, SourceSpan("[unify]")); for (auto complex : complexes) { SelectorComponentObj base = complex.back(); if (CompoundSelector * comp = base->getCompound()) { if (unifiedBase->empty()) { unifiedBase->concat(comp); } else { for (SimpleSelectorObj simple : comp->elements()) { unifiedBase = simple->unifyWith(unifiedBase); if (unifiedBase.isNull()) return {}; } } } else { return {}; } } sass::vector> complexesWithoutBases; for (size_t i = 0; i < complexes.size(); i += 1) { sass::vector sel = complexes[i]; sel.pop_back(); // remove last item (base) from the list complexesWithoutBases.push_back(std::move(sel)); } complexesWithoutBases.back().push_back(unifiedBase); return weave(complexesWithoutBases); } // EO unifyComplex // ########################################################################## // Returns a [CompoundSelector] that matches only elements // that are matched by both [compound1] and [compound2]. // If no such selector can be produced, returns `null`. // ########################################################################## CompoundSelector* CompoundSelector::unifyWith(CompoundSelector* rhs) { if (empty()) return rhs; CompoundSelectorObj unified = SASS_MEMORY_COPY(rhs); for (const SimpleSelectorObj& sel : elements()) { unified = sel->unifyWith(unified); if (unified.isNull()) break; } return unified.detach(); } // EO CompoundSelector::unifyWith(CompoundSelector*) // ########################################################################## // Returns the compoments of a [CompoundSelector] that matches only elements // matched by both this and [compound]. By default, this just returns a copy // of [compound] with this selector added to the end, or returns the original // array if this selector already exists in it. Returns `null` if unification // is impossible—for example, if there are multiple ID selectors. // ########################################################################## // This is implemented in `selector/simple.dart` as `SimpleSelector::unify` // ########################################################################## CompoundSelector* SimpleSelector::unifyWith(CompoundSelector* rhs) { if (rhs->length() == 1) { if (rhs->get(0)->is_universal()) { CompoundSelector* this_compound = SASS_MEMORY_NEW(CompoundSelector, pstate()); this_compound->append(SASS_MEMORY_COPY(this)); CompoundSelector* unified = rhs->get(0)->unifyWith(this_compound); if (unified == nullptr || unified != this_compound) delete this_compound; return unified; } } for (const SimpleSelectorObj& sel : rhs->elements()) { if (*this == *sel) { return rhs; } } CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, rhs->pstate()); bool addedThis = false; for (auto simple : rhs->elements()) { // Make sure pseudo selectors always come last. if (!addedThis && simple->getPseudoSelector()) { result->append(this); addedThis = true; } result->append(simple); } if (!addedThis) { result->append(this); } return result.detach(); } // EO SimpleSelector::unifyWith(CompoundSelector*) // ########################################################################## // This is implemented in `selector/type.dart` as `PseudoSelector::unify` // ########################################################################## CompoundSelector* TypeSelector::unifyWith(CompoundSelector* rhs) { if (rhs->empty()) { rhs->append(this); return rhs; } TypeSelector* type = Cast(rhs->at(0)); if (type != nullptr) { SimpleSelector* unified = unifyWith(type); if (unified == nullptr) { return nullptr; } rhs->elements()[0] = unified; } else if (!is_universal() || (has_ns_ && ns_ != "*")) { rhs->insert(rhs->begin(), this); } return rhs; } // ########################################################################## // This is implemented in `selector/id.dart` as `PseudoSelector::unify` // ########################################################################## CompoundSelector* IDSelector::unifyWith(CompoundSelector* rhs) { for (const SimpleSelector* sel : rhs->elements()) { if (const IDSelector* id_sel = Cast(sel)) { if (id_sel->name() != name()) return nullptr; } } return SimpleSelector::unifyWith(rhs); } // ########################################################################## // This is implemented in `selector/pseudo.dart` as `PseudoSelector::unify` // ########################################################################## CompoundSelector* PseudoSelector::unifyWith(CompoundSelector* compound) { if (compound->length() == 1 && compound->first()->is_universal()) { // std::cerr << "implement universal pseudo\n"; } for (const SimpleSelectorObj& sel : compound->elements()) { if (*this == *sel) { return compound; } } CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, compound->pstate()); bool addedThis = false; for (auto simple : compound->elements()) { // Make sure pseudo selectors always come last. if (PseudoSelectorObj pseudo = simple->getPseudoSelector()) { if (pseudo->isElement()) { // A given compound selector may only contain one pseudo element. If // [compound] has a different one than [this], unification fails. if (isElement()) { return {}; } // Otherwise, this is a pseudo selector and // should come before pseduo elements. result->append(this); addedThis = true; } } result->append(simple); } if (!addedThis) { result->append(this); } return result.detach(); } // EO PseudoSelector::unifyWith(CompoundSelector* // ########################################################################## // This is implemented in `extend/functions.dart` as `unifyUniversalAndElement` // Returns a [SimpleSelector] that matches only elements that are matched by // both [selector1] and [selector2], which must both be either [UniversalSelector]s // or [TypeSelector]s. If no such selector can be produced, returns `null`. // Note: libsass handles universal selector directly within the type selector // ########################################################################## SimpleSelector* TypeSelector::unifyWith(const SimpleSelector* rhs) { bool rhs_ns = false; if (!(is_ns_eq(*rhs) || rhs->is_universal_ns())) { if (!is_universal_ns()) { return nullptr; } rhs_ns = true; } bool rhs_name = false; if (!(name_ == rhs->name() || rhs->is_universal())) { if (!(is_universal())) { return nullptr; } rhs_name = true; } if (rhs_ns) { ns(rhs->ns()); has_ns(rhs->has_ns()); } if (rhs_name) name(rhs->name()); return this; } // EO TypeSelector::unifyWith(const SimpleSelector*) // ########################################################################## // Unify two complex selectors. Internally calls `unifyComplex` // and then wraps the result in newly create ComplexSelectors. // ########################################################################## SelectorList* ComplexSelector::unifyWith(ComplexSelector* rhs) { SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate()); sass::vector> rv = unifyComplex({ elements(), rhs->elements() }); for (sass::vector items : rv) { ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate()); sel->elements() = std::move(items); list->append(sel); } return list.detach(); } // EO ComplexSelector::unifyWith(ComplexSelector*) // ########################################################################## // only called from the sass function `selector-unify` // ########################################################################## SelectorList* SelectorList::unifyWith(SelectorList* rhs) { SelectorList* slist = SASS_MEMORY_NEW(SelectorList, pstate()); // Unify all of children with RHS's children, // storing the results in `unified_complex_selectors` for (ComplexSelectorObj& seq1 : elements()) { for (ComplexSelectorObj& seq2 : rhs->elements()) { if (SelectorListObj unified = seq1->unifyWith(seq2)) { std::move(unified->begin(), unified->end(), std::inserter(slist->elements(), slist->end())); } } } return slist; } // EO SelectorList::unifyWith(SelectorList*) // ########################################################################## // ########################################################################## } golibsass-1.0.0/libsass_src/src/ast_sel_weave.cpp000066400000000000000000000563211405214413600221300ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "permutate.hpp" #include "dart_helpers.hpp" namespace Sass { // ########################################################################## // Returns whether or not [compound] contains a `::root` selector. // ########################################################################## bool hasRoot(const CompoundSelector* compound) { // Libsass does not yet know the root selector return false; } // EO hasRoot // ########################################################################## // Returns whether a [CompoundSelector] may contain only // one simple selector of the same type as [simple]. // ########################################################################## bool isUnique(const SimpleSelector* simple) { if (Cast(simple)) return true; if (const PseudoSelector * pseudo = Cast(simple)) { if (pseudo->is_pseudo_element()) return true; } return false; } // EO isUnique // ########################################################################## // Returns whether [complex1] and [complex2] need to be unified to // produce a valid combined selector. This is necessary when both // selectors contain the same unique simple selector, such as an ID. // ########################################################################## bool mustUnify( const sass::vector& complex1, const sass::vector& complex2) { sass::vector uniqueSelectors1; for (const SelectorComponent* component : complex1) { if (const CompoundSelector * compound = component->getCompound()) { for (const SimpleSelector* sel : compound->elements()) { if (isUnique(sel)) { uniqueSelectors1.push_back(sel); } } } } if (uniqueSelectors1.empty()) return false; // ToDo: unsure if this is correct for (const SelectorComponent* component : complex2) { if (const CompoundSelector * compound = component->getCompound()) { for (const SimpleSelector* sel : compound->elements()) { if (isUnique(sel)) { for (auto check : uniqueSelectors1) { if (*check == *sel) return true; } } } } } return false; } // EO isUnique // ########################################################################## // Helper function used by `weaveParents` // ########################################################################## bool cmpGroups( const sass::vector& group1, const sass::vector& group2, sass::vector& select) { if (group1.size() == group2.size() && std::equal(group1.begin(), group1.end(), group2.begin(), PtrObjEqualityFn)) { select = group1; return true; } if (!Cast(group1.front())) { select = {}; return false; } if (!Cast(group2.front())) { select = {}; return false; } if (complexIsParentSuperselector(group1, group2)) { select = group2; return true; } if (complexIsParentSuperselector(group2, group1)) { select = group1; return true; } if (!mustUnify(group1, group2)) { select = {}; return false; } sass::vector> unified = unifyComplex({ group1, group2 }); if (unified.empty()) return false; if (unified.size() > 1) return false; select = unified.front(); return true; } // EO cmpGroups // ########################################################################## // Helper function used by `weaveParents` // ########################################################################## template bool checkForEmptyChild(const T& item) { return item.empty(); } // EO checkForEmptyChild // ########################################################################## // Helper function used by `weaveParents` // ########################################################################## bool cmpChunkForEmptySequence( const sass::vector>& seq, const sass::vector& group) { return seq.empty(); } // EO cmpChunkForEmptySequence // ########################################################################## // Helper function used by `weaveParents` // ########################################################################## bool cmpChunkForParentSuperselector( const sass::vector>& seq, const sass::vector& group) { return seq.empty() || complexIsParentSuperselector(seq.front(), group); } // EO cmpChunkForParentSuperselector // ########################################################################## // Returns all orderings of initial subseqeuences of [queue1] and [queue2]. // The [done] callback is used to determine the extent of the initial // subsequences. It's called with each queue until it returns `true`. // Destructively removes the initial subsequences of [queue1] and [queue2]. // For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|` denoting // the boundary of the initial subsequence), this would return `[(A B C 1 2), // (1 2 A B C)]`. The queues would then contain `(D E)` and `(3 4 5)`. // ########################################################################## template sass::vector> getChunks( sass::vector& queue1, sass::vector& queue2, const T& group, bool(*done)(const sass::vector&, const T&) ) { sass::vector chunk1; while (!done(queue1, group)) { chunk1.push_back(queue1.front()); queue1.erase(queue1.begin()); } sass::vector chunk2; while (!done(queue2, group)) { chunk2.push_back(queue2.front()); queue2.erase(queue2.begin()); } if (chunk1.empty() && chunk2.empty()) return {}; else if (chunk1.empty()) return { chunk2 }; else if (chunk2.empty()) return { chunk1 }; sass::vector choice1(chunk1), choice2(chunk2); std::move(std::begin(chunk2), std::end(chunk2), std::inserter(choice1, std::end(choice1))); std::move(std::begin(chunk1), std::end(chunk1), std::inserter(choice2, std::end(choice2))); return { choice1, choice2 }; } // EO getChunks // ########################################################################## // If the first element of [queue] has a `::root` // selector, removes and returns that element. // ########################################################################## CompoundSelectorObj getFirstIfRoot(sass::vector& queue) { if (queue.empty()) return {}; SelectorComponent* first = queue.front(); if (CompoundSelector* sel = Cast(first)) { if (!hasRoot(sel)) return {}; queue.erase(queue.begin()); return sel; } return {}; } // EO getFirstIfRoot // ########################################################################## // Returns [complex], grouped into sub-lists such that no sub-list // contains two adjacent [ComplexSelector]s. For example, // `(A B > C D + E ~ > G)` is grouped into `[(A) (B > C) (D + E ~ > G)]`. // ########################################################################## sass::vector> groupSelectors( const sass::vector& components) { bool lastWasCompound = false; sass::vector group; sass::vector> groups; for (size_t i = 0; i < components.size(); i += 1) { if (CompoundSelector* compound = components[i]->getCompound()) { if (lastWasCompound) { groups.push_back(group); group.clear(); } group.push_back(compound); lastWasCompound = true; } else if (SelectorCombinator* combinator = components[i]->getCombinator()) { group.push_back(combinator); lastWasCompound = false; } } if (!group.empty()) { groups.push_back(group); } return groups; } // EO groupSelectors // ########################################################################## // Extracts leading [Combinator]s from [components1] and [components2] // and merges them together into a single list of combinators. // If there are no combinators to be merged, returns an empty list. // If the combinators can't be merged, returns `null`. // ########################################################################## bool mergeInitialCombinators( sass::vector& components1, sass::vector& components2, sass::vector& result) { sass::vector combinators1; while (!components1.empty() && Cast(components1.front())) { SelectorCombinatorObj front = Cast(components1.front()); components1.erase(components1.begin()); combinators1.push_back(front); } sass::vector combinators2; while (!components2.empty() && Cast(components2.front())) { SelectorCombinatorObj front = Cast(components2.front()); components2.erase(components2.begin()); combinators2.push_back(front); } // If neither sequence of combinators is a subsequence // of the other, they cannot be merged successfully. sass::vector LCS = lcs(combinators1, combinators2); if (ListEquality(LCS, combinators1, PtrObjEqualityFn)) { result = combinators2; return true; } if (ListEquality(LCS, combinators2, PtrObjEqualityFn)) { result = combinators1; return true; } return false; } // EO mergeInitialCombinators // ########################################################################## // Extracts trailing [Combinator]s, and the selectors to which they apply, // from [components1] and [components2] and merges them together into a // single list. If there are no combinators to be merged, returns an // empty list. If the sequences can't be merged, returns `null`. // ########################################################################## bool mergeFinalCombinators( sass::vector& components1, sass::vector& components2, sass::vector>>& result) { if (components1.empty() || !Cast(components1.back())) { if (components2.empty() || !Cast(components2.back())) { return true; } } sass::vector combinators1; while (!components1.empty() && Cast(components1.back())) { SelectorCombinatorObj back = Cast(components1.back()); components1.erase(components1.end() - 1); combinators1.push_back(back); } sass::vector combinators2; while (!components2.empty() && Cast(components2.back())) { SelectorCombinatorObj back = Cast(components2.back()); components2.erase(components2.end() - 1); combinators2.push_back(back); } // reverse now as we used push_back (faster than new alloc) std::reverse(combinators1.begin(), combinators1.end()); std::reverse(combinators2.begin(), combinators2.end()); if (combinators1.size() > 1 || combinators2.size() > 1) { // If there are multiple combinators, something hacky's going on. If one // is a supersequence of the other, use that, otherwise give up. auto LCS = lcs(combinators1, combinators2); if (ListEquality(LCS, combinators1, PtrObjEqualityFn)) { result.push_back({ combinators2 }); } else if (ListEquality(LCS, combinators2, PtrObjEqualityFn)) { result.push_back({ combinators1 }); } else { return false; } return true; } // This code looks complicated, but it's actually just a bunch of special // cases for interactions between different combinators. SelectorCombinatorObj combinator1, combinator2; if (!combinators1.empty()) combinator1 = combinators1.back(); if (!combinators2.empty()) combinator2 = combinators2.back(); if (!combinator1.isNull() && !combinator2.isNull()) { CompoundSelector* compound1 = Cast(components1.back()); CompoundSelector* compound2 = Cast(components2.back()); components1.pop_back(); components2.pop_back(); if (combinator1->isGeneralCombinator() && combinator2->isGeneralCombinator()) { if (compound1->isSuperselectorOf(compound2)) { result.push_back({ { compound2, combinator2 } }); } else if (compound2->isSuperselectorOf(compound1)) { result.push_back({ { compound1, combinator1 } }); } else { sass::vector> choices; choices.push_back({ compound1, combinator1, compound2, combinator2 }); choices.push_back({ compound2, combinator2, compound1, combinator1 }); if (CompoundSelector* unified = compound1->unifyWith(compound2)) { choices.push_back({ unified, combinator1 }); } result.push_back(choices); } } else if ((combinator1->isGeneralCombinator() && combinator2->isAdjacentCombinator()) || (combinator1->isAdjacentCombinator() && combinator2->isGeneralCombinator())) { CompoundSelector* followingSiblingSelector = combinator1->isGeneralCombinator() ? compound1 : compound2; CompoundSelector* nextSiblingSelector = combinator1->isGeneralCombinator() ? compound2 : compound1; SelectorCombinator* followingSiblingCombinator = combinator1->isGeneralCombinator() ? combinator1 : combinator2; SelectorCombinator* nextSiblingCombinator = combinator1->isGeneralCombinator() ? combinator2 : combinator1; if (followingSiblingSelector->isSuperselectorOf(nextSiblingSelector)) { result.push_back({ { nextSiblingSelector, nextSiblingCombinator } }); } else { CompoundSelectorObj unified = compound1->unifyWith(compound2); sass::vector> items; if (!unified.isNull()) { items.push_back({ unified, nextSiblingCombinator }); } items.insert(items.begin(), { followingSiblingSelector, followingSiblingCombinator, nextSiblingSelector, nextSiblingCombinator, }); result.push_back(items); } } else if (combinator1->isChildCombinator() && (combinator2->isAdjacentCombinator() || combinator2->isGeneralCombinator())) { result.push_back({ { compound2, combinator2 } }); components1.push_back(compound1); components1.push_back(combinator1); } else if (combinator2->isChildCombinator() && (combinator1->isAdjacentCombinator() || combinator1->isGeneralCombinator())) { result.push_back({ { compound1, combinator1 } }); components2.push_back(compound2); components2.push_back(combinator2); } else if (*combinator1 == *combinator2) { CompoundSelectorObj unified = compound1->unifyWith(compound2); if (unified.isNull()) return false; result.push_back({ { unified, combinator1 } }); } else { return false; } return mergeFinalCombinators(components1, components2, result); } else if (!combinator1.isNull()) { if (combinator1->isChildCombinator() && !components2.empty()) { const CompoundSelector* back1 = Cast(components1.back()); const CompoundSelector* back2 = Cast(components2.back()); if (back1 && back2 && back2->isSuperselectorOf(back1)) { components2.pop_back(); } } result.push_back({ { components1.back(), combinator1 } }); components1.pop_back(); return mergeFinalCombinators(components1, components2, result); } if (combinator2->isChildCombinator() && !components1.empty()) { const CompoundSelector* back1 = Cast(components1.back()); const CompoundSelector* back2 = Cast(components2.back()); if (back1 && back2 && back1->isSuperselectorOf(back2)) { components1.pop_back(); } } result.push_back({ { components2.back(), combinator2 } }); components2.pop_back(); return mergeFinalCombinators(components1, components2, result); } // EO mergeFinalCombinators // ########################################################################## // Expands "parenthesized selectors" in [complexes]. That is, if // we have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually // expands into `.D .C, .D (.A .B)`, and this function translates // `.D (.A .B)` into `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B` // would also be required, but including merged selectors results in // exponential output for very little gain. The selector `.D (.A .B)` // is represented as the list `[[.D], [.A, .B]]`. // ########################################################################## sass::vector> weave( const sass::vector>& complexes) { sass::vector> prefixes; prefixes.push_back(complexes.at(0)); for (size_t i = 1; i < complexes.size(); i += 1) { if (complexes[i].empty()) { continue; } const sass::vector& complex = complexes[i]; SelectorComponent* target = complex.back(); if (complex.size() == 1) { for (auto& prefix : prefixes) { prefix.push_back(target); } continue; } sass::vector parents(complex); parents.pop_back(); sass::vector> newPrefixes; for (sass::vector prefix : prefixes) { sass::vector> parentPrefixes = weaveParents(prefix, parents); if (parentPrefixes.empty()) continue; for (auto& parentPrefix : parentPrefixes) { parentPrefix.push_back(target); newPrefixes.push_back(parentPrefix); } } prefixes = newPrefixes; } return prefixes; } // EO weave // ########################################################################## // Interweaves [parents1] and [parents2] as parents of the same target // selector. Returns all possible orderings of the selectors in the // inputs (including using unification) that maintain the relative // ordering of the input. For example, given `.foo .bar` and `.baz .bang`, // this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`, // `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`, // and so on until `.baz .bang .foo .bar`. Semantically, for selectors A // and B, this returns all selectors `AB_i` such that the union over all i // of elements matched by `AB_i X` is identical to the intersection of all // elements matched by `A X` and all elements matched by `B X`. Some `AB_i` // are elided to reduce the size of the output. // ########################################################################## sass::vector> weaveParents( sass::vector queue1, sass::vector queue2) { sass::vector leads; sass::vector>> trails; if (!mergeInitialCombinators(queue1, queue2, leads)) return {}; if (!mergeFinalCombinators(queue1, queue2, trails)) return {}; // list comes out in reverse order for performance std::reverse(trails.begin(), trails.end()); // Make sure there's at most one `:root` in the output. // Note: does not yet do anything in libsass (no root selector) CompoundSelectorObj root1 = getFirstIfRoot(queue1); CompoundSelectorObj root2 = getFirstIfRoot(queue2); if (!root1.isNull() && !root2.isNull()) { CompoundSelectorObj root = root1->unifyWith(root2); if (root.isNull()) return {}; // null queue1.insert(queue1.begin(), root); queue2.insert(queue2.begin(), root); } else if (!root1.isNull()) { queue2.insert(queue2.begin(), root1); } else if (!root2.isNull()) { queue1.insert(queue1.begin(), root2); } // group into sub-lists so no sub-list contains two adjacent ComplexSelectors. sass::vector> groups1 = groupSelectors(queue1); sass::vector> groups2 = groupSelectors(queue2); // The main array to store our choices that will be permutated sass::vector>> choices; // append initial combinators choices.push_back({ leads }); sass::vector> LCS = lcs>(groups1, groups2, cmpGroups); for (auto group : LCS) { // Create junks from groups1 and groups2 sass::vector>> chunks = getChunks>( groups1, groups2, group, cmpChunkForParentSuperselector); // Create expanded array by flattening chunks2 inner sass::vector> expanded = flattenInner(chunks); // Prepare data structures choices.push_back(expanded); choices.push_back({ group }); if (!groups1.empty()) { groups1.erase(groups1.begin()); } if (!groups2.empty()) { groups2.erase(groups2.begin()); } } // Create junks from groups1 and groups2 sass::vector>> chunks = getChunks>( groups1, groups2, {}, cmpChunkForEmptySequence); // Append chunks with inner arrays flattened choices.emplace_back(flattenInner(chunks)); // append all trailing selectors to choices std::move(std::begin(trails), std::end(trails), std::inserter(choices, std::end(choices))); // move all non empty items to the front, then erase the trailing ones choices.erase(std::remove_if(choices.begin(), choices.end(), checkForEmptyChild >>), choices.end()); // permutate all possible paths through selectors sass::vector> results = flattenInner(permutate(choices)); return results; } // EO weaveParents // ########################################################################## // ########################################################################## } golibsass-1.0.0/libsass_src/src/ast_selectors.cpp000066400000000000000000000754341405214413600221670ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "permutate.hpp" #include "util_string.hpp" namespace Sass { ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Selector::Selector(SourceSpan pstate) : Expression(pstate), hash_(0) { concrete_type(SELECTOR); } Selector::Selector(const Selector* ptr) : Expression(ptr), hash_(ptr->hash_) { concrete_type(SELECTOR); } bool Selector::has_real_parent_ref() const { return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Selector_Schema::Selector_Schema(SourceSpan pstate, String_Obj c) : AST_Node(pstate), contents_(c), connect_parent_(true), hash_(0) { } Selector_Schema::Selector_Schema(const Selector_Schema* ptr) : AST_Node(ptr), contents_(ptr->contents_), connect_parent_(ptr->connect_parent_), hash_(ptr->hash_) { } unsigned long Selector_Schema::specificity() const { return 0; } size_t Selector_Schema::hash() const { if (hash_ == 0) { hash_combine(hash_, contents_->hash()); } return hash_; } bool Selector_Schema::has_real_parent_ref() const { // Note: disabled since it does not seem to do anything? // if (String_Schema_Obj schema = Cast(contents())) { // if (schema->empty()) return false; // const auto first = schema->first(); // return Cast(first); // } return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SimpleSelector::SimpleSelector(SourceSpan pstate, sass::string n) : Selector(pstate), ns_(""), name_(n), has_ns_(false) { size_t pos = n.find('|'); // found some namespace if (pos != sass::string::npos) { has_ns_ = true; ns_ = n.substr(0, pos); name_ = n.substr(pos + 1); } } SimpleSelector::SimpleSelector(const SimpleSelector* ptr) : Selector(ptr), ns_(ptr->ns_), name_(ptr->name_), has_ns_(ptr->has_ns_) { } sass::string SimpleSelector::ns_name() const { if (!has_ns_) return name_; else return ns_ + "|" + name_; } size_t SimpleSelector::hash() const { if (hash_ == 0) { hash_combine(hash_, name()); hash_combine(hash_, (int)SELECTOR); hash_combine(hash_, (int)simple_type()); if (has_ns_) hash_combine(hash_, ns()); } return hash_; } bool SimpleSelector::empty() const { return ns().empty() && name().empty(); } // namespace compare functions bool SimpleSelector::is_ns_eq(const SimpleSelector& r) const { return has_ns_ == r.has_ns_ && ns_ == r.ns_; } // namespace query functions bool SimpleSelector::is_universal_ns() const { return has_ns_ && ns_ == "*"; } bool SimpleSelector::is_empty_ns() const { return !has_ns_ || ns_ == ""; } bool SimpleSelector::has_empty_ns() const { return has_ns_ && ns_ == ""; } bool SimpleSelector::has_qualified_ns() const { return has_ns_ && ns_ != "" && ns_ != "*"; } // name query functions bool SimpleSelector::is_universal() const { return name_ == "*"; } bool SimpleSelector::has_placeholder() { return false; } bool SimpleSelector::has_real_parent_ref() const { return false; }; bool SimpleSelector::is_pseudo_element() const { return false; } CompoundSelectorObj SimpleSelector::wrapInCompound() { CompoundSelectorObj selector = SASS_MEMORY_NEW(CompoundSelector, pstate()); selector->append(this); return selector; } ComplexSelectorObj SimpleSelector::wrapInComplex() { ComplexSelectorObj selector = SASS_MEMORY_NEW(ComplexSelector, pstate()); selector->append(wrapInCompound()); return selector; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// PlaceholderSelector::PlaceholderSelector(SourceSpan pstate, sass::string n) : SimpleSelector(pstate, n) { simple_type(PLACEHOLDER_SEL); } PlaceholderSelector::PlaceholderSelector(const PlaceholderSelector* ptr) : SimpleSelector(ptr) { simple_type(PLACEHOLDER_SEL); } unsigned long PlaceholderSelector::specificity() const { return Constants::Specificity_Base; } bool PlaceholderSelector::has_placeholder() { return true; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// TypeSelector::TypeSelector(SourceSpan pstate, sass::string n) : SimpleSelector(pstate, n) { simple_type(TYPE_SEL); } TypeSelector::TypeSelector(const TypeSelector* ptr) : SimpleSelector(ptr) { simple_type(TYPE_SEL); } unsigned long TypeSelector::specificity() const { if (name() == "*") return 0; else return Constants::Specificity_Element; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ClassSelector::ClassSelector(SourceSpan pstate, sass::string n) : SimpleSelector(pstate, n) { simple_type(CLASS_SEL); } ClassSelector::ClassSelector(const ClassSelector* ptr) : SimpleSelector(ptr) { simple_type(CLASS_SEL); } unsigned long ClassSelector::specificity() const { return Constants::Specificity_Class; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// IDSelector::IDSelector(SourceSpan pstate, sass::string n) : SimpleSelector(pstate, n) { simple_type(ID_SEL); } IDSelector::IDSelector(const IDSelector* ptr) : SimpleSelector(ptr) { simple_type(ID_SEL); } unsigned long IDSelector::specificity() const { return Constants::Specificity_ID; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// AttributeSelector::AttributeSelector(SourceSpan pstate, sass::string n, sass::string m, String_Obj v, char o) : SimpleSelector(pstate, n), matcher_(m), value_(v), modifier_(o) { simple_type(ATTRIBUTE_SEL); } AttributeSelector::AttributeSelector(const AttributeSelector* ptr) : SimpleSelector(ptr), matcher_(ptr->matcher_), value_(ptr->value_), modifier_(ptr->modifier_) { simple_type(ATTRIBUTE_SEL); } size_t AttributeSelector::hash() const { if (hash_ == 0) { hash_combine(hash_, SimpleSelector::hash()); hash_combine(hash_, std::hash()(matcher())); if (value_) hash_combine(hash_, value_->hash()); } return hash_; } unsigned long AttributeSelector::specificity() const { return Constants::Specificity_Attr; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// PseudoSelector::PseudoSelector(SourceSpan pstate, sass::string name, bool element) : SimpleSelector(pstate, name), normalized_(Util::unvendor(name)), argument_({}), selector_({}), isSyntacticClass_(!element), isClass_(!element && !isFakePseudoElement(normalized_)) { simple_type(PSEUDO_SEL); } PseudoSelector::PseudoSelector(const PseudoSelector* ptr) : SimpleSelector(ptr), normalized_(ptr->normalized()), argument_(ptr->argument()), selector_(ptr->selector()), isSyntacticClass_(ptr->isSyntacticClass()), isClass_(ptr->isClass()) { simple_type(PSEUDO_SEL); } // A pseudo-element is made of two colons (::) followed by the name. // The `::` notation is introduced by the current document in order to // establish a discrimination between pseudo-classes and pseudo-elements. // For compatibility with existing style sheets, user agents must also // accept the previous one-colon notation for pseudo-elements introduced // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and // :after). This compatibility is not allowed for the new pseudo-elements // introduced in this specification. bool PseudoSelector::is_pseudo_element() const { return isElement(); } size_t PseudoSelector::hash() const { if (hash_ == 0) { hash_combine(hash_, SimpleSelector::hash()); if (selector_) hash_combine(hash_, selector_->hash()); if (argument_) hash_combine(hash_, argument_->hash()); } return hash_; } unsigned long PseudoSelector::specificity() const { if (is_pseudo_element()) return Constants::Specificity_Element; return Constants::Specificity_Pseudo; } PseudoSelectorObj PseudoSelector::withSelector(SelectorListObj selector) { PseudoSelectorObj pseudo = SASS_MEMORY_COPY(this); pseudo->selector(selector); return pseudo; } bool PseudoSelector::empty() const { // Only considered empty if selector is // available but has no items in it. return selector() && selector()->empty(); } void PseudoSelector::cloneChildren() { if (selector().isNull()) selector({}); else selector(SASS_MEMORY_CLONE(selector())); } bool PseudoSelector::has_real_parent_ref() const { if (!selector()) return false; return selector()->has_real_parent_ref(); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SelectorList::SelectorList(SourceSpan pstate, size_t s) : Selector(pstate), Vectorized(s), is_optional_(false) { } SelectorList::SelectorList(const SelectorList* ptr) : Selector(ptr), Vectorized(*ptr), is_optional_(ptr->is_optional_) { } size_t SelectorList::hash() const { if (Selector::hash_ == 0) { hash_combine(Selector::hash_, Vectorized::hash()); } return Selector::hash_; } bool SelectorList::has_real_parent_ref() const { for (ComplexSelectorObj s : elements()) { if (s && s->has_real_parent_ref()) return true; } return false; } void SelectorList::cloneChildren() { for (size_t i = 0, l = length(); i < l; i++) { at(i) = SASS_MEMORY_CLONE(at(i)); } } unsigned long SelectorList::specificity() const { return 0; } bool SelectorList::isInvisible() const { if (length() == 0) return true; for (size_t i = 0; i < length(); i += 1) { if (get(i)->isInvisible()) return true; } return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ComplexSelector::ComplexSelector(SourceSpan pstate) : Selector(pstate), Vectorized(), chroots_(false), hasPreLineFeed_(false) { } ComplexSelector::ComplexSelector(const ComplexSelector* ptr) : Selector(ptr), Vectorized(ptr->elements()), chroots_(ptr->chroots()), hasPreLineFeed_(ptr->hasPreLineFeed()) { } void ComplexSelector::cloneChildren() { for (size_t i = 0, l = length(); i < l; i++) { at(i) = SASS_MEMORY_CLONE(at(i)); } } unsigned long ComplexSelector::specificity() const { int sum = 0; for (auto component : elements()) { sum += component->specificity(); } return sum; } bool ComplexSelector::isInvisible() const { if (length() == 0) return true; for (size_t i = 0; i < length(); i += 1) { if (CompoundSelectorObj compound = get(i)->getCompound()) { if (compound->isInvisible()) return true; } } return false; } bool ComplexSelector::isInvalidCss() const { for (size_t i = 0; i < length(); i += 1) { if (CompoundSelectorObj compound = get(i)->getCompound()) { if (compound->isInvalidCss()) return true; } } return false; } SelectorListObj ComplexSelector::wrapInList() { SelectorListObj selector = SASS_MEMORY_NEW(SelectorList, pstate()); selector->append(this); return selector; } size_t ComplexSelector::hash() const { if (Selector::hash_ == 0) { hash_combine(Selector::hash_, Vectorized::hash()); // ToDo: this breaks some extend lookup // hash_combine(Selector::hash_, chroots_); } return Selector::hash_; } bool ComplexSelector::has_placeholder() const { for (size_t i = 0, L = length(); i < L; ++i) { if (get(i)->has_placeholder()) return true; } return false; } bool ComplexSelector::has_real_parent_ref() const { for (auto item : elements()) { if (item->has_real_parent_ref()) return true; } return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SelectorComponent::SelectorComponent(SourceSpan pstate, bool postLineBreak) : Selector(pstate), hasPostLineBreak_(postLineBreak) { } SelectorComponent::SelectorComponent(const SelectorComponent* ptr) : Selector(ptr), hasPostLineBreak_(ptr->hasPostLineBreak()) { } void SelectorComponent::cloneChildren() { } unsigned long SelectorComponent::specificity() const { return 0; } // Wrap the compound selector with a complex selector ComplexSelector* SelectorComponent::wrapInComplex() { auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate()); complex->append(this); return complex; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SelectorCombinator::SelectorCombinator(SourceSpan pstate, SelectorCombinator::Combinator combinator, bool postLineBreak) : SelectorComponent(pstate, postLineBreak), combinator_(combinator) { } SelectorCombinator::SelectorCombinator(const SelectorCombinator* ptr) : SelectorComponent(ptr->pstate(), false), combinator_(ptr->combinator()) { } void SelectorCombinator::cloneChildren() { } unsigned long SelectorCombinator::specificity() const { return 0; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// CompoundSelector::CompoundSelector(SourceSpan pstate, bool postLineBreak) : SelectorComponent(pstate, postLineBreak), Vectorized(), hasRealParent_(false) { } CompoundSelector::CompoundSelector(const CompoundSelector* ptr) : SelectorComponent(ptr), Vectorized(*ptr), hasRealParent_(ptr->hasRealParent()) { } size_t CompoundSelector::hash() const { if (Selector::hash_ == 0) { hash_combine(Selector::hash_, Vectorized::hash()); hash_combine(Selector::hash_, hasRealParent_); } return Selector::hash_; } bool CompoundSelector::has_real_parent_ref() const { if (hasRealParent()) return true; // ToDo: dart sass has another check? // if (Cast(front)) { // if (front->ns() != "") return false; // } for (const SimpleSelector* s : elements()) { if (s && s->has_real_parent_ref()) return true; } return false; } bool CompoundSelector::has_placeholder() const { if (length() == 0) return false; for (SimpleSelectorObj ss : elements()) { if (ss->has_placeholder()) return true; } return false; } void CompoundSelector::cloneChildren() { for (size_t i = 0, l = length(); i < l; i++) { at(i) = SASS_MEMORY_CLONE(at(i)); } } unsigned long CompoundSelector::specificity() const { int sum = 0; for (size_t i = 0, L = length(); i < L; ++i) { sum += get(i)->specificity(); } return sum; } bool CompoundSelector::isInvisible() const { for (size_t i = 0; i < length(); i += 1) { if (!get(i)->isInvisible()) return false; } return true; } bool CompoundSelector::isSuperselectorOf(const CompoundSelector* sub, sass::string wrapped) const { CompoundSelector* rhs2 = const_cast(sub); CompoundSelector* lhs2 = const_cast(this); return compoundIsSuperselector(lhs2, rhs2, {}); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// MediaRule::MediaRule(SourceSpan pstate, Block_Obj block) : ParentStatement(pstate, block), schema_({}) { statement_type(MEDIA); } MediaRule::MediaRule(const MediaRule* ptr) : ParentStatement(ptr), schema_(ptr->schema_) { statement_type(MEDIA); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// CssMediaRule::CssMediaRule(SourceSpan pstate, Block_Obj block) : ParentStatement(pstate, block), Vectorized() { statement_type(MEDIA); } CssMediaRule::CssMediaRule(const CssMediaRule* ptr) : ParentStatement(ptr), Vectorized(*ptr) { statement_type(MEDIA); } CssMediaQuery::CssMediaQuery(SourceSpan pstate) : AST_Node(pstate), modifier_(""), type_(""), features_() { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// bool CssMediaQuery::operator==(const CssMediaQuery& rhs) const { return type_ == rhs.type_ && modifier_ == rhs.modifier_ && features_ == rhs.features_; } // Implemented after dart-sass (maybe move to other class?) CssMediaQuery_Obj CssMediaQuery::merge(CssMediaQuery_Obj& other) { sass::string ourType = this->type(); Util::ascii_str_tolower(&ourType); sass::string theirType = other->type(); Util::ascii_str_tolower(&theirType); sass::string ourModifier = this->modifier(); Util::ascii_str_tolower(&ourModifier); sass::string theirModifier = other->modifier(); Util::ascii_str_tolower(&theirModifier); sass::string type; sass::string modifier; sass::vector features; if (ourType.empty() && theirType.empty()) { CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate()); sass::vector f1(this->features()); sass::vector f2(other->features()); features.insert(features.end(), f1.begin(), f1.end()); features.insert(features.end(), f2.begin(), f2.end()); query->features(features); return query; } if ((ourModifier == "not") != (theirModifier == "not")) { if (ourType == theirType) { sass::vector negativeFeatures = ourModifier == "not" ? this->features() : other->features(); sass::vector positiveFeatures = ourModifier == "not" ? other->features() : this->features(); // If the negative features are a subset of the positive features, the // query is empty. For example, `not screen and (color)` has no // intersection with `screen and (color) and (grid)`. // However, `not screen and (color)` *does* intersect with `screen and // (grid)`, because it means `not (screen and (color))` and so it allows // a screen with no color but with a grid. if (listIsSubsetOrEqual(negativeFeatures, positiveFeatures)) { return SASS_MEMORY_NEW(CssMediaQuery, pstate()); } else { return {}; } } else if (this->matchesAllTypes() || other->matchesAllTypes()) { return {}; } if (ourModifier == "not") { modifier = theirModifier; type = theirType; features = other->features(); } else { modifier = ourModifier; type = ourType; features = this->features(); } } else if (ourModifier == "not") { SASS_ASSERT(theirModifier == "not", "modifiers not is sync"); // CSS has no way of representing "neither screen nor print". if (ourType != theirType) return {}; auto moreFeatures = this->features().size() > other->features().size() ? this->features() : other->features(); auto fewerFeatures = this->features().size() > other->features().size() ? other->features() : this->features(); // If one set of features is a superset of the other, // use those features because they're strictly narrower. if (listIsSubsetOrEqual(fewerFeatures, moreFeatures)) { modifier = ourModifier; // "not" type = ourType; features = moreFeatures; } else { // Otherwise, there's no way to // represent the intersection. return {}; } } else { if (this->matchesAllTypes()) { modifier = theirModifier; // Omit the type if either input query did, since that indicates that they // aren't targeting a browser that requires "all and". type = (other->matchesAllTypes() && ourType.empty()) ? "" : theirType; sass::vector f1(this->features()); sass::vector f2(other->features()); features.insert(features.end(), f1.begin(), f1.end()); features.insert(features.end(), f2.begin(), f2.end()); } else if (other->matchesAllTypes()) { modifier = ourModifier; type = ourType; sass::vector f1(this->features()); sass::vector f2(other->features()); features.insert(features.end(), f1.begin(), f1.end()); features.insert(features.end(), f2.begin(), f2.end()); } else if (ourType != theirType) { return SASS_MEMORY_NEW(CssMediaQuery, pstate()); } else { modifier = ourModifier.empty() ? theirModifier : ourModifier; type = ourType; sass::vector f1(this->features()); sass::vector f2(other->features()); features.insert(features.end(), f1.begin(), f1.end()); features.insert(features.end(), f2.begin(), f2.end()); } } CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate()); query->modifier(modifier == ourModifier ? this->modifier() : other->modifier()); query->type(ourType.empty() ? other->type() : this->type()); query->features(features); return query; } CssMediaQuery::CssMediaQuery(const CssMediaQuery* ptr) : AST_Node(*ptr), modifier_(ptr->modifier_), type_(ptr->type_), features_(ptr->features_) { } ///////////////////////////////////////////////////////////////////////// // ToDo: finalize specificity implementation ///////////////////////////////////////////////////////////////////////// size_t SelectorList::maxSpecificity() const { size_t specificity = 0; for (auto complex : elements()) { specificity = std::max(specificity, complex->maxSpecificity()); } return specificity; } size_t SelectorList::minSpecificity() const { size_t specificity = 0; for (auto complex : elements()) { specificity = std::min(specificity, complex->minSpecificity()); } return specificity; } size_t CompoundSelector::maxSpecificity() const { size_t specificity = 0; for (auto simple : elements()) { specificity += simple->maxSpecificity(); } return specificity; } size_t CompoundSelector::minSpecificity() const { size_t specificity = 0; for (auto simple : elements()) { specificity += simple->minSpecificity(); } return specificity; } size_t ComplexSelector::maxSpecificity() const { size_t specificity = 0; for (auto component : elements()) { specificity += component->maxSpecificity(); } return specificity; } size_t ComplexSelector::minSpecificity() const { size_t specificity = 0; for (auto component : elements()) { specificity += component->minSpecificity(); } return specificity; } ///////////////////////////////////////////////////////////////////////// // ToDo: this might be done easier with new selector format ///////////////////////////////////////////////////////////////////////// sass::vector CompoundSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent) { auto parent = pstack.back(); sass::vector rv; for (SimpleSelectorObj simple : elements()) { if (PseudoSelector * pseudo = Cast(simple)) { if (SelectorList* sel = Cast(pseudo->selector())) { if (parent) { pseudo->selector(sel->resolve_parent_refs( pstack, traces, implicit_parent)); } } } } // Mix with parents from stack if (hasRealParent()) { if (parent.isNull()) { return { wrapInComplex() }; } else { for (auto complex : parent->elements()) { // The parent complex selector has a compound selector if (CompoundSelectorObj tail = Cast(complex->last())) { // Create a copy to alter it complex = SASS_MEMORY_COPY(complex); tail = SASS_MEMORY_COPY(tail); // Check if we can merge front with back if (length() > 0 && tail->length() > 0) { SimpleSelectorObj back = tail->last(); SimpleSelectorObj front = first(); auto simple_back = Cast(back); auto simple_front = Cast(front); if (simple_front && simple_back) { simple_back = SASS_MEMORY_COPY(simple_back); auto name = simple_back->name(); name += simple_front->name(); simple_back->name(name); tail->elements().back() = simple_back; tail->elements().insert(tail->end(), begin() + 1, end()); } else { tail->concat(this); } } else { tail->concat(this); } complex->elements().back() = tail; // Append to results rv.push_back(complex); } else { // Can't insert parent that ends with a combinator // where the parent selector is followed by something if (parent && length() > 0) { throw Exception::InvalidParent(parent, traces, this); } // Create a copy to alter it complex = SASS_MEMORY_COPY(complex); // Just append ourself complex->append(this); // Append to results rv.push_back(complex); } } } } // No parents else { // Create a new wrapper to wrap ourself auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate()); // Just append ourself complex->append(this); // Append to results rv.push_back(complex); } return rv; } bool cmpSimpleSelectors(SimpleSelector* a, SimpleSelector* b) { return (a->getSortOrder() < b->getSortOrder()); } void CompoundSelector::sortChildren() { std::sort(begin(), end(), cmpSimpleSelectors); } bool CompoundSelector::isInvalidCss() const { size_t current = 0, next = 0; for (const SimpleSelector* sel : elements()) { next = sel->getSortOrder(); // Must only have one type selector if (current == 1 && next == 1) { return true; } if (next < current) { return true; } current = next; } return false; } /* better return sass::vector? only - is empty container anyway? */ SelectorList* ComplexSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent) { sass::vector> vars; auto parent = pstack.back(); if (has_real_parent_ref() && !parent) { throw Exception::TopLevelParent(traces, pstate()); } if (!chroots() && parent) { if (!has_real_parent_ref() && !implicit_parent) { SelectorList* retval = SASS_MEMORY_NEW(SelectorList, pstate(), 1); retval->append(this); return retval; } vars.push_back(parent->elements()); } for (auto sel : elements()) { if (CompoundSelectorObj comp = Cast(sel)) { auto asd = comp->resolve_parent_refs(pstack, traces, implicit_parent); if (asd.size() > 0) vars.push_back(asd); } else { // ToDo: merge together sequences whenever possible auto cont = SASS_MEMORY_NEW(ComplexSelector, pstate()); cont->append(sel); vars.push_back({ cont }); } } // Need complex selectors to preserve linefeeds sass::vector> res = permutateAlt(vars); // std::reverse(std::begin(res), std::end(res)); auto lst = SASS_MEMORY_NEW(SelectorList, pstate()); for (auto items : res) { if (items.size() > 0) { ComplexSelectorObj first = SASS_MEMORY_COPY(items[0]); first->hasPreLineFeed(first->hasPreLineFeed() || (!has_real_parent_ref() && hasPreLineFeed())); // ToDo: remove once we know how to handle line feeds // ToDo: currently a mashup between ruby and dart sass // if (has_real_parent_ref()) first->has_line_feed(false); // first->has_line_break(first->has_line_break() || has_line_break()); first->chroots(true); // has been resolved by now for (size_t i = 1; i < items.size(); i += 1) { first->concat(items[i]); } lst->append(first); } } return lst; } SelectorList* SelectorList::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent) { SelectorList* rv = SASS_MEMORY_NEW(SelectorList, pstate()); for (auto sel : elements()) { // Note: this one is tricky as we get back a pointer from resolve parents ... SelectorListObj res = sel->resolve_parent_refs(pstack, traces, implicit_parent); // Note: ... and concat will only append the items in elements // Therefore by passing it directly, the container will leak! rv->concat(res); } return rv; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// IMPLEMENT_AST_OPERATORS(Selector_Schema); IMPLEMENT_AST_OPERATORS(PlaceholderSelector); IMPLEMENT_AST_OPERATORS(AttributeSelector); IMPLEMENT_AST_OPERATORS(TypeSelector); IMPLEMENT_AST_OPERATORS(ClassSelector); IMPLEMENT_AST_OPERATORS(IDSelector); IMPLEMENT_AST_OPERATORS(PseudoSelector); IMPLEMENT_AST_OPERATORS(SelectorCombinator); IMPLEMENT_AST_OPERATORS(CompoundSelector); IMPLEMENT_AST_OPERATORS(ComplexSelector); IMPLEMENT_AST_OPERATORS(SelectorList); } golibsass-1.0.0/libsass_src/src/ast_selectors.hpp000066400000000000000000000475211405214413600221700ustar00rootroot00000000000000#ifndef SASS_AST_SEL_H #define SASS_AST_SEL_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" namespace Sass { ///////////////////////////////////////////////////////////////////////// // Some helper functions ///////////////////////////////////////////////////////////////////////// bool compoundIsSuperselector( const CompoundSelectorObj& compound1, const CompoundSelectorObj& compound2, const sass::vector& parents); bool complexIsParentSuperselector( const sass::vector& complex1, const sass::vector& complex2); sass::vector> weave( const sass::vector>& complexes); sass::vector> weaveParents( sass::vector parents1, sass::vector parents2); sass::vector unifyCompound( const sass::vector& compound1, const sass::vector& compound2); sass::vector> unifyComplex( const sass::vector>& complexes); ///////////////////////////////////////// // Abstract base class for CSS selectors. ///////////////////////////////////////// class Selector : public Expression { protected: mutable size_t hash_; public: Selector(SourceSpan pstate); virtual ~Selector() = 0; size_t hash() const override = 0; virtual bool has_real_parent_ref() const; // you should reset this to null on containers virtual unsigned long specificity() const = 0; // by default we return the regular specificity // you must override this for all containers virtual size_t maxSpecificity() const { return specificity(); } virtual size_t minSpecificity() const { return specificity(); } // dispatch to correct handlers ATTACH_VIRTUAL_CMP_OPERATIONS(Selector) ATTACH_VIRTUAL_AST_OPERATIONS(Selector) }; inline Selector::~Selector() { } ///////////////////////////////////////////////////////////////////////// // Interpolated selectors -- the interpolated String will be expanded and // re-parsed into a normal selector class. ///////////////////////////////////////////////////////////////////////// class Selector_Schema final : public AST_Node { ADD_PROPERTY(String_Schema_Obj, contents) ADD_PROPERTY(bool, connect_parent); // store computed hash mutable size_t hash_; public: Selector_Schema(SourceSpan pstate, String_Obj c); bool has_real_parent_ref() const; // selector schema is not yet a final selector, so we do not // have a specificity for it yet. We need to virtual unsigned long specificity() const; size_t hash() const override; ATTACH_AST_OPERATIONS(Selector_Schema) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////// // Abstract base class for simple selectors. //////////////////////////////////////////// class SimpleSelector : public Selector { public: enum Simple_Type { ID_SEL, TYPE_SEL, CLASS_SEL, PSEUDO_SEL, ATTRIBUTE_SEL, PLACEHOLDER_SEL, }; public: HASH_CONSTREF(sass::string, ns) HASH_CONSTREF(sass::string, name) ADD_PROPERTY(Simple_Type, simple_type) HASH_PROPERTY(bool, has_ns) public: SimpleSelector(SourceSpan pstate, sass::string n = ""); // ordering within parent (peudos go last) virtual int getSortOrder() const = 0; virtual sass::string ns_name() const; size_t hash() const override; virtual bool empty() const; // namespace compare functions bool is_ns_eq(const SimpleSelector& r) const; // namespace query functions bool is_universal_ns() const; bool is_empty_ns() const; bool has_empty_ns() const; bool has_qualified_ns() const; // name query functions bool is_universal() const; virtual bool has_placeholder(); virtual ~SimpleSelector() = 0; virtual CompoundSelector* unifyWith(CompoundSelector*); /* helper function for syntax sugar */ virtual IDSelector* getIdSelector() { return NULL; } virtual TypeSelector* getTypeSelector() { return NULL; } virtual PseudoSelector* getPseudoSelector() { return NULL; } ComplexSelectorObj wrapInComplex(); CompoundSelectorObj wrapInCompound(); virtual bool isInvisible() const { return false; } virtual bool is_pseudo_element() const; virtual bool has_real_parent_ref() const override; bool operator==(const Selector& rhs) const final override; virtual bool operator==(const SelectorList& rhs) const; virtual bool operator==(const ComplexSelector& rhs) const; virtual bool operator==(const CompoundSelector& rhs) const; ATTACH_VIRTUAL_CMP_OPERATIONS(SimpleSelector); ATTACH_VIRTUAL_AST_OPERATIONS(SimpleSelector); ATTACH_CRTP_PERFORM_METHODS(); }; inline SimpleSelector::~SimpleSelector() { } ///////////////////////////////////////////////////////////////////////// // Placeholder selectors (e.g., "%foo") for use in extend-only selectors. ///////////////////////////////////////////////////////////////////////// class PlaceholderSelector final : public SimpleSelector { public: PlaceholderSelector(SourceSpan pstate, sass::string n); int getSortOrder() const override final { return 0; } bool isInvisible() const override { return true; } virtual unsigned long specificity() const override; virtual bool has_placeholder() override; bool operator==(const SimpleSelector& rhs) const override; ATTACH_CMP_OPERATIONS(PlaceholderSelector) ATTACH_AST_OPERATIONS(PlaceholderSelector) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////////////////////// // Type selectors (and the universal selector) -- e.g., div, span, *. ///////////////////////////////////////////////////////////////////// class TypeSelector final : public SimpleSelector { public: TypeSelector(SourceSpan pstate, sass::string n); int getSortOrder() const override final { return 1; } virtual unsigned long specificity() const override; SimpleSelector* unifyWith(const SimpleSelector*); CompoundSelector* unifyWith(CompoundSelector*) override; TypeSelector* getTypeSelector() override { return this; } bool operator==(const SimpleSelector& rhs) const final override; ATTACH_CMP_OPERATIONS(TypeSelector) ATTACH_AST_OPERATIONS(TypeSelector) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////// // Class selectors -- i.e., .foo. //////////////////////////////////////////////// class ClassSelector final : public SimpleSelector { public: ClassSelector(SourceSpan pstate, sass::string n); int getSortOrder() const override final { return 2; } virtual unsigned long specificity() const override; bool operator==(const SimpleSelector& rhs) const final override; ATTACH_CMP_OPERATIONS(ClassSelector) ATTACH_AST_OPERATIONS(ClassSelector) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////// // ID selectors -- i.e., #foo. //////////////////////////////////////////////// class IDSelector final : public SimpleSelector { public: IDSelector(SourceSpan pstate, sass::string n); int getSortOrder() const override final { return 2; } virtual unsigned long specificity() const override; CompoundSelector* unifyWith(CompoundSelector*) override; IDSelector* getIdSelector() final override { return this; } bool operator==(const SimpleSelector& rhs) const final override; ATTACH_CMP_OPERATIONS(IDSelector) ATTACH_AST_OPERATIONS(IDSelector) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////// // Attribute selectors -- e.g., [src*=".jpg"], etc. /////////////////////////////////////////////////// class AttributeSelector final : public SimpleSelector { ADD_CONSTREF(sass::string, matcher) // this cannot be changed to obj atm!!!!!!????!!!!!!! ADD_PROPERTY(String_Obj, value) // might be interpolated ADD_PROPERTY(char, modifier); public: AttributeSelector(SourceSpan pstate, sass::string n, sass::string m, String_Obj v, char o = 0); int getSortOrder() const override final { return 2; } size_t hash() const override; virtual unsigned long specificity() const override; bool operator==(const SimpleSelector& rhs) const final override; ATTACH_CMP_OPERATIONS(AttributeSelector) ATTACH_AST_OPERATIONS(AttributeSelector) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////////////////////////////////// // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc. ////////////////////////////////////////////////////////////////// // Pseudo Selector cannot have any namespace? class PseudoSelector final : public SimpleSelector { ADD_PROPERTY(sass::string, normalized) ADD_PROPERTY(String_Obj, argument) ADD_PROPERTY(SelectorListObj, selector) ADD_PROPERTY(bool, isSyntacticClass) ADD_PROPERTY(bool, isClass) public: PseudoSelector(SourceSpan pstate, sass::string n, bool element = false); int getSortOrder() const override final { return 3; } virtual bool is_pseudo_element() const override; size_t hash() const override; bool empty() const override; bool has_real_parent_ref() const override; // Whether this is a pseudo-element selector. // This is `true` if and only if [isClass] is `false`. bool isElement() const { return !isClass(); } // Whether this is syntactically a pseudo-element selector. // This is `true` if and only if [isSyntacticClass] is `false`. bool isSyntacticElement() const { return !isSyntacticClass(); } virtual unsigned long specificity() const override; PseudoSelectorObj withSelector(SelectorListObj selector); CompoundSelector* unifyWith(CompoundSelector*) override; PseudoSelector* getPseudoSelector() final override { return this; } bool operator==(const SimpleSelector& rhs) const final override; ATTACH_CMP_OPERATIONS(PseudoSelector) ATTACH_AST_OPERATIONS(PseudoSelector) void cloneChildren() override; ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// // Complex Selectors are the most important class of selectors. // A Selector List consists of Complex Selectors (separated by comma) // Complex Selectors are itself a list of Compounds and Combinators // Between each item there is an implicit ancestor of combinator //////////////////////////////////////////////////////////////////////////// class ComplexSelector final : public Selector, public Vectorized { ADD_PROPERTY(bool, chroots); // line break before list separator ADD_PROPERTY(bool, hasPreLineFeed); public: ComplexSelector(SourceSpan pstate); // Returns true if the first components // is a compound selector and fulfills // a few other criteria. bool isInvisible() const; bool isInvalidCss() const; size_t hash() const override; void cloneChildren() override; bool has_placeholder() const; bool has_real_parent_ref() const override; SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true); virtual unsigned long specificity() const override; SelectorList* unifyWith(ComplexSelector* rhs); bool isSuperselectorOf(const ComplexSelector* sub) const; SelectorListObj wrapInList(); size_t maxSpecificity() const override; size_t minSpecificity() const override; bool operator==(const Selector& rhs) const override; bool operator==(const SelectorList& rhs) const; bool operator==(const CompoundSelector& rhs) const; bool operator==(const SimpleSelector& rhs) const; ATTACH_CMP_OPERATIONS(ComplexSelector) ATTACH_AST_OPERATIONS(ComplexSelector) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// // Base class for complex selector components //////////////////////////////////////////////////////////////////////////// class SelectorComponent : public Selector { // line break after list separator ADD_PROPERTY(bool, hasPostLineBreak) public: SelectorComponent(SourceSpan pstate, bool postLineBreak = false); size_t hash() const override = 0; void cloneChildren() override; // By default we consider instances not empty virtual bool empty() const { return false; } virtual bool has_placeholder() const = 0; bool has_real_parent_ref() const override = 0; ComplexSelector* wrapInComplex(); size_t maxSpecificity() const override { return 0; } size_t minSpecificity() const override { return 0; } virtual bool isCompound() const { return false; }; virtual bool isCombinator() const { return false; }; /* helper function for syntax sugar */ virtual CompoundSelector* getCompound() { return NULL; } virtual SelectorCombinator* getCombinator() { return NULL; } virtual const CompoundSelector* getCompound() const { return NULL; } virtual const SelectorCombinator* getCombinator() const { return NULL; } virtual unsigned long specificity() const override; bool operator==(const Selector& rhs) const override = 0; ATTACH_VIRTUAL_CMP_OPERATIONS(SelectorComponent); ATTACH_VIRTUAL_AST_OPERATIONS(SelectorComponent); }; //////////////////////////////////////////////////////////////////////////// // A specific combinator between compound selectors //////////////////////////////////////////////////////////////////////////// class SelectorCombinator final : public SelectorComponent { public: // Enumerate all possible selector combinators. There is some // discrepancy with dart-sass. Opted to name them as in CSS33 enum Combinator { CHILD /* > */, GENERAL /* ~ */, ADJACENT /* + */}; private: // Store the type of this combinator HASH_CONSTREF(Combinator, combinator) public: SelectorCombinator(SourceSpan pstate, Combinator combinator, bool postLineBreak = false); bool has_real_parent_ref() const override { return false; } bool has_placeholder() const override { return false; } /* helper function for syntax sugar */ SelectorCombinator* getCombinator() final override { return this; } const SelectorCombinator* getCombinator() const final override { return this; } // Query type of combinator bool isCombinator() const override { return true; }; // Matches the right-hand selector if it's a direct child of the left- // hand selector in the DOM tree. Dart-sass also calls this `child` // https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator bool isChildCombinator() const { return combinator_ == CHILD; } // > // Matches the right-hand selector if it comes after the left-hand // selector in the DOM tree. Dart-sass class this `followingSibling` // https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator bool isGeneralCombinator() const { return combinator_ == GENERAL; } // ~ // Matches the right-hand selector if it's immediately adjacent to the // left-hand selector in the DOM tree. Dart-sass calls this `nextSibling` // https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator bool isAdjacentCombinator() const { return combinator_ == ADJACENT; } // + size_t maxSpecificity() const override { return 0; } size_t minSpecificity() const override { return 0; } size_t hash() const override { return std::hash()(combinator_); } void cloneChildren() override; virtual unsigned long specificity() const override; bool operator==(const Selector& rhs) const override; bool operator==(const SelectorComponent& rhs) const override; ATTACH_CMP_OPERATIONS(SelectorCombinator) ATTACH_AST_OPERATIONS(SelectorCombinator) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////////// // A compound selector consists of multiple simple selectors //////////////////////////////////////////////////////////////////////////// class CompoundSelector final : public SelectorComponent, public Vectorized { ADD_PROPERTY(bool, hasRealParent) public: CompoundSelector(SourceSpan pstate, bool postLineBreak = false); // Returns true if this compound selector // fulfills various criteria. bool isInvisible() const; bool empty() const override { return Vectorized::empty(); } size_t hash() const override; CompoundSelector* unifyWith(CompoundSelector* rhs); /* helper function for syntax sugar */ CompoundSelector* getCompound() final override { return this; } const CompoundSelector* getCompound() const final override { return this; } bool isSuperselectorOf(const CompoundSelector* sub, sass::string wrapped = "") const; void cloneChildren() override; bool has_real_parent_ref() const override; bool has_placeholder() const override; sass::vector resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true); virtual bool isCompound() const override { return true; }; virtual unsigned long specificity() const override; size_t maxSpecificity() const override; size_t minSpecificity() const override; bool operator==(const Selector& rhs) const override; bool operator==(const SelectorComponent& rhs) const override; bool operator==(const SelectorList& rhs) const; bool operator==(const ComplexSelector& rhs) const; bool operator==(const SimpleSelector& rhs) const; void sortChildren(); bool isInvalidCss() const; ATTACH_CMP_OPERATIONS(CompoundSelector) ATTACH_AST_OPERATIONS(CompoundSelector) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////// // Comma-separated selector groups. /////////////////////////////////// class SelectorList final : public Selector, public Vectorized { private: // maybe we have optional flag // ToDo: should be at ExtendRule? ADD_PROPERTY(bool, is_optional) public: SelectorList(SourceSpan pstate, size_t s = 0); sass::string type() const override { return "list"; } size_t hash() const override; SelectorList* unifyWith(SelectorList*); // Returns true if all complex selectors // can have real parents, meaning every // first component does allow for it bool isInvisible() const; void cloneChildren() override; bool has_real_parent_ref() const override; SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true); virtual unsigned long specificity() const override; bool isSuperselectorOf(const SelectorList* sub) const; size_t maxSpecificity() const override; size_t minSpecificity() const override; bool operator==(const Selector& rhs) const override; bool operator==(const ComplexSelector& rhs) const; bool operator==(const CompoundSelector& rhs) const; bool operator==(const SimpleSelector& rhs) const; // Selector Lists can be compared to comma lists bool operator==(const Expression& rhs) const override; ATTACH_CMP_OPERATIONS(SelectorList) ATTACH_AST_OPERATIONS(SelectorList) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////// // The Sass `@extend` directive. //////////////////////////////// class ExtendRule final : public Statement { ADD_PROPERTY(bool, isOptional) // This should be a simple selector only! ADD_PROPERTY(SelectorListObj, selector) ADD_PROPERTY(Selector_Schema_Obj, schema) public: ExtendRule(SourceSpan pstate, SelectorListObj s); ExtendRule(SourceSpan pstate, Selector_Schema_Obj s); ATTACH_AST_OPERATIONS(ExtendRule) ATTACH_CRTP_PERFORM_METHODS() }; } #endif golibsass-1.0.0/libsass_src/src/ast_supports.cpp000066400000000000000000000077211405214413600220550ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" namespace Sass { ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SupportsRule::SupportsRule(SourceSpan pstate, SupportsConditionObj condition, Block_Obj block) : ParentStatement(pstate, block), condition_(condition) { statement_type(SUPPORTS); } SupportsRule::SupportsRule(const SupportsRule* ptr) : ParentStatement(ptr), condition_(ptr->condition_) { statement_type(SUPPORTS); } bool SupportsRule::bubbles() { return true; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SupportsCondition::SupportsCondition(SourceSpan pstate) : Expression(pstate) { } SupportsCondition::SupportsCondition(const SupportsCondition* ptr) : Expression(ptr) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SupportsOperation::SupportsOperation(SourceSpan pstate, SupportsConditionObj l, SupportsConditionObj r, Operand o) : SupportsCondition(pstate), left_(l), right_(r), operand_(o) { } SupportsOperation::SupportsOperation(const SupportsOperation* ptr) : SupportsCondition(ptr), left_(ptr->left_), right_(ptr->right_), operand_(ptr->operand_) { } bool SupportsOperation::needs_parens(SupportsConditionObj cond) const { if (SupportsOperationObj op = Cast(cond)) { return op->operand() != operand(); } return Cast(cond) != NULL; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SupportsNegation::SupportsNegation(SourceSpan pstate, SupportsConditionObj c) : SupportsCondition(pstate), condition_(c) { } SupportsNegation::SupportsNegation(const SupportsNegation* ptr) : SupportsCondition(ptr), condition_(ptr->condition_) { } bool SupportsNegation::needs_parens(SupportsConditionObj cond) const { return Cast(cond) || Cast(cond); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// SupportsDeclaration::SupportsDeclaration(SourceSpan pstate, ExpressionObj f, ExpressionObj v) : SupportsCondition(pstate), feature_(f), value_(v) { } SupportsDeclaration::SupportsDeclaration(const SupportsDeclaration* ptr) : SupportsCondition(ptr), feature_(ptr->feature_), value_(ptr->value_) { } bool SupportsDeclaration::needs_parens(SupportsConditionObj cond) const { return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Supports_Interpolation::Supports_Interpolation(SourceSpan pstate, ExpressionObj v) : SupportsCondition(pstate), value_(v) { } Supports_Interpolation::Supports_Interpolation(const Supports_Interpolation* ptr) : SupportsCondition(ptr), value_(ptr->value_) { } bool Supports_Interpolation::needs_parens(SupportsConditionObj cond) const { return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// IMPLEMENT_AST_OPERATORS(SupportsRule); IMPLEMENT_AST_OPERATORS(SupportsCondition); IMPLEMENT_AST_OPERATORS(SupportsOperation); IMPLEMENT_AST_OPERATORS(SupportsNegation); IMPLEMENT_AST_OPERATORS(SupportsDeclaration); IMPLEMENT_AST_OPERATORS(Supports_Interpolation); ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// } golibsass-1.0.0/libsass_src/src/ast_supports.hpp000066400000000000000000000074071405214413600220630ustar00rootroot00000000000000#ifndef SASS_AST_SUPPORTS_H #define SASS_AST_SUPPORTS_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include #include #include #include #include "sass/base.h" #include "ast_fwd_decl.hpp" #include "util.hpp" #include "units.hpp" #include "context.hpp" #include "position.hpp" #include "constants.hpp" #include "operation.hpp" #include "position.hpp" #include "inspect.hpp" #include "source_map.hpp" #include "environment.hpp" #include "error_handling.hpp" #include "ast_def_macros.hpp" #include "ast_fwd_decl.hpp" #include "source_map.hpp" #include "fn_utils.hpp" #include "sass.h" namespace Sass { //////////////////// // `@supports` rule. //////////////////// class SupportsRule : public ParentStatement { ADD_PROPERTY(SupportsConditionObj, condition) public: SupportsRule(SourceSpan pstate, SupportsConditionObj condition, Block_Obj block = {}); bool bubbles() override; ATTACH_AST_OPERATIONS(SupportsRule) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////////////////////// // The abstract superclass of all Supports conditions. ////////////////////////////////////////////////////// class SupportsCondition : public Expression { public: SupportsCondition(SourceSpan pstate); virtual bool needs_parens(SupportsConditionObj cond) const { return false; } ATTACH_AST_OPERATIONS(SupportsCondition) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////// // An operator condition (e.g. `CONDITION1 and CONDITION2`). //////////////////////////////////////////////////////////// class SupportsOperation : public SupportsCondition { public: enum Operand { AND, OR }; private: ADD_PROPERTY(SupportsConditionObj, left); ADD_PROPERTY(SupportsConditionObj, right); ADD_PROPERTY(Operand, operand); public: SupportsOperation(SourceSpan pstate, SupportsConditionObj l, SupportsConditionObj r, Operand o); virtual bool needs_parens(SupportsConditionObj cond) const override; ATTACH_AST_OPERATIONS(SupportsOperation) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////////// // A negation condition (`not CONDITION`). ////////////////////////////////////////// class SupportsNegation : public SupportsCondition { private: ADD_PROPERTY(SupportsConditionObj, condition); public: SupportsNegation(SourceSpan pstate, SupportsConditionObj c); virtual bool needs_parens(SupportsConditionObj cond) const override; ATTACH_AST_OPERATIONS(SupportsNegation) ATTACH_CRTP_PERFORM_METHODS() }; ///////////////////////////////////////////////////// // A declaration condition (e.g. `(feature: value)`). ///////////////////////////////////////////////////// class SupportsDeclaration : public SupportsCondition { private: ADD_PROPERTY(ExpressionObj, feature); ADD_PROPERTY(ExpressionObj, value); public: SupportsDeclaration(SourceSpan pstate, ExpressionObj f, ExpressionObj v); virtual bool needs_parens(SupportsConditionObj cond) const override; ATTACH_AST_OPERATIONS(SupportsDeclaration) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////// // An interpolation condition (e.g. `#{$var}`). /////////////////////////////////////////////// class Supports_Interpolation : public SupportsCondition { private: ADD_PROPERTY(ExpressionObj, value); public: Supports_Interpolation(SourceSpan pstate, ExpressionObj v); virtual bool needs_parens(SupportsConditionObj cond) const override; ATTACH_AST_OPERATIONS(Supports_Interpolation) ATTACH_CRTP_PERFORM_METHODS() }; } #endif golibsass-1.0.0/libsass_src/src/ast_values.cpp000066400000000000000000001003251405214413600214470ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" namespace Sass { void str_rtrim(sass::string& str, const sass::string& delimiters = " \f\n\r\t\v") { str.erase( str.find_last_not_of( delimiters ) + 1 ); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// PreValue::PreValue(SourceSpan pstate, bool d, bool e, bool i, Type ct) : Expression(pstate, d, e, i, ct) { } PreValue::PreValue(const PreValue* ptr) : Expression(ptr) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Value::Value(SourceSpan pstate, bool d, bool e, bool i, Type ct) : PreValue(pstate, d, e, i, ct) { } Value::Value(const Value* ptr) : PreValue(ptr) { } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// List::List(SourceSpan pstate, size_t size, enum Sass_Separator sep, bool argl, bool bracket) : Value(pstate), Vectorized(size), separator_(sep), is_arglist_(argl), is_bracketed_(bracket), from_selector_(false) { concrete_type(LIST); } List::List(const List* ptr) : Value(ptr), Vectorized(*ptr), separator_(ptr->separator_), is_arglist_(ptr->is_arglist_), is_bracketed_(ptr->is_bracketed_), from_selector_(ptr->from_selector_) { concrete_type(LIST); } size_t List::hash() const { if (hash_ == 0) { hash_ = std::hash()(sep_string()); hash_combine(hash_, std::hash()(is_bracketed())); for (size_t i = 0, L = length(); i < L; ++i) hash_combine(hash_, (elements()[i])->hash()); } return hash_; } void List::set_delayed(bool delayed) { is_delayed(delayed); // don't set children } bool List::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (length() < r->length()) return true; if (length() > r->length()) return false; const auto& left = elements(); const auto& right = r->elements(); for (size_t i = 0; i < left.size(); i += 1) { if (*left[i] < *right[i]) return true; if (*left[i] == *right[i]) continue; return false; } return false; } // compare/sort by type return type() < rhs.type(); } bool List::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (length() != r->length()) return false; if (separator() != r->separator()) return false; if (is_bracketed() != r->is_bracketed()) return false; for (size_t i = 0, L = length(); i < L; ++i) { auto rv = r->at(i); auto lv = this->at(i); if (!lv && rv) return false; else if (!rv && lv) return false; else if (*lv != *rv) return false; } return true; } return false; } size_t List::size() const { if (!is_arglist_) return length(); // arglist expects a list of arguments // so we need to break before keywords for (size_t i = 0, L = length(); i < L; ++i) { ExpressionObj obj = this->at(i); if (Argument* arg = Cast(obj)) { if (!arg->name().empty()) return i; } } return length(); } ExpressionObj List::value_at_index(size_t i) { ExpressionObj obj = this->at(i); if (is_arglist_) { if (Argument* arg = Cast(obj)) { return arg->value(); } else { return obj; } } else { return obj; } } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Map::Map(SourceSpan pstate, size_t size) : Value(pstate), Hashed(size) { concrete_type(MAP); } Map::Map(const Map* ptr) : Value(ptr), Hashed(*ptr) { concrete_type(MAP); } bool Map::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (length() < r->length()) return true; if (length() > r->length()) return false; const auto& lkeys = keys(); const auto& rkeys = r->keys(); for (size_t i = 0; i < lkeys.size(); i += 1) { if (*lkeys[i] < *rkeys[i]) return true; if (*lkeys[i] == *rkeys[i]) continue; return false; } const auto& lvals = values(); const auto& rvals = r->values(); for (size_t i = 0; i < lvals.size(); i += 1) { if (*lvals[i] < *rvals[i]) return true; if (*lvals[i] == *rvals[i]) continue; return false; } return false; } // compare/sort by type return type() < rhs.type(); } bool Map::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (length() != r->length()) return false; for (auto key : keys()) { auto rv = r->at(key); auto lv = this->at(key); if (!lv && rv) return false; else if (!rv && lv) return false; else if (*lv != *rv) return false; } return true; } return false; } List_Obj Map::to_list(SourceSpan& pstate) { List_Obj ret = SASS_MEMORY_NEW(List, pstate, length(), SASS_COMMA); for (auto key : keys()) { List_Obj l = SASS_MEMORY_NEW(List, pstate, 2); l->append(key); l->append(at(key)); ret->append(l); } return ret; } size_t Map::hash() const { if (hash_ == 0) { for (auto key : keys()) { hash_combine(hash_, key->hash()); hash_combine(hash_, at(key)->hash()); } } return hash_; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Binary_Expression::Binary_Expression(SourceSpan pstate, Operand op, ExpressionObj lhs, ExpressionObj rhs) : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0) { } Binary_Expression::Binary_Expression(const Binary_Expression* ptr) : PreValue(ptr), op_(ptr->op_), left_(ptr->left_), right_(ptr->right_), hash_(ptr->hash_) { } bool Binary_Expression::is_left_interpolant(void) const { return is_interpolant() || (left() && left()->is_left_interpolant()); } bool Binary_Expression::is_right_interpolant(void) const { return is_interpolant() || (right() && right()->is_right_interpolant()); } const sass::string Binary_Expression::type_name() { return sass_op_to_name(optype()); } const sass::string Binary_Expression::separator() { return sass_op_separator(optype()); } bool Binary_Expression::has_interpolant() const { return is_left_interpolant() || is_right_interpolant(); } void Binary_Expression::set_delayed(bool delayed) { right()->set_delayed(delayed); left()->set_delayed(delayed); is_delayed(delayed); } bool Binary_Expression::operator<(const Expression& rhs) const { if (auto m = Cast(&rhs)) { return type() < m->type() || *left() < *m->left() || *right() < *m->right(); } // compare/sort by type return type() < rhs.type(); } bool Binary_Expression::operator==(const Expression& rhs) const { if (auto m = Cast(&rhs)) { return type() == m->type() && *left() == *m->left() && *right() == *m->right(); } return false; } size_t Binary_Expression::hash() const { if (hash_ == 0) { hash_ = std::hash()(optype()); hash_combine(hash_, left()->hash()); hash_combine(hash_, right()->hash()); } return hash_; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Function::Function(SourceSpan pstate, Definition_Obj def, bool css) : Value(pstate), definition_(def), is_css_(css) { concrete_type(FUNCTION_VAL); } Function::Function(const Function* ptr) : Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_) { concrete_type(FUNCTION_VAL); } bool Function::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { auto d1 = Cast(definition()); auto d2 = Cast(r->definition()); if (d1 == nullptr) return d2 != nullptr; else if (d2 == nullptr) return false; if (is_css() == r->is_css()) { return d1 < d2; } return r->is_css(); } // compare/sort by type return type() < rhs.type(); } bool Function::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { auto d1 = Cast(definition()); auto d2 = Cast(r->definition()); return d1 && d2 && d1 == d2 && is_css() == r->is_css(); } return false; } sass::string Function::name() { if (definition_) { return definition_->name(); } return ""; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Function_Call::Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, void* cookie) : PreValue(pstate), sname_(n), arguments_(args), func_(), via_call_(false), cookie_(cookie), hash_(0) { concrete_type(FUNCTION); } Function_Call::Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, Function_Obj func) : PreValue(pstate), sname_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0) { concrete_type(FUNCTION); } Function_Call::Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args) : PreValue(pstate), sname_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0) { concrete_type(FUNCTION); } Function_Call::Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, void* cookie) : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(), via_call_(false), cookie_(cookie), hash_(0) { concrete_type(FUNCTION); } Function_Call::Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Function_Obj func) : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0) { concrete_type(FUNCTION); } Function_Call::Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args) : PreValue(pstate), sname_(SASS_MEMORY_NEW(String_Constant, pstate, n)), arguments_(args), via_call_(false), cookie_(0), hash_(0) { concrete_type(FUNCTION); } Function_Call::Function_Call(const Function_Call* ptr) : PreValue(ptr), sname_(ptr->sname_), arguments_(ptr->arguments_), func_(ptr->func_), via_call_(ptr->via_call_), cookie_(ptr->cookie_), hash_(ptr->hash_) { concrete_type(FUNCTION); } bool Function_Call::operator==(const Expression& rhs) const { if (auto m = Cast(&rhs)) { if (*sname() != *m->sname()) return false; if (arguments()->length() != m->arguments()->length()) return false; for (size_t i = 0, L = arguments()->length(); i < L; ++i) if (*arguments()->get(i) != *m->arguments()->get(i)) return false; return true; } return false; } size_t Function_Call::hash() const { if (hash_ == 0) { hash_ = std::hash()(name()); for (auto argument : arguments()->elements()) hash_combine(hash_, argument->hash()); } return hash_; } sass::string Function_Call::name() const { return sname(); } bool Function_Call::is_css() { if (func_) return func_->is_css(); return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Variable::Variable(SourceSpan pstate, sass::string n) : PreValue(pstate), name_(n) { concrete_type(VARIABLE); } Variable::Variable(const Variable* ptr) : PreValue(ptr), name_(ptr->name_) { concrete_type(VARIABLE); } bool Variable::operator==(const Expression& rhs) const { if (auto e = Cast(&rhs)) { return name() == e->name(); } return false; } size_t Variable::hash() const { return std::hash()(name()); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Number::Number(SourceSpan pstate, double val, sass::string u, bool zero) : Value(pstate), Units(), value_(val), zero_(zero), hash_(0) { size_t l = 0; size_t r; if (!u.empty()) { bool nominator = true; while (true) { r = u.find_first_of("*/", l); sass::string unit(u.substr(l, r == sass::string::npos ? r : r - l)); if (!unit.empty()) { if (nominator) numerators.push_back(unit); else denominators.push_back(unit); } if (r == sass::string::npos) break; // ToDo: should error for multiple slashes // if (!nominator && u[r] == '/') error(...) if (u[r] == '/') nominator = false; // strange math parsing? // else if (u[r] == '*') // nominator = true; l = r + 1; } } concrete_type(NUMBER); } Number::Number(const Number* ptr) : Value(ptr), Units(ptr), value_(ptr->value_), zero_(ptr->zero_), hash_(ptr->hash_) { concrete_type(NUMBER); } // cancel out unnecessary units void Number::reduce() { // apply conversion factor value_ *= this->Units::reduce(); } void Number::normalize() { // apply conversion factor value_ *= this->Units::normalize(); } size_t Number::hash() const { if (hash_ == 0) { hash_ = std::hash()(value_); for (const auto& numerator : numerators) hash_combine(hash_, std::hash()(numerator)); for (const auto& denominator : denominators) hash_combine(hash_, std::hash()(denominator)); } return hash_; } bool Number::operator< (const Expression& rhs) const { if (auto n = Cast(&rhs)) { return *this < *n; } return false; } bool Number::operator== (const Expression& rhs) const { if (auto n = Cast(&rhs)) { return *this == *n; } return false; } bool Number::operator== (const Number& rhs) const { // unitless or only having one unit are equivalent (3.4) // therefore we need to reduce the units beforehand Number l(*this), r(rhs); l.reduce(); r.reduce(); size_t lhs_units = l.numerators.size() + l.denominators.size(); size_t rhs_units = r.numerators.size() + r.denominators.size(); if (!lhs_units || !rhs_units) { return NEAR_EQUAL(l.value(), r.value()); } // ensure both have same units l.normalize(); r.normalize(); Units &lhs_unit = l, &rhs_unit = r; return lhs_unit == rhs_unit && NEAR_EQUAL(l.value(), r.value()); } bool Number::operator< (const Number& rhs) const { // unitless or only having one unit are equivalent (3.4) // therefore we need to reduce the units beforehand Number l(*this), r(rhs); l.reduce(); r.reduce(); size_t lhs_units = l.numerators.size() + l.denominators.size(); size_t rhs_units = r.numerators.size() + r.denominators.size(); if (!lhs_units || !rhs_units) { return l.value() < r.value(); } // ensure both have same units l.normalize(); r.normalize(); Units &lhs_unit = l, &rhs_unit = r; if (!(lhs_unit == rhs_unit)) { /* ToDo: do we always get useful backtraces? */ throw Exception::IncompatibleUnits(rhs, *this); } if (lhs_unit == rhs_unit) { return l.value() < r.value(); } else { return lhs_unit < rhs_unit; } } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Color::Color(SourceSpan pstate, double a, const sass::string disp) : Value(pstate), disp_(disp), a_(a), hash_(0) { concrete_type(COLOR); } Color::Color(const Color* ptr) : Value(ptr->pstate()), // reset on copy disp_(""), a_(ptr->a_), hash_(ptr->hash_) { concrete_type(COLOR); } bool Color::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return *this < *r; } else if (auto r = Cast(&rhs)) { return *this < *r; } else if (auto r = Cast(&rhs)) { return a_ < r->a(); } // compare/sort by type return type() < rhs.type(); } bool Color::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return *this == *r; } else if (auto r = Cast(&rhs)) { return *this == *r; } else if (auto r = Cast(&rhs)) { return a_ == r->a(); } return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Color_RGBA::Color_RGBA(SourceSpan pstate, double r, double g, double b, double a, const sass::string disp) : Color(pstate, a, disp), r_(r), g_(g), b_(b) { concrete_type(COLOR); } Color_RGBA::Color_RGBA(const Color_RGBA* ptr) : Color(ptr), r_(ptr->r_), g_(ptr->g_), b_(ptr->b_) { concrete_type(COLOR); } bool Color_RGBA::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (r_ < r->r()) return true; if (r_ > r->r()) return false; if (g_ < r->g()) return true; if (g_ > r->g()) return false; if (b_ < r->b()) return true; if (b_ > r->b()) return false; if (a_ < r->a()) return true; if (a_ > r->a()) return false; return false; // is equal } // compare/sort by type return type() < rhs.type(); } bool Color_RGBA::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return r_ == r->r() && g_ == r->g() && b_ == r->b() && a_ == r->a(); } return false; } size_t Color_RGBA::hash() const { if (hash_ == 0) { hash_ = std::hash()("RGBA"); hash_combine(hash_, std::hash()(a_)); hash_combine(hash_, std::hash()(r_)); hash_combine(hash_, std::hash()(g_)); hash_combine(hash_, std::hash()(b_)); } return hash_; } Color_HSLA* Color_RGBA::copyAsHSLA() const { // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV double r = r_ / 255.0; double g = g_ / 255.0; double b = b_ / 255.0; double max = std::max(r, std::max(g, b)); double min = std::min(r, std::min(g, b)); double delta = max - min; double h = 0; double s; double l = (max + min) / 2.0; if (NEAR_EQUAL(max, min)) { h = s = 0; // achromatic } else { if (l < 0.5) s = delta / (max + min); else s = delta / (2.0 - max - min); if (r == max) h = (g - b) / delta + (g < b ? 6 : 0); else if (g == max) h = (b - r) / delta + 2; else if (b == max) h = (r - g) / delta + 4; } // HSL hsl_struct; h = h * 60; s = s * 100; l = l * 100; return SASS_MEMORY_NEW(Color_HSLA, pstate(), h, s, l, a(), "" ); } Color_RGBA* Color_RGBA::copyAsRGBA() const { return SASS_MEMORY_COPY(this); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Color_HSLA::Color_HSLA(SourceSpan pstate, double h, double s, double l, double a, const sass::string disp) : Color(pstate, a, disp), h_(absmod(h, 360.0)), s_(clip(s, 0.0, 100.0)), l_(clip(l, 0.0, 100.0)) // hash_(0) { concrete_type(COLOR); } Color_HSLA::Color_HSLA(const Color_HSLA* ptr) : Color(ptr), h_(ptr->h_), s_(ptr->s_), l_(ptr->l_) // hash_(ptr->hash_) { concrete_type(COLOR); } bool Color_HSLA::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (h_ < r->h()) return true; if (h_ > r->h()) return false; if (s_ < r->s()) return true; if (s_ > r->s()) return false; if (l_ < r->l()) return true; if (l_ > r->l()) return false; if (a_ < r->a()) return true; if (a_ > r->a()) return false; return false; // is equal } // compare/sort by type return type() < rhs.type(); } bool Color_HSLA::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return h_ == r->h() && s_ == r->s() && l_ == r->l() && a_ == r->a(); } return false; } size_t Color_HSLA::hash() const { if (hash_ == 0) { hash_ = std::hash()("HSLA"); hash_combine(hash_, std::hash()(a_)); hash_combine(hash_, std::hash()(h_)); hash_combine(hash_, std::hash()(s_)); hash_combine(hash_, std::hash()(l_)); } return hash_; } // hue to RGB helper function double h_to_rgb(double m1, double m2, double h) { h = absmod(h, 1.0); if (h*6.0 < 1) return m1 + (m2 - m1)*h*6; if (h*2.0 < 1) return m2; if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6; return m1; } Color_RGBA* Color_HSLA::copyAsRGBA() const { double h = absmod(h_ / 360.0, 1.0); double s = clip(s_ / 100.0, 0.0, 1.0); double l = clip(l_ / 100.0, 0.0, 1.0); // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color. double m2; if (l <= 0.5) m2 = l*(s+1.0); else m2 = (l+s)-(l*s); double m1 = (l*2.0)-m2; // round the results -- consider moving this into the Color constructor double r = (h_to_rgb(m1, m2, h + 1.0/3.0) * 255.0); double g = (h_to_rgb(m1, m2, h) * 255.0); double b = (h_to_rgb(m1, m2, h - 1.0/3.0) * 255.0); return SASS_MEMORY_NEW(Color_RGBA, pstate(), r, g, b, a(), "" ); } Color_HSLA* Color_HSLA::copyAsHSLA() const { return SASS_MEMORY_COPY(this); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Custom_Error::Custom_Error(SourceSpan pstate, sass::string msg) : Value(pstate), message_(msg) { concrete_type(C_ERROR); } Custom_Error::Custom_Error(const Custom_Error* ptr) : Value(ptr), message_(ptr->message_) { concrete_type(C_ERROR); } bool Custom_Error::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return message() < r->message(); } // compare/sort by type return type() < rhs.type(); } bool Custom_Error::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return message() == r->message(); } return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Custom_Warning::Custom_Warning(SourceSpan pstate, sass::string msg) : Value(pstate), message_(msg) { concrete_type(C_WARNING); } Custom_Warning::Custom_Warning(const Custom_Warning* ptr) : Value(ptr), message_(ptr->message_) { concrete_type(C_WARNING); } bool Custom_Warning::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return message() < r->message(); } // compare/sort by type return type() < rhs.type(); } bool Custom_Warning::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return message() == r->message(); } return false; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Boolean::Boolean(SourceSpan pstate, bool val) : Value(pstate), value_(val), hash_(0) { concrete_type(BOOLEAN); } Boolean::Boolean(const Boolean* ptr) : Value(ptr), value_(ptr->value_), hash_(ptr->hash_) { concrete_type(BOOLEAN); } bool Boolean::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return (value() < r->value()); } return false; } bool Boolean::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { return (value() == r->value()); } return false; } size_t Boolean::hash() const { if (hash_ == 0) { hash_ = std::hash()(value_); } return hash_; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// String::String(SourceSpan pstate, bool delayed) : Value(pstate, delayed) { concrete_type(STRING); } String::String(const String* ptr) : Value(ptr) { concrete_type(STRING); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// String_Schema::String_Schema(SourceSpan pstate, size_t size, bool css) : String(pstate), Vectorized(size), css_(css), hash_(0) { concrete_type(STRING); } String_Schema::String_Schema(const String_Schema* ptr) : String(ptr), Vectorized(*ptr), css_(ptr->css_), hash_(ptr->hash_) { concrete_type(STRING); } void String_Schema::rtrim() { if (!empty()) { if (String* str = Cast(last())) str->rtrim(); } } bool String_Schema::is_left_interpolant(void) const { return length() && first()->is_left_interpolant(); } bool String_Schema::is_right_interpolant(void) const { return length() && last()->is_right_interpolant(); } bool String_Schema::operator< (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (length() < r->length()) return true; if (length() > r->length()) return false; for (size_t i = 0, L = length(); i < L; ++i) { if (*get(i) < *r->get(i)) return true; if (*get(i) == *r->get(i)) continue; return false; } // Is equal return false; } // compare/sort by type return type() < rhs.type(); } bool String_Schema::operator== (const Expression& rhs) const { if (auto r = Cast(&rhs)) { if (length() != r->length()) return false; for (size_t i = 0, L = length(); i < L; ++i) { auto rv = (*r)[i]; auto lv = (*this)[i]; if (*lv != *rv) return false; } return true; } return false; } bool String_Schema::has_interpolants() { for (auto el : elements()) { if (el->is_interpolant()) return true; } return false; } size_t String_Schema::hash() const { if (hash_ == 0) { for (auto string : elements()) hash_combine(hash_, string->hash()); } return hash_; } void String_Schema::set_delayed(bool delayed) { is_delayed(delayed); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// String_Constant::String_Constant(SourceSpan pstate, sass::string val, bool css) : String(pstate), quote_mark_(0), value_(read_css_string(val, css)), hash_(0) { } String_Constant::String_Constant(SourceSpan pstate, const char* beg, bool css) : String(pstate), quote_mark_(0), value_(read_css_string(sass::string(beg), css)), hash_(0) { } String_Constant::String_Constant(SourceSpan pstate, const char* beg, const char* end, bool css) : String(pstate), quote_mark_(0), value_(read_css_string(sass::string(beg, end-beg), css)), hash_(0) { } String_Constant::String_Constant(SourceSpan pstate, const Token& tok, bool css) : String(pstate), quote_mark_(0), value_(read_css_string(sass::string(tok.begin, tok.end), css)), hash_(0) { } String_Constant::String_Constant(const String_Constant* ptr) : String(ptr), quote_mark_(ptr->quote_mark_), value_(ptr->value_), hash_(ptr->hash_) { } bool String_Constant::is_invisible() const { return value_.empty() && quote_mark_ == 0; } bool String_Constant::operator< (const Expression& rhs) const { if (auto qstr = Cast(&rhs)) { return value() < qstr->value(); } else if (auto cstr = Cast(&rhs)) { return value() < cstr->value(); } // compare/sort by type return type() < rhs.type(); } bool String_Constant::operator== (const Expression& rhs) const { if (auto qstr = Cast(&rhs)) { return value() == qstr->value(); } else if (auto cstr = Cast(&rhs)) { return value() == cstr->value(); } return false; } sass::string String_Constant::inspect() const { return quote(value_, '*'); } void String_Constant::rtrim() { str_rtrim(value_); } size_t String_Constant::hash() const { if (hash_ == 0) { hash_ = std::hash()(value_); } return hash_; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// String_Quoted::String_Quoted(SourceSpan pstate, sass::string val, char q, bool keep_utf8_escapes, bool skip_unquoting, bool strict_unquoting, bool css) : String_Constant(pstate, val, css) { if (skip_unquoting == false) { value_ = unquote(value_, "e_mark_, keep_utf8_escapes, strict_unquoting); } if (q && quote_mark_) quote_mark_ = q; } String_Quoted::String_Quoted(const String_Quoted* ptr) : String_Constant(ptr) { } bool String_Quoted::operator< (const Expression& rhs) const { if (auto qstr = Cast(&rhs)) { return value() < qstr->value(); } else if (auto cstr = Cast(&rhs)) { return value() < cstr->value(); } // compare/sort by type return type() < rhs.type(); } bool String_Quoted::operator== (const Expression& rhs) const { if (auto qstr = Cast(&rhs)) { return value() == qstr->value(); } else if (auto cstr = Cast(&rhs)) { return value() == cstr->value(); } return false; } sass::string String_Quoted::inspect() const { return quote(value_, '*'); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Null::Null(SourceSpan pstate) : Value(pstate) { concrete_type(NULL_VAL); } Null::Null(const Null* ptr) : Value(ptr) { concrete_type(NULL_VAL); } bool Null::operator< (const Expression& rhs) const { if (Cast(&rhs)) { return false; } // compare/sort by type return type() < rhs.type(); } bool Null::operator== (const Expression& rhs) const { return Cast(&rhs) != nullptr; } size_t Null::hash() const { return -1; } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// Parent_Reference::Parent_Reference(SourceSpan pstate) : Value(pstate) { concrete_type(PARENT); } Parent_Reference::Parent_Reference(const Parent_Reference* ptr) : Value(ptr) { concrete_type(PARENT); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// IMPLEMENT_AST_OPERATORS(List); IMPLEMENT_AST_OPERATORS(Map); IMPLEMENT_AST_OPERATORS(Binary_Expression); IMPLEMENT_AST_OPERATORS(Function); IMPLEMENT_AST_OPERATORS(Function_Call); IMPLEMENT_AST_OPERATORS(Variable); IMPLEMENT_AST_OPERATORS(Number); IMPLEMENT_AST_OPERATORS(Color_RGBA); IMPLEMENT_AST_OPERATORS(Color_HSLA); IMPLEMENT_AST_OPERATORS(Custom_Error); IMPLEMENT_AST_OPERATORS(Custom_Warning); IMPLEMENT_AST_OPERATORS(Boolean); IMPLEMENT_AST_OPERATORS(String_Schema); IMPLEMENT_AST_OPERATORS(String_Constant); IMPLEMENT_AST_OPERATORS(String_Quoted); IMPLEMENT_AST_OPERATORS(Null); IMPLEMENT_AST_OPERATORS(Parent_Reference); ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// } golibsass-1.0.0/libsass_src/src/ast_values.hpp000066400000000000000000000417531405214413600214650ustar00rootroot00000000000000#ifndef SASS_AST_VALUES_H #define SASS_AST_VALUES_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" namespace Sass { ////////////////////////////////////////////////////////////////////// // Still just an expression, but with a to_string method ////////////////////////////////////////////////////////////////////// class PreValue : public Expression { public: PreValue(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE); ATTACH_VIRTUAL_AST_OPERATIONS(PreValue); virtual ~PreValue() { } }; ////////////////////////////////////////////////////////////////////// // base class for values that support operations ////////////////////////////////////////////////////////////////////// class Value : public PreValue { public: Value(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE); // Some obects are not meant to be compared // ToDo: maybe fallback to pointer comparison? virtual bool operator< (const Expression& rhs) const override = 0; virtual bool operator== (const Expression& rhs) const override = 0; // We can give some reasonable implementations by using // inverst operators on the specialized implementations virtual bool operator> (const Expression& rhs) const { return rhs < *this; } virtual bool operator!= (const Expression& rhs) const { return !(*this == rhs); } ATTACH_VIRTUAL_AST_OPERATIONS(Value); }; /////////////////////////////////////////////////////////////////////// // Lists of values, both comma- and space-separated (distinguished by a // type-tag.) Also used to represent variable-length argument lists. /////////////////////////////////////////////////////////////////////// class List : public Value, public Vectorized { void adjust_after_pushing(ExpressionObj e) override { is_expanded(false); } private: ADD_PROPERTY(enum Sass_Separator, separator) ADD_PROPERTY(bool, is_arglist) ADD_PROPERTY(bool, is_bracketed) ADD_PROPERTY(bool, from_selector) public: List(SourceSpan pstate, size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false); sass::string type() const override { return is_arglist_ ? "arglist" : "list"; } static sass::string type_name() { return "list"; } const char* sep_string(bool compressed = false) const { return separator() == SASS_SPACE ? " " : (compressed ? "," : ", "); } bool is_invisible() const override { return empty() && !is_bracketed(); } ExpressionObj value_at_index(size_t i); virtual size_t hash() const override; virtual size_t size() const; virtual void set_delayed(bool delayed) override; virtual bool operator< (const Expression& rhs) const override; virtual bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(List) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////////////////////////////////////////////////////// // Key value paris. /////////////////////////////////////////////////////////////////////// class Map : public Value, public Hashed { void adjust_after_pushing(std::pair p) override { is_expanded(false); } public: Map(SourceSpan pstate, size_t size = 0); sass::string type() const override { return "map"; } static sass::string type_name() { return "map"; } bool is_invisible() const override { return empty(); } List_Obj to_list(SourceSpan& pstate); virtual size_t hash() const override; virtual bool operator< (const Expression& rhs) const override; virtual bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Map) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////////////////////////////////////////////// // Binary expressions. Represents logical, relational, and arithmetic // operations. Templatized to avoid large switch statements and repetitive // subclassing. ////////////////////////////////////////////////////////////////////////// class Binary_Expression : public PreValue { private: HASH_PROPERTY(Operand, op) HASH_PROPERTY(ExpressionObj, left) HASH_PROPERTY(ExpressionObj, right) mutable size_t hash_; public: Binary_Expression(SourceSpan pstate, Operand op, ExpressionObj lhs, ExpressionObj rhs); const sass::string type_name(); const sass::string separator(); bool is_left_interpolant(void) const override; bool is_right_interpolant(void) const override; bool has_interpolant() const override; virtual void set_delayed(bool delayed) override; virtual bool operator< (const Expression& rhs) const override; virtual bool operator==(const Expression& rhs) const override; virtual size_t hash() const override; enum Sass_OP optype() const { return op_.operand; } ATTACH_AST_OPERATIONS(Binary_Expression) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////// // Function reference. //////////////////////////////////////////////////// class Function final : public Value { public: ADD_PROPERTY(Definition_Obj, definition) ADD_PROPERTY(bool, is_css) public: Function(SourceSpan pstate, Definition_Obj def, bool css); sass::string type() const override { return "function"; } static sass::string type_name() { return "function"; } bool is_invisible() const override { return true; } sass::string name(); bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Function) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////// // Function calls. ////////////////// class Function_Call final : public PreValue { HASH_CONSTREF(String_Obj, sname) HASH_PROPERTY(Arguments_Obj, arguments) HASH_PROPERTY(Function_Obj, func) ADD_PROPERTY(bool, via_call) ADD_PROPERTY(void*, cookie) mutable size_t hash_; public: Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, void* cookie); Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Function_Obj func); Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args); Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, void* cookie); Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, Function_Obj func); Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args); sass::string name() const; bool is_css(); bool operator==(const Expression& rhs) const override; size_t hash() const override; ATTACH_AST_OPERATIONS(Function_Call) ATTACH_CRTP_PERFORM_METHODS() }; /////////////////////// // Variable references. /////////////////////// class Variable final : public PreValue { ADD_CONSTREF(sass::string, name) public: Variable(SourceSpan pstate, sass::string n); virtual bool operator==(const Expression& rhs) const override; virtual size_t hash() const override; ATTACH_AST_OPERATIONS(Variable) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////// // Numbers, percentages, dimensions, and colors. //////////////////////////////////////////////// class Number final : public Value, public Units { HASH_PROPERTY(double, value) ADD_PROPERTY(bool, zero) mutable size_t hash_; public: Number(SourceSpan pstate, double val, sass::string u = "", bool zero = true); bool zero() { return zero_; } sass::string type() const override { return "number"; } static sass::string type_name() { return "number"; } // cancel out unnecessary units // result will be in input units void reduce(); // normalize units to defaults // needed to compare two numbers void normalize(); size_t hash() const override; bool operator< (const Number& rhs) const; bool operator== (const Number& rhs) const; bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Number) ATTACH_CRTP_PERFORM_METHODS() }; ////////// // Colors. ////////// class Color : public Value { ADD_CONSTREF(sass::string, disp) HASH_PROPERTY(double, a) protected: mutable size_t hash_; public: Color(SourceSpan pstate, double a = 1, const sass::string disp = ""); sass::string type() const override { return "color"; } static sass::string type_name() { return "color"; } virtual size_t hash() const override = 0; bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; virtual Color_RGBA* copyAsRGBA() const = 0; virtual Color_RGBA* toRGBA() = 0; virtual Color_HSLA* copyAsHSLA() const = 0; virtual Color_HSLA* toHSLA() = 0; ATTACH_VIRTUAL_AST_OPERATIONS(Color) }; ////////// // Colors. ////////// class Color_RGBA final : public Color { HASH_PROPERTY(double, r) HASH_PROPERTY(double, g) HASH_PROPERTY(double, b) public: Color_RGBA(SourceSpan pstate, double r, double g, double b, double a = 1, const sass::string disp = ""); sass::string type() const override { return "color"; } static sass::string type_name() { return "color"; } size_t hash() const override; Color_RGBA* copyAsRGBA() const override; Color_RGBA* toRGBA() override { return this; } Color_HSLA* copyAsHSLA() const override; Color_HSLA* toHSLA() override { return copyAsHSLA(); } bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Color_RGBA) ATTACH_CRTP_PERFORM_METHODS() }; ////////// // Colors. ////////// class Color_HSLA final : public Color { HASH_PROPERTY(double, h) HASH_PROPERTY(double, s) HASH_PROPERTY(double, l) public: Color_HSLA(SourceSpan pstate, double h, double s, double l, double a = 1, const sass::string disp = ""); sass::string type() const override { return "color"; } static sass::string type_name() { return "color"; } size_t hash() const override; Color_RGBA* copyAsRGBA() const override; Color_RGBA* toRGBA() override { return copyAsRGBA(); } Color_HSLA* copyAsHSLA() const override; Color_HSLA* toHSLA() override { return this; } bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Color_HSLA) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////// // Errors from Sass_Values. ////////////////////////////// class Custom_Error final : public Value { ADD_CONSTREF(sass::string, message) public: Custom_Error(SourceSpan pstate, sass::string msg); bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Custom_Error) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////// // Warnings from Sass_Values. ////////////////////////////// class Custom_Warning final : public Value { ADD_CONSTREF(sass::string, message) public: Custom_Warning(SourceSpan pstate, sass::string msg); bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Custom_Warning) ATTACH_CRTP_PERFORM_METHODS() }; //////////// // Booleans. //////////// class Boolean final : public Value { HASH_PROPERTY(bool, value) mutable size_t hash_; public: Boolean(SourceSpan pstate, bool val); operator bool() override { return value_; } sass::string type() const override { return "bool"; } static sass::string type_name() { return "bool"; } size_t hash() const override; bool is_false() override { return !value_; } bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Boolean) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////////////////////// // Abstract base class for Sass string values. Includes interpolated and // "flat" strings. //////////////////////////////////////////////////////////////////////// class String : public Value { public: String(SourceSpan pstate, bool delayed = false); static sass::string type_name() { return "string"; } virtual ~String() = 0; virtual void rtrim() = 0; virtual bool operator<(const Expression& rhs) const override { return this->to_string() < rhs.to_string(); }; virtual bool operator==(const Expression& rhs) const override { return this->to_string() == rhs.to_string(); }; ATTACH_VIRTUAL_AST_OPERATIONS(String); ATTACH_CRTP_PERFORM_METHODS() }; inline String::~String() { }; /////////////////////////////////////////////////////////////////////// // Interpolated strings. Meant to be reduced to flat strings during the // evaluation phase. /////////////////////////////////////////////////////////////////////// class String_Schema final : public String, public Vectorized { ADD_PROPERTY(bool, css) mutable size_t hash_; public: String_Schema(SourceSpan pstate, size_t size = 0, bool css = true); sass::string type() const override { return "string"; } static sass::string type_name() { return "string"; } bool is_left_interpolant(void) const override; bool is_right_interpolant(void) const override; bool has_interpolants(); void rtrim() override; size_t hash() const override; virtual void set_delayed(bool delayed) override; bool operator< (const Expression& rhs) const override; bool operator==(const Expression& rhs) const override; ATTACH_AST_OPERATIONS(String_Schema) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////// // Flat strings -- the lowest level of raw textual data. //////////////////////////////////////////////////////// class String_Constant : public String { ADD_PROPERTY(char, quote_mark) HASH_CONSTREF(sass::string, value) protected: mutable size_t hash_; public: String_Constant(SourceSpan pstate, sass::string val, bool css = true); String_Constant(SourceSpan pstate, const char* beg, bool css = true); String_Constant(SourceSpan pstate, const char* beg, const char* end, bool css = true); String_Constant(SourceSpan pstate, const Token& tok, bool css = true); sass::string type() const override { return "string"; } static sass::string type_name() { return "string"; } bool is_invisible() const override; virtual void rtrim() override; size_t hash() const override; bool operator< (const Expression& rhs) const override; bool operator==(const Expression& rhs) const override; // quotes are forced on inspection virtual sass::string inspect() const override; ATTACH_AST_OPERATIONS(String_Constant) ATTACH_CRTP_PERFORM_METHODS() }; //////////////////////////////////////////////////////// // Possibly quoted string (unquote on instantiation) //////////////////////////////////////////////////////// class String_Quoted final : public String_Constant { public: String_Quoted(SourceSpan pstate, sass::string val, char q = 0, bool keep_utf8_escapes = false, bool skip_unquoting = false, bool strict_unquoting = true, bool css = true); bool operator< (const Expression& rhs) const override; bool operator==(const Expression& rhs) const override; // quotes are forced on inspection sass::string inspect() const override; ATTACH_AST_OPERATIONS(String_Quoted) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////// // The null value. ////////////////// class Null final : public Value { public: Null(SourceSpan pstate); sass::string type() const override { return "null"; } static sass::string type_name() { return "null"; } bool is_invisible() const override { return true; } operator bool() override { return false; } bool is_false() override { return true; } size_t hash() const override; bool operator< (const Expression& rhs) const override; bool operator== (const Expression& rhs) const override; ATTACH_AST_OPERATIONS(Null) ATTACH_CRTP_PERFORM_METHODS() }; ////////////////////////////////// // The Parent Reference Expression. ////////////////////////////////// class Parent_Reference final : public Value { public: Parent_Reference(SourceSpan pstate); sass::string type() const override { return "parent"; } static sass::string type_name() { return "parent"; } bool operator< (const Expression& rhs) const override { return false; // they are always equal } bool operator==(const Expression& rhs) const override { return true; // they are always equal }; ATTACH_AST_OPERATIONS(Parent_Reference) ATTACH_CRTP_PERFORM_METHODS() }; } #endif golibsass-1.0.0/libsass_src/src/b64/000077500000000000000000000000001405214413600171675ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/src/b64/cencode.h000066400000000000000000000013251405214413600207410ustar00rootroot00000000000000/* cencode.h - c header for a base64 encoding algorithm This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_CENCODE_H #define BASE64_CENCODE_H typedef enum { step_A, step_B, step_C } base64_encodestep; typedef struct { base64_encodestep step; char result; int stepcount; } base64_encodestate; void base64_init_encodestate(base64_encodestate* state_in); char base64_encode_value(char value_in); int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); int base64_encode_blockend(char* code_out, base64_encodestate* state_in); #endif /* BASE64_CENCODE_H */ golibsass-1.0.0/libsass_src/src/b64/encode.h000066400000000000000000000030761405214413600206030ustar00rootroot00000000000000// :mode=c++: /* encode.h - c++ wrapper for a base64 encoding algorithm This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_ENCODE_H #define BASE64_ENCODE_H #include namespace base64 { extern "C" { #include "cencode.h" } struct encoder { base64_encodestate _state; int _buffersize; encoder(int buffersize_in = BUFFERSIZE) : _buffersize(buffersize_in) { base64_init_encodestate(&_state); } int encode(char value_in) { return base64_encode_value(value_in); } int encode(const char* code_in, const int length_in, char* plaintext_out) { return base64_encode_block(code_in, length_in, plaintext_out, &_state); } int encode_end(char* plaintext_out) { return base64_encode_blockend(plaintext_out, &_state); } void encode(std::istream& istream_in, std::ostream& ostream_in) { base64_init_encodestate(&_state); // const int N = _buffersize; char* plaintext = new char[N]; char* code = new char[2*N]; int plainlength; int codelength; do { istream_in.read(plaintext, N); plainlength = static_cast(istream_in.gcount()); // codelength = encode(plaintext, plainlength, code); ostream_in.write(code, codelength); } while (istream_in.good() && plainlength > 0); codelength = encode_end(code); ostream_in.write(code, codelength); // base64_init_encodestate(&_state); delete [] code; delete [] plaintext; } }; } // namespace base64 #endif // BASE64_ENCODE_H golibsass-1.0.0/libsass_src/src/backtrace.cpp000066400000000000000000000022751405214413600212250ustar00rootroot00000000000000#include "backtrace.hpp" namespace Sass { const sass::string traces_to_string(Backtraces traces, sass::string indent) { sass::ostream ss; sass::string cwd(File::get_cwd()); bool first = true; size_t i_beg = traces.size() - 1; size_t i_end = sass::string::npos; for (size_t i = i_beg; i != i_end; i --) { const Backtrace& trace = traces[i]; // make path relative to the current directory sass::string rel_path(File::abs2rel(trace.pstate.getPath(), cwd, cwd)); // skip functions on error cases (unsure why ruby sass does this) // if (trace.caller.substr(0, 6) == ", in f") continue; if (first) { ss << indent; ss << "on line "; ss << trace.pstate.getLine(); ss << ":"; ss << trace.pstate.getColumn(); ss << " of " << rel_path; // ss << trace.caller; first = false; } else { ss << trace.caller; ss << std::endl; ss << indent; ss << "from line "; ss << trace.pstate.getLine(); ss << ":"; ss << trace.pstate.getColumn(); ss << " of " << rel_path; } } ss << std::endl; return ss.str(); } }; golibsass-1.0.0/libsass_src/src/backtrace.hpp000066400000000000000000000007311405214413600212250ustar00rootroot00000000000000#ifndef SASS_BACKTRACE_H #define SASS_BACKTRACE_H #include #include #include "file.hpp" #include "position.hpp" namespace Sass { struct Backtrace { SourceSpan pstate; sass::string caller; Backtrace(SourceSpan pstate, sass::string c = "") : pstate(pstate), caller(c) { } }; typedef sass::vector Backtraces; const sass::string traces_to_string(Backtraces traces, sass::string indent = "\t"); } #endif golibsass-1.0.0/libsass_src/src/base64vlq.cpp000066400000000000000000000021771405214413600211160ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "base64vlq.hpp" namespace Sass { sass::string Base64VLQ::encode(const int number) const { sass::string encoded = ""; int vlq = to_vlq_signed(number); do { int digit = vlq & VLQ_BASE_MASK; vlq >>= VLQ_BASE_SHIFT; if (vlq > 0) { digit |= VLQ_CONTINUATION_BIT; } encoded += base64_encode(digit); } while (vlq > 0); return encoded; } char Base64VLQ::base64_encode(const int number) const { int index = number; if (index < 0) index = 0; if (index > 63) index = 63; return CHARACTERS[index]; } int Base64VLQ::to_vlq_signed(const int number) const { return (number < 0) ? ((-number) << 1) + 1 : (number << 1) + 0; } const char* Base64VLQ::CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const int Base64VLQ::VLQ_BASE_SHIFT = 5; const int Base64VLQ::VLQ_BASE = 1 << VLQ_BASE_SHIFT; const int Base64VLQ::VLQ_BASE_MASK = VLQ_BASE - 1; const int Base64VLQ::VLQ_CONTINUATION_BIT = VLQ_BASE; } golibsass-1.0.0/libsass_src/src/base64vlq.hpp000066400000000000000000000007361405214413600211220ustar00rootroot00000000000000#ifndef SASS_BASE64VLQ_H #define SASS_BASE64VLQ_H #include namespace Sass { class Base64VLQ { public: sass::string encode(const int number) const; private: char base64_encode(const int number) const; int to_vlq_signed(const int number) const; static const char* CHARACTERS; static const int VLQ_BASE_SHIFT; static const int VLQ_BASE; static const int VLQ_BASE_MASK; static const int VLQ_CONTINUATION_BIT; }; } #endif golibsass-1.0.0/libsass_src/src/bind.cpp000066400000000000000000000270611405214413600202220ustar00rootroot00000000000000#include "sass.hpp" #include "bind.hpp" #include "ast.hpp" #include "backtrace.hpp" #include "context.hpp" #include "expand.hpp" #include "eval.hpp" #include #include #include namespace Sass { void bind(sass::string type, sass::string name, Parameters_Obj ps, Arguments_Obj as, Env* env, Eval* eval, Backtraces& traces) { sass::string callee(type + " " + name); std::map param_map; List_Obj varargs = SASS_MEMORY_NEW(List, as->pstate()); varargs->is_arglist(true); // enable keyword size handling for (size_t i = 0, L = as->length(); i < L; ++i) { if (auto str = Cast((*as)[i]->value())) { // force optional quotes (only if needed) if (str->quote_mark()) { str->quote_mark('*'); } } } // Set up a map to ensure named arguments refer to actual parameters. Also // eval each default value left-to-right, wrt env, populating env as we go. for (size_t i = 0, L = ps->length(); i < L; ++i) { Parameter_Obj p = ps->at(i); param_map[p->name()] = p; // if (p->default_value()) { // env->local_frame()[p->name()] = p->default_value()->perform(eval->with(env)); // } } // plug in all args; if we have leftover params, deal with it later size_t ip = 0, LP = ps->length(); size_t ia = 0, LA = as->length(); while (ia < LA) { Argument_Obj a = as->at(ia); if (ip >= LP) { // skip empty rest arguments if (a->is_rest_argument()) { if (List_Obj l = Cast(a->value())) { if (l->length() == 0) { ++ ia; continue; } } } sass::ostream msg; msg << "wrong number of arguments (" << LA << " for " << LP << ")"; msg << " for `" << name << "'"; return error(msg.str(), as->pstate(), traces); } Parameter_Obj p = ps->at(ip); // If the current parameter is the rest parameter, process and break the loop if (p->is_rest_parameter()) { // The next argument by coincidence provides a rest argument if (a->is_rest_argument()) { // We should always get a list for rest arguments if (List_Obj rest = Cast(a->value())) { // create a new list object for wrapped items List* arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, rest->separator(), true); // wrap each item from list as an argument for (ExpressionObj item : rest->elements()) { if (Argument_Obj arg = Cast(item)) { arglist->append(SASS_MEMORY_COPY(arg)); // copy } else { arglist->append(SASS_MEMORY_NEW(Argument, item->pstate(), item, "", false, false)); } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; } // invalid state else { throw std::runtime_error("invalid state"); } } else if (a->is_keyword_argument()) { // expand keyword arguments into their parameters List* arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); env->local_frame()[p->name()] = arglist; Map_Obj argmap = Cast(a->value()); for (auto key : argmap->keys()) { if (String_Constant_Obj str = Cast(key)) { sass::string param = unquote(str->value()); arglist->append(SASS_MEMORY_NEW(Argument, key->pstate(), argmap->at(key), "$" + param, false, false)); } else { traces.push_back(Backtrace(key->pstate())); throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); } } } else { // create a new list object for wrapped items List_Obj arglist = SASS_MEMORY_NEW(List, p->pstate(), 0, SASS_COMMA, true); // consume the next args while (ia < LA) { // get and post inc a = (*as)[ia++]; // maybe we have another list as argument List_Obj ls = Cast(a->value()); // skip any list completely if empty if (ls && ls->empty() && a->is_rest_argument()) continue; ExpressionObj value = a->value(); if (Argument_Obj arg = Cast(value)) { arglist->append(arg); } // check if we have rest argument else if (a->is_rest_argument()) { // preserve the list separator from rest args if (List_Obj rest = Cast(a->value())) { arglist->separator(rest->separator()); for (size_t i = 0, L = rest->length(); i < L; ++i) { ExpressionObj obj = rest->value_at_index(i); arglist->append(SASS_MEMORY_NEW(Argument, obj->pstate(), obj, "", false, false)); } } // no more arguments break; } // wrap all other value types into Argument else { arglist->append(SASS_MEMORY_NEW(Argument, a->pstate(), a->value(), a->name(), false, false)); } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; } // consumed parameter ++ip; // no more parameters break; } // If the current argument is the rest argument, extract a value for processing else if (a->is_rest_argument()) { // normal param and rest arg List_Obj arglist = Cast(a->value()); if (!arglist) { if (ExpressionObj arg = Cast(a->value())) { arglist = SASS_MEMORY_NEW(List, a->pstate(), 1); arglist->append(arg); } } // empty rest arg - treat all args as default values if (!arglist || !arglist->length()) { break; } else { if (arglist->length() > LP - ip && !ps->has_rest_parameter()) { size_t arg_count = (arglist->length() + LA - 1); sass::ostream msg; msg << callee << " takes " << LP; msg << (LP == 1 ? " argument" : " arguments"); msg << " but " << arg_count; msg << (arg_count == 1 ? " was passed" : " were passed."); deprecated_bind(msg.str(), as->pstate()); while (arglist->length() > LP - ip) { arglist->elements().erase(arglist->elements().end() - 1); } } } // otherwise move one of the rest args into the param, converting to argument if necessary ExpressionObj obj = arglist->at(0); if (!(a = Cast(obj))) { Expression* a_to_convert = obj; a = SASS_MEMORY_NEW(Argument, a_to_convert->pstate(), a_to_convert, "", false, false); } arglist->elements().erase(arglist->elements().begin()); if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) { ++ia; } } else if (a->is_keyword_argument()) { Map_Obj argmap = Cast(a->value()); for (auto key : argmap->keys()) { String_Constant* val = Cast(key); if (val == NULL) { traces.push_back(Backtrace(key->pstate())); throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a); } sass::string param = "$" + unquote(val->value()); if (!param_map.count(param)) { sass::ostream msg; msg << callee << " has no parameter named " << param; error(msg.str(), a->pstate(), traces); } env->local_frame()[param] = argmap->at(key); } ++ia; continue; } else { ++ia; } if (a->name().empty()) { if (env->has_local(p->name())) { sass::ostream msg; msg << "parameter " << p->name() << " provided more than once in call to " << callee; error(msg.str(), a->pstate(), traces); } // ordinal arg -- bind it to the next param env->local_frame()[p->name()] = a->value(); ++ip; } else { // named arg -- bind it to the appropriately named param if (!param_map.count(a->name())) { if (ps->has_rest_parameter()) { varargs->append(a); } else { sass::ostream msg; msg << callee << " has no parameter named " << a->name(); error(msg.str(), a->pstate(), traces); } } if (param_map[a->name()]) { if (param_map[a->name()]->is_rest_parameter()) { sass::ostream msg; msg << "argument " << a->name() << " of " << callee << "cannot be used as named argument"; error(msg.str(), a->pstate(), traces); } } if (env->has_local(a->name())) { sass::ostream msg; msg << "parameter " << p->name() << "provided more than once in call to " << callee; error(msg.str(), a->pstate(), traces); } env->local_frame()[a->name()] = a->value(); } } // EO while ia // If we make it here, we're out of args but may have leftover params. // That's only okay if they have default values, or were already bound by // named arguments, or if it's a single rest-param. for (size_t i = ip; i < LP; ++i) { Parameter_Obj leftover = ps->at(i); // cerr << "env for default params:" << endl; // env->print(); // cerr << "********" << endl; if (!env->has_local(leftover->name())) { if (leftover->is_rest_parameter()) { env->local_frame()[leftover->name()] = varargs; } else if (leftover->default_value()) { Expression* dv = leftover->default_value()->perform(eval); env->local_frame()[leftover->name()] = dv; } else { // param is unbound and has no default value -- error throw Exception::MissingArgument(as->pstate(), traces, name, leftover->name(), type); } } } return; } } golibsass-1.0.0/libsass_src/src/bind.hpp000066400000000000000000000004341405214413600202220ustar00rootroot00000000000000#ifndef SASS_BIND_H #define SASS_BIND_H #include #include "backtrace.hpp" #include "environment.hpp" #include "ast_fwd_decl.hpp" namespace Sass { void bind(sass::string type, sass::string name, Parameters_Obj, Arguments_Obj, Env*, Eval*, Backtraces& traces); } #endif golibsass-1.0.0/libsass_src/src/c2ast.cpp000066400000000000000000000041761405214413600203240ustar00rootroot00000000000000#include "ast.hpp" #include "units.hpp" #include "position.hpp" #include "backtrace.hpp" #include "sass/values.h" #include "ast_fwd_decl.hpp" #include "error_handling.hpp" namespace Sass { Value* c2ast(union Sass_Value* v, Backtraces traces, SourceSpan pstate) { using std::strlen; using std::strcpy; Value* e = NULL; switch (sass_value_get_tag(v)) { case SASS_BOOLEAN: { e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v)); } break; case SASS_NUMBER: { e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v)); } break; case SASS_COLOR: { e = SASS_MEMORY_NEW(Color_RGBA, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); } break; case SASS_STRING: { if (sass_string_is_quoted(v)) e = SASS_MEMORY_NEW(String_Quoted, pstate, sass_string_get_value(v)); else { e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v)); } } break; case SASS_LIST: { List* l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v)); for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) { l->append(c2ast(sass_list_get_value(v, i), traces, pstate)); } l->is_bracketed(sass_list_get_is_bracketed(v)); e = l; } break; case SASS_MAP: { Map* m = SASS_MEMORY_NEW(Map, pstate); for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) { *m << std::make_pair( c2ast(sass_map_get_key(v, i), traces, pstate), c2ast(sass_map_get_value(v, i), traces, pstate)); } e = m; } break; case SASS_NULL: { e = SASS_MEMORY_NEW(Null, pstate); } break; case SASS_ERROR: { error("Error in C function: " + sass::string(sass_error_get_message(v)), pstate, traces); } break; case SASS_WARNING: { error("Warning in C function: " + sass::string(sass_warning_get_message(v)), pstate, traces); } break; default: break; } return e; } } golibsass-1.0.0/libsass_src/src/c2ast.hpp000066400000000000000000000003411405214413600203170ustar00rootroot00000000000000#ifndef SASS_C2AST_H #define SASS_C2AST_H #include "position.hpp" #include "backtrace.hpp" #include "ast_fwd_decl.hpp" namespace Sass { Value* c2ast(union Sass_Value* v, Backtraces traces, SourceSpan pstate); } #endif golibsass-1.0.0/libsass_src/src/c99func.c000066400000000000000000000033131405214413600202200ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if defined(_MSC_VER) && _MSC_VER < 1900 #include #include #include static int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) { int count = -1; if (size != 0) count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); if (count == -1) count = _vscprintf(format, ap); return count; } int snprintf(char* str, size_t size, const char* format, ...) { int count; va_list ap; va_start(ap, format); count = c99_vsnprintf(str, size, format, ap); va_end(ap); return count; } #endif golibsass-1.0.0/libsass_src/src/cencode.c000066400000000000000000000046701405214413600203470ustar00rootroot00000000000000/* cencoder.c - c source to a base64 encoding algorithm implementation This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #include "b64/cencode.h" void base64_init_encodestate(base64_encodestate* state_in) { state_in->step = step_A; state_in->result = 0; state_in->stepcount = 0; } char base64_encode_value(char value_in) { static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if (value_in > 63) return '='; return encoding[(int)value_in]; } int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) { const char* plainchar = plaintext_in; const char* const plaintextend = plaintext_in + length_in; char* codechar = code_out; char result; char fragment; result = state_in->result; switch (state_in->step) { while (1) { case step_A: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_A; return (int)(codechar - code_out); } fragment = *plainchar++; result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; /* fall through */ case step_B: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_B; return (int)(codechar - code_out); } fragment = *plainchar++; result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; /* fall through */ case step_C: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_C; return (int)(codechar - code_out); } fragment = *plainchar++; result |= (fragment & 0x0c0) >> 6; *codechar++ = base64_encode_value(result); result = (fragment & 0x03f) >> 0; *codechar++ = base64_encode_value(result); ++(state_in->stepcount); } } /* control should not reach here */ return (int)(codechar - code_out); } int base64_encode_blockend(char* code_out, base64_encodestate* state_in) { char* codechar = code_out; switch (state_in->step) { case step_B: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; *codechar++ = '='; break; case step_C: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; break; case step_A: break; } *codechar++ = '\n'; return (int)(codechar - code_out); } golibsass-1.0.0/libsass_src/src/check_nesting.cpp000066400000000000000000000240011405214413600221010ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "check_nesting.hpp" namespace Sass { CheckNesting::CheckNesting() : parents(sass::vector()), traces(sass::vector()), parent(0), current_mixin_definition(0) { } void error(AST_Node* node, Backtraces traces, sass::string msg) { traces.push_back(Backtrace(node->pstate())); throw Exception::InvalidSass(node->pstate(), traces, msg); } Statement* CheckNesting::visit_children(Statement* parent) { Statement* old_parent = this->parent; if (AtRootRule* root = Cast(parent)) { sass::vector old_parents = this->parents; sass::vector new_parents; for (size_t i = 0, L = this->parents.size(); i < L; i++) { Statement* p = this->parents.at(i); if (!root->exclude_node(p)) { new_parents.push_back(p); } } this->parents = new_parents; for (size_t i = this->parents.size(); i > 0; i--) { Statement* p = 0; Statement* gp = 0; if (i > 0) p = this->parents.at(i - 1); if (i > 1) gp = this->parents.at(i - 2); if (!this->is_transparent_parent(p, gp)) { this->parent = p; break; } } AtRootRule* ar = Cast(parent); Block* ret = ar->block(); if (ret != NULL) { for (auto n : ret->elements()) { n->perform(this); } } this->parent = old_parent; this->parents = old_parents; return ret; } if (!this->is_transparent_parent(parent, old_parent)) { this->parent = parent; } this->parents.push_back(parent); Block* b = Cast(parent); if (Trace* trace = Cast(parent)) { if (trace->type() == 'i') { this->traces.push_back(Backtrace(trace->pstate())); } } if (!b) { if (ParentStatement* bb = Cast(parent)) { b = bb->block(); } } if (b) { for (auto n : b->elements()) { n->perform(this); } } this->parent = old_parent; this->parents.pop_back(); if (Trace* trace = Cast(parent)) { if (trace->type() == 'i') { this->traces.pop_back(); } } return b; } Statement* CheckNesting::operator()(Block* b) { return this->visit_children(b); } Statement* CheckNesting::operator()(Definition* n) { if (!this->should_visit(n)) return NULL; if (!is_mixin(n)) { visit_children(n); return n; } Definition* old_mixin_definition = this->current_mixin_definition; this->current_mixin_definition = n; visit_children(n); this->current_mixin_definition = old_mixin_definition; return n; } Statement* CheckNesting::operator()(If* i) { this->visit_children(i); if (Block* b = Cast(i->alternative())) { for (auto n : b->elements()) n->perform(this); } return i; } bool CheckNesting::should_visit(Statement* node) { if (!this->parent) return true; if (Cast(node)) { this->invalid_content_parent(this->parent, node); } if (is_charset(node)) { this->invalid_charset_parent(this->parent, node); } if (Cast(node)) { this->invalid_extend_parent(this->parent, node); } // if (Cast(node)) // { this->invalid_import_parent(this->parent); } if (this->is_mixin(node)) { this->invalid_mixin_definition_parent(this->parent, node); } if (this->is_function(node)) { this->invalid_function_parent(this->parent, node); } if (this->is_function(this->parent)) { this->invalid_function_child(node); } if (Declaration* d = Cast(node)) { this->invalid_prop_parent(this->parent, node); this->invalid_value_child(d->value()); } if (Cast(this->parent)) { this->invalid_prop_child(node); } if (Cast(node)) { this->invalid_return_parent(this->parent, node); } return true; } void CheckNesting::invalid_content_parent(Statement* parent, AST_Node* node) { if (!this->current_mixin_definition) { error(node, traces, "@content may only be used within a mixin."); } } void CheckNesting::invalid_charset_parent(Statement* parent, AST_Node* node) { if (!( is_root_node(parent) )) { error(node, traces, "@charset may only be used at the root of a document."); } } void CheckNesting::invalid_extend_parent(Statement* parent, AST_Node* node) { if (!( Cast(parent) || Cast(parent) || is_mixin(parent) )) { error(node, traces, "Extend directives may only be used within rules."); } } // void CheckNesting::invalid_import_parent(Statement* parent, AST_Node* node) // { // for (auto pp : this->parents) { // if ( // Cast(pp) || // Cast(pp) || // Cast(pp) || // Cast(pp) || // Cast(pp) || // Cast(pp) || // is_mixin(pp) // ) { // error(node, traces, "Import directives may not be defined within control directives or other mixins."); // } // } // if (this->is_root_node(parent)) { // return; // } // if (false/*n.css_import?*/) { // error(node, traces, "CSS import directives may only be used at the root of a document."); // } // } void CheckNesting::invalid_mixin_definition_parent(Statement* parent, AST_Node* node) { for (Statement* pp : this->parents) { if ( Cast(pp) || Cast(pp) || Cast(pp) || Cast(pp) || Cast(pp) || Cast(pp) || is_mixin(pp) ) { error(node, traces, "Mixins may not be defined within control directives or other mixins."); } } } void CheckNesting::invalid_function_parent(Statement* parent, AST_Node* node) { for (Statement* pp : this->parents) { if ( Cast(pp) || Cast(pp) || Cast(pp) || Cast(pp) || Cast(pp) || Cast(pp) || is_mixin(pp) ) { error(node, traces, "Functions may not be defined within control directives or other mixins."); } } } void CheckNesting::invalid_function_child(Statement* child) { if (!( Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || // Ruby Sass doesn't distinguish variables and assignments Cast(child) || Cast(child) || Cast(child) )) { error(child, traces, "Functions can only contain variable declarations and control directives."); } } void CheckNesting::invalid_prop_child(Statement* child) { if (!( Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) || Cast(child) )) { error(child, traces, "Illegal nesting: Only properties may be nested beneath properties."); } } void CheckNesting::invalid_prop_parent(Statement* parent, AST_Node* node) { if (!( is_mixin(parent) || is_directive_node(parent) || Cast(parent) || Cast(parent) || Cast(parent) || Cast(parent) )) { error(node, traces, "Properties are only allowed within rules, directives, mixin includes, or other properties."); } } void CheckNesting::invalid_value_child(AST_Node* d) { if (Map* m = Cast(d)) { traces.push_back(Backtrace(m->pstate())); throw Exception::InvalidValue(traces, *m); } if (Number* n = Cast(d)) { if (!n->is_valid_css_unit()) { traces.push_back(Backtrace(n->pstate())); throw Exception::InvalidValue(traces, *n); } } // error(dbg + " isn't a valid CSS value.", m->pstate(),); } void CheckNesting::invalid_return_parent(Statement* parent, AST_Node* node) { if (!this->is_function(parent)) { error(node, traces, "@return may only be used within a function."); } } bool CheckNesting::is_transparent_parent(Statement* parent, Statement* grandparent) { bool parent_bubbles = parent && parent->bubbles(); bool valid_bubble_node = parent_bubbles && !is_root_node(grandparent) && !is_at_root_node(grandparent); return Cast(parent) || Cast(parent) || Cast(parent) || Cast(parent) || Cast(parent) || Cast(parent) || valid_bubble_node; } bool CheckNesting::is_charset(Statement* n) { AtRule* d = Cast(n); return d && d->keyword() == "charset"; } bool CheckNesting::is_mixin(Statement* n) { Definition* def = Cast(n); return def && def->type() == Definition::MIXIN; } bool CheckNesting::is_function(Statement* n) { Definition* def = Cast(n); return def && def->type() == Definition::FUNCTION; } bool CheckNesting::is_root_node(Statement* n) { if (Cast(n)) return false; Block* b = Cast(n); return b && b->is_root(); } bool CheckNesting::is_at_root_node(Statement* n) { return Cast(n) != NULL; } bool CheckNesting::is_directive_node(Statement* n) { return Cast(n) || Cast(n) || Cast(n) || Cast(n) || Cast(n); } } golibsass-1.0.0/libsass_src/src/check_nesting.hpp000066400000000000000000000036401405214413600221140ustar00rootroot00000000000000#ifndef SASS_CHECK_NESTING_H #define SASS_CHECK_NESTING_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "operation.hpp" #include namespace Sass { class CheckNesting : public Operation_CRTP { sass::vector parents; Backtraces traces; Statement* parent; Definition* current_mixin_definition; Statement* before(Statement*); Statement* visit_children(Statement*); public: CheckNesting(); ~CheckNesting() { } Statement* operator()(Block*); Statement* operator()(Definition*); Statement* operator()(If*); template Statement* fallback(U x) { Statement* s = Cast(x); if (s && this->should_visit(s)) { Block* b1 = Cast(s); ParentStatement* b2 = Cast(s); if (b1 || b2) return visit_children(s); } return s; } private: void invalid_content_parent(Statement*, AST_Node*); void invalid_charset_parent(Statement*, AST_Node*); void invalid_extend_parent(Statement*, AST_Node*); // void invalid_import_parent(Statement*); void invalid_mixin_definition_parent(Statement*, AST_Node*); void invalid_function_parent(Statement*, AST_Node*); void invalid_function_child(Statement*); void invalid_prop_child(Statement*); void invalid_prop_parent(Statement*, AST_Node*); void invalid_return_parent(Statement*, AST_Node*); void invalid_value_child(AST_Node*); bool is_transparent_parent(Statement*, Statement*); bool should_visit(Statement*); bool is_charset(Statement*); bool is_mixin(Statement*); bool is_function(Statement*); bool is_root_node(Statement*); bool is_at_root_node(Statement*); bool is_directive_node(Statement*); }; } #endif golibsass-1.0.0/libsass_src/src/color_maps.cpp000066400000000000000000001017301405214413600214400ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "color_maps.hpp" #include "util_string.hpp" namespace Sass { namespace ColorNames { const char aliceblue [] = "aliceblue"; const char antiquewhite [] = "antiquewhite"; const char cyan [] = "cyan"; const char aqua [] = "aqua"; const char aquamarine [] = "aquamarine"; const char azure [] = "azure"; const char beige [] = "beige"; const char bisque [] = "bisque"; const char black [] = "black"; const char blanchedalmond [] = "blanchedalmond"; const char blue [] = "blue"; const char blueviolet [] = "blueviolet"; const char brown [] = "brown"; const char burlywood [] = "burlywood"; const char cadetblue [] = "cadetblue"; const char chartreuse [] = "chartreuse"; const char chocolate [] = "chocolate"; const char coral [] = "coral"; const char cornflowerblue [] = "cornflowerblue"; const char cornsilk [] = "cornsilk"; const char crimson [] = "crimson"; const char darkblue [] = "darkblue"; const char darkcyan [] = "darkcyan"; const char darkgoldenrod [] = "darkgoldenrod"; const char darkgray [] = "darkgray"; const char darkgrey [] = "darkgrey"; const char darkgreen [] = "darkgreen"; const char darkkhaki [] = "darkkhaki"; const char darkmagenta [] = "darkmagenta"; const char darkolivegreen [] = "darkolivegreen"; const char darkorange [] = "darkorange"; const char darkorchid [] = "darkorchid"; const char darkred [] = "darkred"; const char darksalmon [] = "darksalmon"; const char darkseagreen [] = "darkseagreen"; const char darkslateblue [] = "darkslateblue"; const char darkslategray [] = "darkslategray"; const char darkslategrey [] = "darkslategrey"; const char darkturquoise [] = "darkturquoise"; const char darkviolet [] = "darkviolet"; const char deeppink [] = "deeppink"; const char deepskyblue [] = "deepskyblue"; const char dimgray [] = "dimgray"; const char dimgrey [] = "dimgrey"; const char dodgerblue [] = "dodgerblue"; const char firebrick [] = "firebrick"; const char floralwhite [] = "floralwhite"; const char forestgreen [] = "forestgreen"; const char magenta [] = "magenta"; const char fuchsia [] = "fuchsia"; const char gainsboro [] = "gainsboro"; const char ghostwhite [] = "ghostwhite"; const char gold [] = "gold"; const char goldenrod [] = "goldenrod"; const char gray [] = "gray"; const char grey [] = "grey"; const char green [] = "green"; const char greenyellow [] = "greenyellow"; const char honeydew [] = "honeydew"; const char hotpink [] = "hotpink"; const char indianred [] = "indianred"; const char indigo [] = "indigo"; const char ivory [] = "ivory"; const char khaki [] = "khaki"; const char lavender [] = "lavender"; const char lavenderblush [] = "lavenderblush"; const char lawngreen [] = "lawngreen"; const char lemonchiffon [] = "lemonchiffon"; const char lightblue [] = "lightblue"; const char lightcoral [] = "lightcoral"; const char lightcyan [] = "lightcyan"; const char lightgoldenrodyellow [] = "lightgoldenrodyellow"; const char lightgray [] = "lightgray"; const char lightgrey [] = "lightgrey"; const char lightgreen [] = "lightgreen"; const char lightpink [] = "lightpink"; const char lightsalmon [] = "lightsalmon"; const char lightseagreen [] = "lightseagreen"; const char lightskyblue [] = "lightskyblue"; const char lightslategray [] = "lightslategray"; const char lightslategrey [] = "lightslategrey"; const char lightsteelblue [] = "lightsteelblue"; const char lightyellow [] = "lightyellow"; const char lime [] = "lime"; const char limegreen [] = "limegreen"; const char linen [] = "linen"; const char maroon [] = "maroon"; const char mediumaquamarine [] = "mediumaquamarine"; const char mediumblue [] = "mediumblue"; const char mediumorchid [] = "mediumorchid"; const char mediumpurple [] = "mediumpurple"; const char mediumseagreen [] = "mediumseagreen"; const char mediumslateblue [] = "mediumslateblue"; const char mediumspringgreen [] = "mediumspringgreen"; const char mediumturquoise [] = "mediumturquoise"; const char mediumvioletred [] = "mediumvioletred"; const char midnightblue [] = "midnightblue"; const char mintcream [] = "mintcream"; const char mistyrose [] = "mistyrose"; const char moccasin [] = "moccasin"; const char navajowhite [] = "navajowhite"; const char navy [] = "navy"; const char oldlace [] = "oldlace"; const char olive [] = "olive"; const char olivedrab [] = "olivedrab"; const char orange [] = "orange"; const char orangered [] = "orangered"; const char orchid [] = "orchid"; const char palegoldenrod [] = "palegoldenrod"; const char palegreen [] = "palegreen"; const char paleturquoise [] = "paleturquoise"; const char palevioletred [] = "palevioletred"; const char papayawhip [] = "papayawhip"; const char peachpuff [] = "peachpuff"; const char peru [] = "peru"; const char pink [] = "pink"; const char plum [] = "plum"; const char powderblue [] = "powderblue"; const char purple [] = "purple"; const char red [] = "red"; const char rosybrown [] = "rosybrown"; const char royalblue [] = "royalblue"; const char saddlebrown [] = "saddlebrown"; const char salmon [] = "salmon"; const char sandybrown [] = "sandybrown"; const char seagreen [] = "seagreen"; const char seashell [] = "seashell"; const char sienna [] = "sienna"; const char silver [] = "silver"; const char skyblue [] = "skyblue"; const char slateblue [] = "slateblue"; const char slategray [] = "slategray"; const char slategrey [] = "slategrey"; const char snow [] = "snow"; const char springgreen [] = "springgreen"; const char steelblue [] = "steelblue"; const char tan [] = "tan"; const char teal [] = "teal"; const char thistle [] = "thistle"; const char tomato [] = "tomato"; const char turquoise [] = "turquoise"; const char violet [] = "violet"; const char wheat [] = "wheat"; const char white [] = "white"; const char whitesmoke [] = "whitesmoke"; const char yellow [] = "yellow"; const char yellowgreen [] = "yellowgreen"; const char rebeccapurple [] = "rebeccapurple"; const char transparent [] = "transparent"; } namespace Colors { const SourceSpan color_table("[COLOR TABLE]"); const Color_RGBA aliceblue(color_table, 240, 248, 255, 1); const Color_RGBA antiquewhite(color_table, 250, 235, 215, 1); const Color_RGBA cyan(color_table, 0, 255, 255, 1); const Color_RGBA aqua(color_table, 0, 255, 255, 1); const Color_RGBA aquamarine(color_table, 127, 255, 212, 1); const Color_RGBA azure(color_table, 240, 255, 255, 1); const Color_RGBA beige(color_table, 245, 245, 220, 1); const Color_RGBA bisque(color_table, 255, 228, 196, 1); const Color_RGBA black(color_table, 0, 0, 0, 1); const Color_RGBA blanchedalmond(color_table, 255, 235, 205, 1); const Color_RGBA blue(color_table, 0, 0, 255, 1); const Color_RGBA blueviolet(color_table, 138, 43, 226, 1); const Color_RGBA brown(color_table, 165, 42, 42, 1); const Color_RGBA burlywood(color_table, 222, 184, 135, 1); const Color_RGBA cadetblue(color_table, 95, 158, 160, 1); const Color_RGBA chartreuse(color_table, 127, 255, 0, 1); const Color_RGBA chocolate(color_table, 210, 105, 30, 1); const Color_RGBA coral(color_table, 255, 127, 80, 1); const Color_RGBA cornflowerblue(color_table, 100, 149, 237, 1); const Color_RGBA cornsilk(color_table, 255, 248, 220, 1); const Color_RGBA crimson(color_table, 220, 20, 60, 1); const Color_RGBA darkblue(color_table, 0, 0, 139, 1); const Color_RGBA darkcyan(color_table, 0, 139, 139, 1); const Color_RGBA darkgoldenrod(color_table, 184, 134, 11, 1); const Color_RGBA darkgray(color_table, 169, 169, 169, 1); const Color_RGBA darkgrey(color_table, 169, 169, 169, 1); const Color_RGBA darkgreen(color_table, 0, 100, 0, 1); const Color_RGBA darkkhaki(color_table, 189, 183, 107, 1); const Color_RGBA darkmagenta(color_table, 139, 0, 139, 1); const Color_RGBA darkolivegreen(color_table, 85, 107, 47, 1); const Color_RGBA darkorange(color_table, 255, 140, 0, 1); const Color_RGBA darkorchid(color_table, 153, 50, 204, 1); const Color_RGBA darkred(color_table, 139, 0, 0, 1); const Color_RGBA darksalmon(color_table, 233, 150, 122, 1); const Color_RGBA darkseagreen(color_table, 143, 188, 143, 1); const Color_RGBA darkslateblue(color_table, 72, 61, 139, 1); const Color_RGBA darkslategray(color_table, 47, 79, 79, 1); const Color_RGBA darkslategrey(color_table, 47, 79, 79, 1); const Color_RGBA darkturquoise(color_table, 0, 206, 209, 1); const Color_RGBA darkviolet(color_table, 148, 0, 211, 1); const Color_RGBA deeppink(color_table, 255, 20, 147, 1); const Color_RGBA deepskyblue(color_table, 0, 191, 255, 1); const Color_RGBA dimgray(color_table, 105, 105, 105, 1); const Color_RGBA dimgrey(color_table, 105, 105, 105, 1); const Color_RGBA dodgerblue(color_table, 30, 144, 255, 1); const Color_RGBA firebrick(color_table, 178, 34, 34, 1); const Color_RGBA floralwhite(color_table, 255, 250, 240, 1); const Color_RGBA forestgreen(color_table, 34, 139, 34, 1); const Color_RGBA magenta(color_table, 255, 0, 255, 1); const Color_RGBA fuchsia(color_table, 255, 0, 255, 1); const Color_RGBA gainsboro(color_table, 220, 220, 220, 1); const Color_RGBA ghostwhite(color_table, 248, 248, 255, 1); const Color_RGBA gold(color_table, 255, 215, 0, 1); const Color_RGBA goldenrod(color_table, 218, 165, 32, 1); const Color_RGBA gray(color_table, 128, 128, 128, 1); const Color_RGBA grey(color_table, 128, 128, 128, 1); const Color_RGBA green(color_table, 0, 128, 0, 1); const Color_RGBA greenyellow(color_table, 173, 255, 47, 1); const Color_RGBA honeydew(color_table, 240, 255, 240, 1); const Color_RGBA hotpink(color_table, 255, 105, 180, 1); const Color_RGBA indianred(color_table, 205, 92, 92, 1); const Color_RGBA indigo(color_table, 75, 0, 130, 1); const Color_RGBA ivory(color_table, 255, 255, 240, 1); const Color_RGBA khaki(color_table, 240, 230, 140, 1); const Color_RGBA lavender(color_table, 230, 230, 250, 1); const Color_RGBA lavenderblush(color_table, 255, 240, 245, 1); const Color_RGBA lawngreen(color_table, 124, 252, 0, 1); const Color_RGBA lemonchiffon(color_table, 255, 250, 205, 1); const Color_RGBA lightblue(color_table, 173, 216, 230, 1); const Color_RGBA lightcoral(color_table, 240, 128, 128, 1); const Color_RGBA lightcyan(color_table, 224, 255, 255, 1); const Color_RGBA lightgoldenrodyellow(color_table, 250, 250, 210, 1); const Color_RGBA lightgray(color_table, 211, 211, 211, 1); const Color_RGBA lightgrey(color_table, 211, 211, 211, 1); const Color_RGBA lightgreen(color_table, 144, 238, 144, 1); const Color_RGBA lightpink(color_table, 255, 182, 193, 1); const Color_RGBA lightsalmon(color_table, 255, 160, 122, 1); const Color_RGBA lightseagreen(color_table, 32, 178, 170, 1); const Color_RGBA lightskyblue(color_table, 135, 206, 250, 1); const Color_RGBA lightslategray(color_table, 119, 136, 153, 1); const Color_RGBA lightslategrey(color_table, 119, 136, 153, 1); const Color_RGBA lightsteelblue(color_table, 176, 196, 222, 1); const Color_RGBA lightyellow(color_table, 255, 255, 224, 1); const Color_RGBA lime(color_table, 0, 255, 0, 1); const Color_RGBA limegreen(color_table, 50, 205, 50, 1); const Color_RGBA linen(color_table, 250, 240, 230, 1); const Color_RGBA maroon(color_table, 128, 0, 0, 1); const Color_RGBA mediumaquamarine(color_table, 102, 205, 170, 1); const Color_RGBA mediumblue(color_table, 0, 0, 205, 1); const Color_RGBA mediumorchid(color_table, 186, 85, 211, 1); const Color_RGBA mediumpurple(color_table, 147, 112, 219, 1); const Color_RGBA mediumseagreen(color_table, 60, 179, 113, 1); const Color_RGBA mediumslateblue(color_table, 123, 104, 238, 1); const Color_RGBA mediumspringgreen(color_table, 0, 250, 154, 1); const Color_RGBA mediumturquoise(color_table, 72, 209, 204, 1); const Color_RGBA mediumvioletred(color_table, 199, 21, 133, 1); const Color_RGBA midnightblue(color_table, 25, 25, 112, 1); const Color_RGBA mintcream(color_table, 245, 255, 250, 1); const Color_RGBA mistyrose(color_table, 255, 228, 225, 1); const Color_RGBA moccasin(color_table, 255, 228, 181, 1); const Color_RGBA navajowhite(color_table, 255, 222, 173, 1); const Color_RGBA navy(color_table, 0, 0, 128, 1); const Color_RGBA oldlace(color_table, 253, 245, 230, 1); const Color_RGBA olive(color_table, 128, 128, 0, 1); const Color_RGBA olivedrab(color_table, 107, 142, 35, 1); const Color_RGBA orange(color_table, 255, 165, 0, 1); const Color_RGBA orangered(color_table, 255, 69, 0, 1); const Color_RGBA orchid(color_table, 218, 112, 214, 1); const Color_RGBA palegoldenrod(color_table, 238, 232, 170, 1); const Color_RGBA palegreen(color_table, 152, 251, 152, 1); const Color_RGBA paleturquoise(color_table, 175, 238, 238, 1); const Color_RGBA palevioletred(color_table, 219, 112, 147, 1); const Color_RGBA papayawhip(color_table, 255, 239, 213, 1); const Color_RGBA peachpuff(color_table, 255, 218, 185, 1); const Color_RGBA peru(color_table, 205, 133, 63, 1); const Color_RGBA pink(color_table, 255, 192, 203, 1); const Color_RGBA plum(color_table, 221, 160, 221, 1); const Color_RGBA powderblue(color_table, 176, 224, 230, 1); const Color_RGBA purple(color_table, 128, 0, 128, 1); const Color_RGBA red(color_table, 255, 0, 0, 1); const Color_RGBA rosybrown(color_table, 188, 143, 143, 1); const Color_RGBA royalblue(color_table, 65, 105, 225, 1); const Color_RGBA saddlebrown(color_table, 139, 69, 19, 1); const Color_RGBA salmon(color_table, 250, 128, 114, 1); const Color_RGBA sandybrown(color_table, 244, 164, 96, 1); const Color_RGBA seagreen(color_table, 46, 139, 87, 1); const Color_RGBA seashell(color_table, 255, 245, 238, 1); const Color_RGBA sienna(color_table, 160, 82, 45, 1); const Color_RGBA silver(color_table, 192, 192, 192, 1); const Color_RGBA skyblue(color_table, 135, 206, 235, 1); const Color_RGBA slateblue(color_table, 106, 90, 205, 1); const Color_RGBA slategray(color_table, 112, 128, 144, 1); const Color_RGBA slategrey(color_table, 112, 128, 144, 1); const Color_RGBA snow(color_table, 255, 250, 250, 1); const Color_RGBA springgreen(color_table, 0, 255, 127, 1); const Color_RGBA steelblue(color_table, 70, 130, 180, 1); const Color_RGBA tan(color_table, 210, 180, 140, 1); const Color_RGBA teal(color_table, 0, 128, 128, 1); const Color_RGBA thistle(color_table, 216, 191, 216, 1); const Color_RGBA tomato(color_table, 255, 99, 71, 1); const Color_RGBA turquoise(color_table, 64, 224, 208, 1); const Color_RGBA violet(color_table, 238, 130, 238, 1); const Color_RGBA wheat(color_table, 245, 222, 179, 1); const Color_RGBA white(color_table, 255, 255, 255, 1); const Color_RGBA whitesmoke(color_table, 245, 245, 245, 1); const Color_RGBA yellow(color_table, 255, 255, 0, 1); const Color_RGBA yellowgreen(color_table, 154, 205, 50, 1); const Color_RGBA rebeccapurple(color_table, 102, 51, 153, 1); const Color_RGBA transparent(color_table, 0, 0, 0, 0); } static const auto* const colors_to_names = new std::unordered_map { { 240 * 0x10000 + 248 * 0x100 + 255, ColorNames::aliceblue }, { 250 * 0x10000 + 235 * 0x100 + 215, ColorNames::antiquewhite }, { 0 * 0x10000 + 255 * 0x100 + 255, ColorNames::cyan }, { 127 * 0x10000 + 255 * 0x100 + 212, ColorNames::aquamarine }, { 240 * 0x10000 + 255 * 0x100 + 255, ColorNames::azure }, { 245 * 0x10000 + 245 * 0x100 + 220, ColorNames::beige }, { 255 * 0x10000 + 228 * 0x100 + 196, ColorNames::bisque }, { 0 * 0x10000 + 0 * 0x100 + 0, ColorNames::black }, { 255 * 0x10000 + 235 * 0x100 + 205, ColorNames::blanchedalmond }, { 0 * 0x10000 + 0 * 0x100 + 255, ColorNames::blue }, { 138 * 0x10000 + 43 * 0x100 + 226, ColorNames::blueviolet }, { 165 * 0x10000 + 42 * 0x100 + 42, ColorNames::brown }, { 222 * 0x10000 + 184 * 0x100 + 135, ColorNames::burlywood }, { 95 * 0x10000 + 158 * 0x100 + 160, ColorNames::cadetblue }, { 127 * 0x10000 + 255 * 0x100 + 0, ColorNames::chartreuse }, { 210 * 0x10000 + 105 * 0x100 + 30, ColorNames::chocolate }, { 255 * 0x10000 + 127 * 0x100 + 80, ColorNames::coral }, { 100 * 0x10000 + 149 * 0x100 + 237, ColorNames::cornflowerblue }, { 255 * 0x10000 + 248 * 0x100 + 220, ColorNames::cornsilk }, { 220 * 0x10000 + 20 * 0x100 + 60, ColorNames::crimson }, { 0 * 0x10000 + 0 * 0x100 + 139, ColorNames::darkblue }, { 0 * 0x10000 + 139 * 0x100 + 139, ColorNames::darkcyan }, { 184 * 0x10000 + 134 * 0x100 + 11, ColorNames::darkgoldenrod }, { 169 * 0x10000 + 169 * 0x100 + 169, ColorNames::darkgray }, { 0 * 0x10000 + 100 * 0x100 + 0, ColorNames::darkgreen }, { 189 * 0x10000 + 183 * 0x100 + 107, ColorNames::darkkhaki }, { 139 * 0x10000 + 0 * 0x100 + 139, ColorNames::darkmagenta }, { 85 * 0x10000 + 107 * 0x100 + 47, ColorNames::darkolivegreen }, { 255 * 0x10000 + 140 * 0x100 + 0, ColorNames::darkorange }, { 153 * 0x10000 + 50 * 0x100 + 204, ColorNames::darkorchid }, { 139 * 0x10000 + 0 * 0x100 + 0, ColorNames::darkred }, { 233 * 0x10000 + 150 * 0x100 + 122, ColorNames::darksalmon }, { 143 * 0x10000 + 188 * 0x100 + 143, ColorNames::darkseagreen }, { 72 * 0x10000 + 61 * 0x100 + 139, ColorNames::darkslateblue }, { 47 * 0x10000 + 79 * 0x100 + 79, ColorNames::darkslategray }, { 0 * 0x10000 + 206 * 0x100 + 209, ColorNames::darkturquoise }, { 148 * 0x10000 + 0 * 0x100 + 211, ColorNames::darkviolet }, { 255 * 0x10000 + 20 * 0x100 + 147, ColorNames::deeppink }, { 0 * 0x10000 + 191 * 0x100 + 255, ColorNames::deepskyblue }, { 105 * 0x10000 + 105 * 0x100 + 105, ColorNames::dimgray }, { 30 * 0x10000 + 144 * 0x100 + 255, ColorNames::dodgerblue }, { 178 * 0x10000 + 34 * 0x100 + 34, ColorNames::firebrick }, { 255 * 0x10000 + 250 * 0x100 + 240, ColorNames::floralwhite }, { 34 * 0x10000 + 139 * 0x100 + 34, ColorNames::forestgreen }, { 255 * 0x10000 + 0 * 0x100 + 255, ColorNames::magenta }, { 220 * 0x10000 + 220 * 0x100 + 220, ColorNames::gainsboro }, { 248 * 0x10000 + 248 * 0x100 + 255, ColorNames::ghostwhite }, { 255 * 0x10000 + 215 * 0x100 + 0, ColorNames::gold }, { 218 * 0x10000 + 165 * 0x100 + 32, ColorNames::goldenrod }, { 128 * 0x10000 + 128 * 0x100 + 128, ColorNames::gray }, { 0 * 0x10000 + 128 * 0x100 + 0, ColorNames::green }, { 173 * 0x10000 + 255 * 0x100 + 47, ColorNames::greenyellow }, { 240 * 0x10000 + 255 * 0x100 + 240, ColorNames::honeydew }, { 255 * 0x10000 + 105 * 0x100 + 180, ColorNames::hotpink }, { 205 * 0x10000 + 92 * 0x100 + 92, ColorNames::indianred }, { 75 * 0x10000 + 0 * 0x100 + 130, ColorNames::indigo }, { 255 * 0x10000 + 255 * 0x100 + 240, ColorNames::ivory }, { 240 * 0x10000 + 230 * 0x100 + 140, ColorNames::khaki }, { 230 * 0x10000 + 230 * 0x100 + 250, ColorNames::lavender }, { 255 * 0x10000 + 240 * 0x100 + 245, ColorNames::lavenderblush }, { 124 * 0x10000 + 252 * 0x100 + 0, ColorNames::lawngreen }, { 255 * 0x10000 + 250 * 0x100 + 205, ColorNames::lemonchiffon }, { 173 * 0x10000 + 216 * 0x100 + 230, ColorNames::lightblue }, { 240 * 0x10000 + 128 * 0x100 + 128, ColorNames::lightcoral }, { 224 * 0x10000 + 255 * 0x100 + 255, ColorNames::lightcyan }, { 250 * 0x10000 + 250 * 0x100 + 210, ColorNames::lightgoldenrodyellow }, { 211 * 0x10000 + 211 * 0x100 + 211, ColorNames::lightgray }, { 144 * 0x10000 + 238 * 0x100 + 144, ColorNames::lightgreen }, { 255 * 0x10000 + 182 * 0x100 + 193, ColorNames::lightpink }, { 255 * 0x10000 + 160 * 0x100 + 122, ColorNames::lightsalmon }, { 32 * 0x10000 + 178 * 0x100 + 170, ColorNames::lightseagreen }, { 135 * 0x10000 + 206 * 0x100 + 250, ColorNames::lightskyblue }, { 119 * 0x10000 + 136 * 0x100 + 153, ColorNames::lightslategray }, { 176 * 0x10000 + 196 * 0x100 + 222, ColorNames::lightsteelblue }, { 255 * 0x10000 + 255 * 0x100 + 224, ColorNames::lightyellow }, { 0 * 0x10000 + 255 * 0x100 + 0, ColorNames::lime }, { 50 * 0x10000 + 205 * 0x100 + 50, ColorNames::limegreen }, { 250 * 0x10000 + 240 * 0x100 + 230, ColorNames::linen }, { 128 * 0x10000 + 0 * 0x100 + 0, ColorNames::maroon }, { 102 * 0x10000 + 205 * 0x100 + 170, ColorNames::mediumaquamarine }, { 0 * 0x10000 + 0 * 0x100 + 205, ColorNames::mediumblue }, { 186 * 0x10000 + 85 * 0x100 + 211, ColorNames::mediumorchid }, { 147 * 0x10000 + 112 * 0x100 + 219, ColorNames::mediumpurple }, { 60 * 0x10000 + 179 * 0x100 + 113, ColorNames::mediumseagreen }, { 123 * 0x10000 + 104 * 0x100 + 238, ColorNames::mediumslateblue }, { 0 * 0x10000 + 250 * 0x100 + 154, ColorNames::mediumspringgreen }, { 72 * 0x10000 + 209 * 0x100 + 204, ColorNames::mediumturquoise }, { 199 * 0x10000 + 21 * 0x100 + 133, ColorNames::mediumvioletred }, { 25 * 0x10000 + 25 * 0x100 + 112, ColorNames::midnightblue }, { 245 * 0x10000 + 255 * 0x100 + 250, ColorNames::mintcream }, { 255 * 0x10000 + 228 * 0x100 + 225, ColorNames::mistyrose }, { 255 * 0x10000 + 228 * 0x100 + 181, ColorNames::moccasin }, { 255 * 0x10000 + 222 * 0x100 + 173, ColorNames::navajowhite }, { 0 * 0x10000 + 0 * 0x100 + 128, ColorNames::navy }, { 253 * 0x10000 + 245 * 0x100 + 230, ColorNames::oldlace }, { 128 * 0x10000 + 128 * 0x100 + 0, ColorNames::olive }, { 107 * 0x10000 + 142 * 0x100 + 35, ColorNames::olivedrab }, { 255 * 0x10000 + 165 * 0x100 + 0, ColorNames::orange }, { 255 * 0x10000 + 69 * 0x100 + 0, ColorNames::orangered }, { 218 * 0x10000 + 112 * 0x100 + 214, ColorNames::orchid }, { 238 * 0x10000 + 232 * 0x100 + 170, ColorNames::palegoldenrod }, { 152 * 0x10000 + 251 * 0x100 + 152, ColorNames::palegreen }, { 175 * 0x10000 + 238 * 0x100 + 238, ColorNames::paleturquoise }, { 219 * 0x10000 + 112 * 0x100 + 147, ColorNames::palevioletred }, { 255 * 0x10000 + 239 * 0x100 + 213, ColorNames::papayawhip }, { 255 * 0x10000 + 218 * 0x100 + 185, ColorNames::peachpuff }, { 205 * 0x10000 + 133 * 0x100 + 63, ColorNames::peru }, { 255 * 0x10000 + 192 * 0x100 + 203, ColorNames::pink }, { 221 * 0x10000 + 160 * 0x100 + 221, ColorNames::plum }, { 176 * 0x10000 + 224 * 0x100 + 230, ColorNames::powderblue }, { 128 * 0x10000 + 0 * 0x100 + 128, ColorNames::purple }, { 255 * 0x10000 + 0 * 0x100 + 0, ColorNames::red }, { 188 * 0x10000 + 143 * 0x100 + 143, ColorNames::rosybrown }, { 65 * 0x10000 + 105 * 0x100 + 225, ColorNames::royalblue }, { 139 * 0x10000 + 69 * 0x100 + 19, ColorNames::saddlebrown }, { 250 * 0x10000 + 128 * 0x100 + 114, ColorNames::salmon }, { 244 * 0x10000 + 164 * 0x100 + 96, ColorNames::sandybrown }, { 46 * 0x10000 + 139 * 0x100 + 87, ColorNames::seagreen }, { 255 * 0x10000 + 245 * 0x100 + 238, ColorNames::seashell }, { 160 * 0x10000 + 82 * 0x100 + 45, ColorNames::sienna }, { 192 * 0x10000 + 192 * 0x100 + 192, ColorNames::silver }, { 135 * 0x10000 + 206 * 0x100 + 235, ColorNames::skyblue }, { 106 * 0x10000 + 90 * 0x100 + 205, ColorNames::slateblue }, { 112 * 0x10000 + 128 * 0x100 + 144, ColorNames::slategray }, { 255 * 0x10000 + 250 * 0x100 + 250, ColorNames::snow }, { 0 * 0x10000 + 255 * 0x100 + 127, ColorNames::springgreen }, { 70 * 0x10000 + 130 * 0x100 + 180, ColorNames::steelblue }, { 210 * 0x10000 + 180 * 0x100 + 140, ColorNames::tan }, { 0 * 0x10000 + 128 * 0x100 + 128, ColorNames::teal }, { 216 * 0x10000 + 191 * 0x100 + 216, ColorNames::thistle }, { 255 * 0x10000 + 99 * 0x100 + 71, ColorNames::tomato }, { 64 * 0x10000 + 224 * 0x100 + 208, ColorNames::turquoise }, { 238 * 0x10000 + 130 * 0x100 + 238, ColorNames::violet }, { 245 * 0x10000 + 222 * 0x100 + 179, ColorNames::wheat }, { 255 * 0x10000 + 255 * 0x100 + 255, ColorNames::white }, { 245 * 0x10000 + 245 * 0x100 + 245, ColorNames::whitesmoke }, { 255 * 0x10000 + 255 * 0x100 + 0, ColorNames::yellow }, { 154 * 0x10000 + 205 * 0x100 + 50, ColorNames::yellowgreen }, { 102 * 0x10000 + 51 * 0x100 + 153, ColorNames::rebeccapurple } }; static const auto *const names_to_colors = new std::unordered_map { { ColorNames::aliceblue, &Colors::aliceblue }, { ColorNames::antiquewhite, &Colors::antiquewhite }, { ColorNames::cyan, &Colors::cyan }, { ColorNames::aqua, &Colors::aqua }, { ColorNames::aquamarine, &Colors::aquamarine }, { ColorNames::azure, &Colors::azure }, { ColorNames::beige, &Colors::beige }, { ColorNames::bisque, &Colors::bisque }, { ColorNames::black, &Colors::black }, { ColorNames::blanchedalmond, &Colors::blanchedalmond }, { ColorNames::blue, &Colors::blue }, { ColorNames::blueviolet, &Colors::blueviolet }, { ColorNames::brown, &Colors::brown }, { ColorNames::burlywood, &Colors::burlywood }, { ColorNames::cadetblue, &Colors::cadetblue }, { ColorNames::chartreuse, &Colors::chartreuse }, { ColorNames::chocolate, &Colors::chocolate }, { ColorNames::coral, &Colors::coral }, { ColorNames::cornflowerblue, &Colors::cornflowerblue }, { ColorNames::cornsilk, &Colors::cornsilk }, { ColorNames::crimson, &Colors::crimson }, { ColorNames::darkblue, &Colors::darkblue }, { ColorNames::darkcyan, &Colors::darkcyan }, { ColorNames::darkgoldenrod, &Colors::darkgoldenrod }, { ColorNames::darkgray, &Colors::darkgray }, { ColorNames::darkgrey, &Colors::darkgrey }, { ColorNames::darkgreen, &Colors::darkgreen }, { ColorNames::darkkhaki, &Colors::darkkhaki }, { ColorNames::darkmagenta, &Colors::darkmagenta }, { ColorNames::darkolivegreen, &Colors::darkolivegreen }, { ColorNames::darkorange, &Colors::darkorange }, { ColorNames::darkorchid, &Colors::darkorchid }, { ColorNames::darkred, &Colors::darkred }, { ColorNames::darksalmon, &Colors::darksalmon }, { ColorNames::darkseagreen, &Colors::darkseagreen }, { ColorNames::darkslateblue, &Colors::darkslateblue }, { ColorNames::darkslategray, &Colors::darkslategray }, { ColorNames::darkslategrey, &Colors::darkslategrey }, { ColorNames::darkturquoise, &Colors::darkturquoise }, { ColorNames::darkviolet, &Colors::darkviolet }, { ColorNames::deeppink, &Colors::deeppink }, { ColorNames::deepskyblue, &Colors::deepskyblue }, { ColorNames::dimgray, &Colors::dimgray }, { ColorNames::dimgrey, &Colors::dimgrey }, { ColorNames::dodgerblue, &Colors::dodgerblue }, { ColorNames::firebrick, &Colors::firebrick }, { ColorNames::floralwhite, &Colors::floralwhite }, { ColorNames::forestgreen, &Colors::forestgreen }, { ColorNames::magenta, &Colors::magenta }, { ColorNames::fuchsia, &Colors::fuchsia }, { ColorNames::gainsboro, &Colors::gainsboro }, { ColorNames::ghostwhite, &Colors::ghostwhite }, { ColorNames::gold, &Colors::gold }, { ColorNames::goldenrod, &Colors::goldenrod }, { ColorNames::gray, &Colors::gray }, { ColorNames::grey, &Colors::grey }, { ColorNames::green, &Colors::green }, { ColorNames::greenyellow, &Colors::greenyellow }, { ColorNames::honeydew, &Colors::honeydew }, { ColorNames::hotpink, &Colors::hotpink }, { ColorNames::indianred, &Colors::indianred }, { ColorNames::indigo, &Colors::indigo }, { ColorNames::ivory, &Colors::ivory }, { ColorNames::khaki, &Colors::khaki }, { ColorNames::lavender, &Colors::lavender }, { ColorNames::lavenderblush, &Colors::lavenderblush }, { ColorNames::lawngreen, &Colors::lawngreen }, { ColorNames::lemonchiffon, &Colors::lemonchiffon }, { ColorNames::lightblue, &Colors::lightblue }, { ColorNames::lightcoral, &Colors::lightcoral }, { ColorNames::lightcyan, &Colors::lightcyan }, { ColorNames::lightgoldenrodyellow, &Colors::lightgoldenrodyellow }, { ColorNames::lightgray, &Colors::lightgray }, { ColorNames::lightgrey, &Colors::lightgrey }, { ColorNames::lightgreen, &Colors::lightgreen }, { ColorNames::lightpink, &Colors::lightpink }, { ColorNames::lightsalmon, &Colors::lightsalmon }, { ColorNames::lightseagreen, &Colors::lightseagreen }, { ColorNames::lightskyblue, &Colors::lightskyblue }, { ColorNames::lightslategray, &Colors::lightslategray }, { ColorNames::lightslategrey, &Colors::lightslategrey }, { ColorNames::lightsteelblue, &Colors::lightsteelblue }, { ColorNames::lightyellow, &Colors::lightyellow }, { ColorNames::lime, &Colors::lime }, { ColorNames::limegreen, &Colors::limegreen }, { ColorNames::linen, &Colors::linen }, { ColorNames::maroon, &Colors::maroon }, { ColorNames::mediumaquamarine, &Colors::mediumaquamarine }, { ColorNames::mediumblue, &Colors::mediumblue }, { ColorNames::mediumorchid, &Colors::mediumorchid }, { ColorNames::mediumpurple, &Colors::mediumpurple }, { ColorNames::mediumseagreen, &Colors::mediumseagreen }, { ColorNames::mediumslateblue, &Colors::mediumslateblue }, { ColorNames::mediumspringgreen, &Colors::mediumspringgreen }, { ColorNames::mediumturquoise, &Colors::mediumturquoise }, { ColorNames::mediumvioletred, &Colors::mediumvioletred }, { ColorNames::midnightblue, &Colors::midnightblue }, { ColorNames::mintcream, &Colors::mintcream }, { ColorNames::mistyrose, &Colors::mistyrose }, { ColorNames::moccasin, &Colors::moccasin }, { ColorNames::navajowhite, &Colors::navajowhite }, { ColorNames::navy, &Colors::navy }, { ColorNames::oldlace, &Colors::oldlace }, { ColorNames::olive, &Colors::olive }, { ColorNames::olivedrab, &Colors::olivedrab }, { ColorNames::orange, &Colors::orange }, { ColorNames::orangered, &Colors::orangered }, { ColorNames::orchid, &Colors::orchid }, { ColorNames::palegoldenrod, &Colors::palegoldenrod }, { ColorNames::palegreen, &Colors::palegreen }, { ColorNames::paleturquoise, &Colors::paleturquoise }, { ColorNames::palevioletred, &Colors::palevioletred }, { ColorNames::papayawhip, &Colors::papayawhip }, { ColorNames::peachpuff, &Colors::peachpuff }, { ColorNames::peru, &Colors::peru }, { ColorNames::pink, &Colors::pink }, { ColorNames::plum, &Colors::plum }, { ColorNames::powderblue, &Colors::powderblue }, { ColorNames::purple, &Colors::purple }, { ColorNames::red, &Colors::red }, { ColorNames::rosybrown, &Colors::rosybrown }, { ColorNames::royalblue, &Colors::royalblue }, { ColorNames::saddlebrown, &Colors::saddlebrown }, { ColorNames::salmon, &Colors::salmon }, { ColorNames::sandybrown, &Colors::sandybrown }, { ColorNames::seagreen, &Colors::seagreen }, { ColorNames::seashell, &Colors::seashell }, { ColorNames::sienna, &Colors::sienna }, { ColorNames::silver, &Colors::silver }, { ColorNames::skyblue, &Colors::skyblue }, { ColorNames::slateblue, &Colors::slateblue }, { ColorNames::slategray, &Colors::slategray }, { ColorNames::slategrey, &Colors::slategrey }, { ColorNames::snow, &Colors::snow }, { ColorNames::springgreen, &Colors::springgreen }, { ColorNames::steelblue, &Colors::steelblue }, { ColorNames::tan, &Colors::tan }, { ColorNames::teal, &Colors::teal }, { ColorNames::thistle, &Colors::thistle }, { ColorNames::tomato, &Colors::tomato }, { ColorNames::turquoise, &Colors::turquoise }, { ColorNames::violet, &Colors::violet }, { ColorNames::wheat, &Colors::wheat }, { ColorNames::white, &Colors::white }, { ColorNames::whitesmoke, &Colors::whitesmoke }, { ColorNames::yellow, &Colors::yellow }, { ColorNames::yellowgreen, &Colors::yellowgreen }, { ColorNames::rebeccapurple, &Colors::rebeccapurple }, { ColorNames::transparent, &Colors::transparent } }; const Color_RGBA* name_to_color(const char* key) { return name_to_color(sass::string(key)); } const Color_RGBA* name_to_color(const sass::string& key) { // case insensitive lookup. See #2462 sass::string lower = key; Util::ascii_str_tolower(&lower); auto p = names_to_colors->find(lower); if (p != names_to_colors->end()) { return p->second; } return nullptr; } const char* color_to_name(const int key) { auto p = colors_to_names->find(key); if (p != colors_to_names->end()) { return p->second; } return nullptr; } const char* color_to_name(const double key) { return color_to_name((int)key); } const char* color_to_name(const Color_RGBA& c) { double key = c.r() * 0x10000 + c.g() * 0x100 + c.b(); return color_to_name(key); } } golibsass-1.0.0/libsass_src/src/color_maps.hpp000066400000000000000000000262351405214413600214530ustar00rootroot00000000000000 #ifndef SASS_COLOR_MAPS_H #define SASS_COLOR_MAPS_H #include #include "ast.hpp" namespace Sass { namespace ColorNames { extern const char aliceblue[]; extern const char antiquewhite[]; extern const char cyan[]; extern const char aqua[]; extern const char aquamarine[]; extern const char azure[]; extern const char beige[]; extern const char bisque[]; extern const char black[]; extern const char blanchedalmond[]; extern const char blue[]; extern const char blueviolet[]; extern const char brown[]; extern const char burlywood[]; extern const char cadetblue[]; extern const char chartreuse[]; extern const char chocolate[]; extern const char coral[]; extern const char cornflowerblue[]; extern const char cornsilk[]; extern const char crimson[]; extern const char darkblue[]; extern const char darkcyan[]; extern const char darkgoldenrod[]; extern const char darkgray[]; extern const char darkgrey[]; extern const char darkgreen[]; extern const char darkkhaki[]; extern const char darkmagenta[]; extern const char darkolivegreen[]; extern const char darkorange[]; extern const char darkorchid[]; extern const char darkred[]; extern const char darksalmon[]; extern const char darkseagreen[]; extern const char darkslateblue[]; extern const char darkslategray[]; extern const char darkslategrey[]; extern const char darkturquoise[]; extern const char darkviolet[]; extern const char deeppink[]; extern const char deepskyblue[]; extern const char dimgray[]; extern const char dimgrey[]; extern const char dodgerblue[]; extern const char firebrick[]; extern const char floralwhite[]; extern const char forestgreen[]; extern const char magenta[]; extern const char fuchsia[]; extern const char gainsboro[]; extern const char ghostwhite[]; extern const char gold[]; extern const char goldenrod[]; extern const char gray[]; extern const char grey[]; extern const char green[]; extern const char greenyellow[]; extern const char honeydew[]; extern const char hotpink[]; extern const char indianred[]; extern const char indigo[]; extern const char ivory[]; extern const char khaki[]; extern const char lavender[]; extern const char lavenderblush[]; extern const char lawngreen[]; extern const char lemonchiffon[]; extern const char lightblue[]; extern const char lightcoral[]; extern const char lightcyan[]; extern const char lightgoldenrodyellow[]; extern const char lightgray[]; extern const char lightgrey[]; extern const char lightgreen[]; extern const char lightpink[]; extern const char lightsalmon[]; extern const char lightseagreen[]; extern const char lightskyblue[]; extern const char lightslategray[]; extern const char lightslategrey[]; extern const char lightsteelblue[]; extern const char lightyellow[]; extern const char lime[]; extern const char limegreen[]; extern const char linen[]; extern const char maroon[]; extern const char mediumaquamarine[]; extern const char mediumblue[]; extern const char mediumorchid[]; extern const char mediumpurple[]; extern const char mediumseagreen[]; extern const char mediumslateblue[]; extern const char mediumspringgreen[]; extern const char mediumturquoise[]; extern const char mediumvioletred[]; extern const char midnightblue[]; extern const char mintcream[]; extern const char mistyrose[]; extern const char moccasin[]; extern const char navajowhite[]; extern const char navy[]; extern const char oldlace[]; extern const char olive[]; extern const char olivedrab[]; extern const char orange[]; extern const char orangered[]; extern const char orchid[]; extern const char palegoldenrod[]; extern const char palegreen[]; extern const char paleturquoise[]; extern const char palevioletred[]; extern const char papayawhip[]; extern const char peachpuff[]; extern const char peru[]; extern const char pink[]; extern const char plum[]; extern const char powderblue[]; extern const char purple[]; extern const char red[]; extern const char rosybrown[]; extern const char royalblue[]; extern const char saddlebrown[]; extern const char salmon[]; extern const char sandybrown[]; extern const char seagreen[]; extern const char seashell[]; extern const char sienna[]; extern const char silver[]; extern const char skyblue[]; extern const char slateblue[]; extern const char slategray[]; extern const char slategrey[]; extern const char snow[]; extern const char springgreen[]; extern const char steelblue[]; extern const char tan[]; extern const char teal[]; extern const char thistle[]; extern const char tomato[]; extern const char turquoise[]; extern const char violet[]; extern const char wheat[]; extern const char white[]; extern const char whitesmoke[]; extern const char yellow[]; extern const char yellowgreen[]; extern const char rebeccapurple[]; extern const char transparent[]; } namespace Colors { extern const Color_RGBA aliceblue; extern const Color_RGBA antiquewhite; extern const Color_RGBA cyan; extern const Color_RGBA aqua; extern const Color_RGBA aquamarine; extern const Color_RGBA azure; extern const Color_RGBA beige; extern const Color_RGBA bisque; extern const Color_RGBA black; extern const Color_RGBA blanchedalmond; extern const Color_RGBA blue; extern const Color_RGBA blueviolet; extern const Color_RGBA brown; extern const Color_RGBA burlywood; extern const Color_RGBA cadetblue; extern const Color_RGBA chartreuse; extern const Color_RGBA chocolate; extern const Color_RGBA coral; extern const Color_RGBA cornflowerblue; extern const Color_RGBA cornsilk; extern const Color_RGBA crimson; extern const Color_RGBA darkblue; extern const Color_RGBA darkcyan; extern const Color_RGBA darkgoldenrod; extern const Color_RGBA darkgray; extern const Color_RGBA darkgrey; extern const Color_RGBA darkgreen; extern const Color_RGBA darkkhaki; extern const Color_RGBA darkmagenta; extern const Color_RGBA darkolivegreen; extern const Color_RGBA darkorange; extern const Color_RGBA darkorchid; extern const Color_RGBA darkred; extern const Color_RGBA darksalmon; extern const Color_RGBA darkseagreen; extern const Color_RGBA darkslateblue; extern const Color_RGBA darkslategray; extern const Color_RGBA darkslategrey; extern const Color_RGBA darkturquoise; extern const Color_RGBA darkviolet; extern const Color_RGBA deeppink; extern const Color_RGBA deepskyblue; extern const Color_RGBA dimgray; extern const Color_RGBA dimgrey; extern const Color_RGBA dodgerblue; extern const Color_RGBA firebrick; extern const Color_RGBA floralwhite; extern const Color_RGBA forestgreen; extern const Color_RGBA magenta; extern const Color_RGBA fuchsia; extern const Color_RGBA gainsboro; extern const Color_RGBA ghostwhite; extern const Color_RGBA gold; extern const Color_RGBA goldenrod; extern const Color_RGBA gray; extern const Color_RGBA grey; extern const Color_RGBA green; extern const Color_RGBA greenyellow; extern const Color_RGBA honeydew; extern const Color_RGBA hotpink; extern const Color_RGBA indianred; extern const Color_RGBA indigo; extern const Color_RGBA ivory; extern const Color_RGBA khaki; extern const Color_RGBA lavender; extern const Color_RGBA lavenderblush; extern const Color_RGBA lawngreen; extern const Color_RGBA lemonchiffon; extern const Color_RGBA lightblue; extern const Color_RGBA lightcoral; extern const Color_RGBA lightcyan; extern const Color_RGBA lightgoldenrodyellow; extern const Color_RGBA lightgray; extern const Color_RGBA lightgrey; extern const Color_RGBA lightgreen; extern const Color_RGBA lightpink; extern const Color_RGBA lightsalmon; extern const Color_RGBA lightseagreen; extern const Color_RGBA lightskyblue; extern const Color_RGBA lightslategray; extern const Color_RGBA lightslategrey; extern const Color_RGBA lightsteelblue; extern const Color_RGBA lightyellow; extern const Color_RGBA lime; extern const Color_RGBA limegreen; extern const Color_RGBA linen; extern const Color_RGBA maroon; extern const Color_RGBA mediumaquamarine; extern const Color_RGBA mediumblue; extern const Color_RGBA mediumorchid; extern const Color_RGBA mediumpurple; extern const Color_RGBA mediumseagreen; extern const Color_RGBA mediumslateblue; extern const Color_RGBA mediumspringgreen; extern const Color_RGBA mediumturquoise; extern const Color_RGBA mediumvioletred; extern const Color_RGBA midnightblue; extern const Color_RGBA mintcream; extern const Color_RGBA mistyrose; extern const Color_RGBA moccasin; extern const Color_RGBA navajowhite; extern const Color_RGBA navy; extern const Color_RGBA oldlace; extern const Color_RGBA olive; extern const Color_RGBA olivedrab; extern const Color_RGBA orange; extern const Color_RGBA orangered; extern const Color_RGBA orchid; extern const Color_RGBA palegoldenrod; extern const Color_RGBA palegreen; extern const Color_RGBA paleturquoise; extern const Color_RGBA palevioletred; extern const Color_RGBA papayawhip; extern const Color_RGBA peachpuff; extern const Color_RGBA peru; extern const Color_RGBA pink; extern const Color_RGBA plum; extern const Color_RGBA powderblue; extern const Color_RGBA purple; extern const Color_RGBA red; extern const Color_RGBA rosybrown; extern const Color_RGBA royalblue; extern const Color_RGBA saddlebrown; extern const Color_RGBA salmon; extern const Color_RGBA sandybrown; extern const Color_RGBA seagreen; extern const Color_RGBA seashell; extern const Color_RGBA sienna; extern const Color_RGBA silver; extern const Color_RGBA skyblue; extern const Color_RGBA slateblue; extern const Color_RGBA slategray; extern const Color_RGBA slategrey; extern const Color_RGBA snow; extern const Color_RGBA springgreen; extern const Color_RGBA steelblue; extern const Color_RGBA tan; extern const Color_RGBA teal; extern const Color_RGBA thistle; extern const Color_RGBA tomato; extern const Color_RGBA turquoise; extern const Color_RGBA violet; extern const Color_RGBA wheat; extern const Color_RGBA white; extern const Color_RGBA whitesmoke; extern const Color_RGBA yellow; extern const Color_RGBA yellowgreen; extern const Color_RGBA rebeccapurple; extern const Color_RGBA transparent; } const Color_RGBA* name_to_color(const char*); const Color_RGBA* name_to_color(const sass::string&); const char* color_to_name(const int); const char* color_to_name(const Color_RGBA&); const char* color_to_name(const double); } #endif golibsass-1.0.0/libsass_src/src/constants.cpp000066400000000000000000000216271405214413600213240ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "constants.hpp" namespace Sass { namespace Constants { extern const unsigned long MaxCallStack = 1024; // https://github.com/sass/libsass/issues/592 // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity // https://github.com/sass/sass/issues/1495#issuecomment-61189114 extern const unsigned long Specificity_Star = 0; extern const unsigned long Specificity_Universal = 0; extern const unsigned long Specificity_Element = 1; extern const unsigned long Specificity_Base = 1000; extern const unsigned long Specificity_Class = 1000; extern const unsigned long Specificity_Attr = 1000; extern const unsigned long Specificity_Pseudo = 1000; extern const unsigned long Specificity_ID = 1000000; extern const int UnificationOrder_Element = 1; extern const int UnificationOrder_Id = 2; extern const int UnificationOrder_Class = 2; extern const int UnificationOrder_Attribute = 3; extern const int UnificationOrder_PseudoClass = 4; extern const int UnificationOrder_Wrapped = 5; extern const int UnificationOrder_PseudoElement = 6; extern const int UnificationOrder_Placeholder = 7; // sass keywords extern const char at_root_kwd[] = "@at-root"; extern const char import_kwd[] = "@import"; extern const char mixin_kwd[] = "@mixin"; extern const char function_kwd[] = "@function"; extern const char return_kwd[] = "@return"; extern const char include_kwd[] = "@include"; extern const char content_kwd[] = "@content"; extern const char extend_kwd[] = "@extend"; extern const char if_kwd[] = "@if"; extern const char else_kwd[] = "@else"; extern const char if_after_else_kwd[] = "if"; extern const char for_kwd[] = "@for"; extern const char from_kwd[] = "from"; extern const char to_kwd[] = "to"; extern const char of_kwd[] = "of"; extern const char through_kwd[] = "through"; extern const char each_kwd[] = "@each"; extern const char in_kwd[] = "in"; extern const char while_kwd[] = "@while"; extern const char warn_kwd[] = "@warn"; extern const char error_kwd[] = "@error"; extern const char debug_kwd[] = "@debug"; extern const char default_kwd[] = "default"; extern const char global_kwd[] = "global"; extern const char null_kwd[] = "null"; extern const char optional_kwd[] = "optional"; extern const char with_kwd[] = "with"; extern const char without_kwd[] = "without"; extern const char all_kwd[] = "all"; extern const char rule_kwd[] = "rule"; // css standard units extern const char em_kwd[] = "em"; extern const char ex_kwd[] = "ex"; extern const char px_kwd[] = "px"; extern const char cm_kwd[] = "cm"; extern const char mm_kwd[] = "mm"; extern const char pt_kwd[] = "pt"; extern const char pc_kwd[] = "pc"; extern const char deg_kwd[] = "deg"; extern const char rad_kwd[] = "rad"; extern const char grad_kwd[] = "grad"; extern const char turn_kwd[] = "turn"; extern const char ms_kwd[] = "ms"; extern const char s_kwd[] = "s"; extern const char Hz_kwd[] = "Hz"; extern const char kHz_kwd[] = "kHz"; // vendor prefixes extern const char vendor_opera_kwd[] = "-o-"; extern const char vendor_webkit_kwd[] = "-webkit-"; extern const char vendor_mozilla_kwd[] = "-moz-"; extern const char vendor_ms_kwd[] = "-ms-"; extern const char vendor_khtml_kwd[] = "-khtml-"; // css functions and keywords extern const char charset_kwd[] = "@charset"; extern const char media_kwd[] = "@media"; extern const char supports_kwd[] = "@supports"; extern const char keyframes_kwd[] = "keyframes"; extern const char only_kwd[] = "only"; extern const char rgb_fn_kwd[] = "rgb("; extern const char url_fn_kwd[] = "url("; extern const char url_kwd[] = "url"; // extern const char url_prefix_fn_kwd[] = "url-prefix("; extern const char important_kwd[] = "important"; extern const char pseudo_not_fn_kwd[] = ":not("; extern const char even_kwd[] = "even"; extern const char odd_kwd[] = "odd"; extern const char progid_kwd[] = "progid"; extern const char expression_kwd[] = "expression"; extern const char calc_fn_kwd[] = "calc"; extern const char almost_any_value_class[] = "\"'#!;{}"; // css selector keywords extern const char sel_deep_kwd[] = "/deep/"; // css attribute-matching operators extern const char tilde_equal[] = "~="; extern const char pipe_equal[] = "|="; extern const char caret_equal[] = "^="; extern const char dollar_equal[] = "$="; extern const char star_equal[] = "*="; // relational & logical operators and constants extern const char and_kwd[] = "and"; extern const char or_kwd[] = "or"; extern const char not_kwd[] = "not"; extern const char gt[] = ">"; extern const char gte[] = ">="; extern const char lt[] = "<"; extern const char lte[] = "<="; extern const char eq[] = "=="; extern const char neq[] = "!="; extern const char true_kwd[] = "true"; extern const char false_kwd[] = "false"; // definition keywords extern const char using_kwd[] = "using"; // miscellaneous punctuation and delimiters extern const char percent_str[] = "%"; extern const char empty_str[] = ""; extern const char slash_slash[] = "//"; extern const char slash_star[] = "/*"; extern const char star_slash[] = "*/"; extern const char hash_lbrace[] = "#{"; extern const char rbrace[] = "}"; extern const char rparen[] = ")"; extern const char sign_chars[] = "-+"; extern const char op_chars[] = "-+"; extern const char hyphen[] = "-"; extern const char ellipsis[] = "..."; // extern const char url_space_chars[] = " \t\r\n\f"; // type names extern const char numeric_name[] = "numeric value"; extern const char number_name[] = "number"; extern const char percentage_name[] = "percentage"; extern const char dimension_name[] = "numeric dimension"; extern const char string_name[] = "string"; extern const char bool_name[] = "bool"; extern const char color_name[] = "color"; extern const char list_name[] = "list"; extern const char map_name[] = "map"; extern const char arglist_name[] = "arglist"; // constants for uri parsing (RFC 3986 Appendix A.) extern const char uri_chars[] = ":;/?!%&#@|[]{}'`^\"*+-.,_=~"; extern const char real_uri_chars[] = "#%&"; extern const char selector_combinator_child[] = ">"; extern const char selector_combinator_general[] = "~"; extern const char selector_combinator_adjacent[] = "+"; // some specific constant character classes // they must be static to be useable by lexer extern const char static_ops[] = "*/%"; // some character classes for the parser extern const char selector_list_delims[] = "){};!"; extern const char complex_selector_delims[] = ",){};!"; extern const char selector_combinator_ops[] = "+~>"; // optional modifiers for alternative compare context extern const char attribute_compare_modifiers[] = "~|^$*"; extern const char selector_lookahead_ops[] = "*&%,()[]"; // byte order marks // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) extern const unsigned char utf_8_bom[] = { 0xEF, 0xBB, 0xBF }; extern const unsigned char utf_16_bom_be[] = { 0xFE, 0xFF }; extern const unsigned char utf_16_bom_le[] = { 0xFF, 0xFE }; extern const unsigned char utf_32_bom_be[] = { 0x00, 0x00, 0xFE, 0xFF }; extern const unsigned char utf_32_bom_le[] = { 0xFF, 0xFE, 0x00, 0x00 }; extern const unsigned char utf_7_bom_1[] = { 0x2B, 0x2F, 0x76, 0x38 }; extern const unsigned char utf_7_bom_2[] = { 0x2B, 0x2F, 0x76, 0x39 }; extern const unsigned char utf_7_bom_3[] = { 0x2B, 0x2F, 0x76, 0x2B }; extern const unsigned char utf_7_bom_4[] = { 0x2B, 0x2F, 0x76, 0x2F }; extern const unsigned char utf_7_bom_5[] = { 0x2B, 0x2F, 0x76, 0x38, 0x2D }; extern const unsigned char utf_1_bom[] = { 0xF7, 0x64, 0x4C }; extern const unsigned char utf_ebcdic_bom[] = { 0xDD, 0x73, 0x66, 0x73 }; extern const unsigned char scsu_bom[] = { 0x0E, 0xFE, 0xFF }; extern const unsigned char bocu_1_bom[] = { 0xFB, 0xEE, 0x28 }; extern const unsigned char gb_18030_bom[] = { 0x84, 0x31, 0x95, 0x33 }; } } golibsass-1.0.0/libsass_src/src/constants.hpp000066400000000000000000000153141405214413600213250ustar00rootroot00000000000000#ifndef SASS_CONSTANTS_H #define SASS_CONSTANTS_H namespace Sass { namespace Constants { // The maximum call stack that can be created extern const unsigned long MaxCallStack; // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity // The following list of selectors is by increasing specificity: extern const unsigned long Specificity_Star; extern const unsigned long Specificity_Universal; extern const unsigned long Specificity_Element; extern const unsigned long Specificity_Base; extern const unsigned long Specificity_Class; extern const unsigned long Specificity_Attr; extern const unsigned long Specificity_Pseudo; extern const unsigned long Specificity_ID; // Selector unification order; extern const int UnificationOrder_Element; extern const int UnificationOrder_Id; extern const int UnificationOrder_Class; extern const int UnificationOrder_Attribute; extern const int UnificationOrder_PseudoClass; extern const int UnificationOrder_Wrapped; extern const int UnificationOrder_PseudoElement; extern const int UnificationOrder_Placeholder; // sass keywords extern const char at_root_kwd[]; extern const char import_kwd[]; extern const char mixin_kwd[]; extern const char function_kwd[]; extern const char return_kwd[]; extern const char include_kwd[]; extern const char content_kwd[]; extern const char extend_kwd[]; extern const char if_kwd[]; extern const char else_kwd[]; extern const char if_after_else_kwd[]; extern const char for_kwd[]; extern const char from_kwd[]; extern const char to_kwd[]; extern const char of_kwd[]; extern const char through_kwd[]; extern const char each_kwd[]; extern const char in_kwd[]; extern const char while_kwd[]; extern const char warn_kwd[]; extern const char error_kwd[]; extern const char debug_kwd[]; extern const char default_kwd[]; extern const char global_kwd[]; extern const char null_kwd[]; extern const char optional_kwd[]; extern const char with_kwd[]; extern const char without_kwd[]; extern const char all_kwd[]; extern const char rule_kwd[]; // css standard units extern const char em_kwd[]; extern const char ex_kwd[]; extern const char px_kwd[]; extern const char cm_kwd[]; extern const char mm_kwd[]; extern const char pt_kwd[]; extern const char pc_kwd[]; extern const char deg_kwd[]; extern const char rad_kwd[]; extern const char grad_kwd[]; extern const char turn_kwd[]; extern const char ms_kwd[]; extern const char s_kwd[]; extern const char Hz_kwd[]; extern const char kHz_kwd[]; // vendor prefixes extern const char vendor_opera_kwd[]; extern const char vendor_webkit_kwd[]; extern const char vendor_mozilla_kwd[]; extern const char vendor_ms_kwd[]; extern const char vendor_khtml_kwd[]; // css functions and keywords extern const char charset_kwd[]; extern const char media_kwd[]; extern const char supports_kwd[]; extern const char keyframes_kwd[]; extern const char only_kwd[]; extern const char rgb_fn_kwd[]; extern const char url_fn_kwd[]; extern const char url_kwd[]; // extern const char url_prefix_fn_kwd[]; extern const char important_kwd[]; extern const char pseudo_not_fn_kwd[]; extern const char even_kwd[]; extern const char odd_kwd[]; extern const char progid_kwd[]; extern const char expression_kwd[]; extern const char calc_fn_kwd[]; // char classes for "regular expressions" extern const char almost_any_value_class[]; // css selector keywords extern const char sel_deep_kwd[]; // css attribute-matching operators extern const char tilde_equal[]; extern const char pipe_equal[]; extern const char caret_equal[]; extern const char dollar_equal[]; extern const char star_equal[]; // relational & logical operators and constants extern const char and_kwd[]; extern const char or_kwd[]; extern const char not_kwd[]; extern const char gt[]; extern const char gte[]; extern const char lt[]; extern const char lte[]; extern const char eq[]; extern const char neq[]; extern const char true_kwd[]; extern const char false_kwd[]; // definition keywords extern const char using_kwd[]; // miscellaneous punctuation and delimiters extern const char percent_str[]; extern const char empty_str[]; extern const char slash_slash[]; extern const char slash_star[]; extern const char star_slash[]; extern const char hash_lbrace[]; extern const char rbrace[]; extern const char rparen[]; extern const char sign_chars[]; extern const char op_chars[]; extern const char hyphen[]; extern const char ellipsis[]; // extern const char url_space_chars[]; // type names extern const char numeric_name[]; extern const char number_name[]; extern const char percentage_name[]; extern const char dimension_name[]; extern const char string_name[]; extern const char bool_name[]; extern const char color_name[]; extern const char list_name[]; extern const char map_name[]; extern const char arglist_name[]; // constants for uri parsing (RFC 3986 Appendix A.) extern const char uri_chars[]; extern const char real_uri_chars[]; // constants for selector combinators extern const char selector_combinator_child[]; extern const char selector_combinator_general[]; extern const char selector_combinator_adjacent[]; // some specific constant character classes // they must be static to be useable by lexer extern const char static_ops[]; extern const char selector_list_delims[]; extern const char complex_selector_delims[]; extern const char selector_combinator_ops[]; extern const char attribute_compare_modifiers[]; extern const char selector_lookahead_ops[]; // byte order marks // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) extern const unsigned char utf_8_bom[]; extern const unsigned char utf_16_bom_be[]; extern const unsigned char utf_16_bom_le[]; extern const unsigned char utf_32_bom_be[]; extern const unsigned char utf_32_bom_le[]; extern const unsigned char utf_7_bom_1[]; extern const unsigned char utf_7_bom_2[]; extern const unsigned char utf_7_bom_3[]; extern const unsigned char utf_7_bom_4[]; extern const unsigned char utf_7_bom_5[]; extern const unsigned char utf_1_bom[]; extern const unsigned char utf_ebcdic_bom[]; extern const unsigned char scsu_bom[]; extern const unsigned char bocu_1_bom[]; extern const unsigned char gb_18030_bom[]; } } #endif golibsass-1.0.0/libsass_src/src/context.cpp000066400000000000000000000773171405214413600210030ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "remove_placeholders.hpp" #include "sass_functions.hpp" #include "check_nesting.hpp" #include "fn_selectors.hpp" #include "fn_strings.hpp" #include "fn_numbers.hpp" #include "fn_colors.hpp" #include "fn_miscs.hpp" #include "fn_lists.hpp" #include "fn_maps.hpp" #include "context.hpp" #include "expand.hpp" #include "parser.hpp" #include "cssize.hpp" #include "source.hpp" namespace Sass { using namespace Constants; using namespace File; using namespace Sass; inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j) { return sass_importer_get_priority(i) > sass_importer_get_priority(j); } static sass::string safe_input(const char* in_path) { if (in_path == nullptr || in_path[0] == '\0') return "stdin"; return in_path; } static sass::string safe_output(const char* out_path, sass::string input_path) { if (out_path == nullptr || out_path[0] == '\0') { if (input_path.empty()) return "stdout"; return input_path.substr(0, input_path.find_last_of(".")) + ".css"; } return out_path; } Context::Context(struct Sass_Context& c_ctx) : CWD(File::get_cwd()), c_options(c_ctx), entry_path(""), head_imports(0), plugins(), emitter(c_options), ast_gc(), strings(), resources(), sheets(), import_stack(), callee_stack(), traces(), extender(Extender::NORMAL, traces), c_compiler(NULL), c_headers (sass::vector()), c_importers (sass::vector()), c_functions (sass::vector()), indent (safe_str(c_options.indent, " ")), linefeed (safe_str(c_options.linefeed, "\n")), input_path (make_canonical_path(safe_input(c_options.input_path))), output_path (make_canonical_path(safe_output(c_options.output_path, input_path))), source_map_file (make_canonical_path(safe_str(c_options.source_map_file, ""))), source_map_root (make_canonical_path(safe_str(c_options.source_map_root, ""))) { // Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default. // If you need the current working directory to be available, set SASS_PATH=. in your shell's environment. // include_paths.push_back(CWD); // collect more paths from different options collect_include_paths(c_options.include_path); collect_include_paths(c_options.include_paths); collect_plugin_paths(c_options.plugin_path); collect_plugin_paths(c_options.plugin_paths); // load plugins and register custom behaviors for(auto plug : plugin_paths) plugins.load_plugins(plug); for(auto fn : plugins.get_headers()) c_headers.push_back(fn); for(auto fn : plugins.get_importers()) c_importers.push_back(fn); for(auto fn : plugins.get_functions()) c_functions.push_back(fn); // sort the items by priority (lowest first) sort (c_headers.begin(), c_headers.end(), sort_importers); sort (c_importers.begin(), c_importers.end(), sort_importers); emitter.set_filename(abs2rel(output_path, source_map_file, CWD)); } void Context::add_c_function(Sass_Function_Entry function) { c_functions.push_back(function); } void Context::add_c_header(Sass_Importer_Entry header) { c_headers.push_back(header); // need to sort the array afterwards (no big deal) sort (c_headers.begin(), c_headers.end(), sort_importers); } void Context::add_c_importer(Sass_Importer_Entry importer) { c_importers.push_back(importer); // need to sort the array afterwards (no big deal) sort (c_importers.begin(), c_importers.end(), sort_importers); } Context::~Context() { // resources were allocated by malloc for (size_t i = 0; i < resources.size(); ++i) { free(resources[i].contents); free(resources[i].srcmap); } // free all strings we kept alive during compiler execution for (size_t n = 0; n < strings.size(); ++n) free(strings[n]); // everything that gets put into sources will be freed by us // this shouldn't have anything in it anyway!? for (size_t m = 0; m < import_stack.size(); ++m) { sass_import_take_source(import_stack[m]); sass_import_take_srcmap(import_stack[m]); sass_delete_import(import_stack[m]); } // clear inner structures (vectors) and input source resources.clear(); import_stack.clear(); sheets.clear(); } Data_Context::~Data_Context() { // --> this will be freed by resources // make sure we free the source even if not processed! // if (resources.size() == 0 && source_c_str) free(source_c_str); // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str); // source_c_str = 0; srcmap_c_str = 0; } File_Context::~File_Context() { } void Context::collect_include_paths(const char* paths_str) { if (paths_str) { const char* beg = paths_str; const char* end = Prelexer::find_first(beg); while (end) { sass::string path(beg, end - beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; include_paths.push_back(path); } beg = end + 1; end = Prelexer::find_first(beg); } sass::string path(beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; include_paths.push_back(path); } } } void Context::collect_include_paths(string_list* paths_array) { while (paths_array) { collect_include_paths(paths_array->string); paths_array = paths_array->next; } } void Context::collect_plugin_paths(const char* paths_str) { if (paths_str) { const char* beg = paths_str; const char* end = Prelexer::find_first(beg); while (end) { sass::string path(beg, end - beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; plugin_paths.push_back(path); } beg = end + 1; end = Prelexer::find_first(beg); } sass::string path(beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; plugin_paths.push_back(path); } } } void Context::collect_plugin_paths(string_list* paths_array) { while (paths_array) { collect_plugin_paths(paths_array->string); paths_array = paths_array->next; } } // resolve the imp_path in base_path or include_paths // looks for alternatives and returns a list from one directory sass::vector Context::find_includes(const Importer& import) { // make sure we resolve against an absolute path sass::string base_path(rel2abs(import.base_path)); // first try to resolve the load path relative to the base path sass::vector vec(resolve_includes(base_path, import.imp_path)); // then search in every include path (but only if nothing found yet) for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i) { // call resolve_includes and individual base path and append all results sass::vector resolved(resolve_includes(include_paths[i], import.imp_path)); if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end()); } // return vector return vec; } // register include with resolved path and its content // memory of the resources will be freed by us on exit void Context::register_resource(const Include& inc, const Resource& res) { // do not parse same resource twice // maybe raise an error in this case // if (sheets.count(inc.abs_path)) { // free(res.contents); free(res.srcmap); // throw std::runtime_error("duplicate resource registered"); // return; // } // get index for this resource size_t idx = resources.size(); // tell emitter about new resource emitter.add_source_index(idx); // put resources under our control // the memory will be freed later resources.push_back(res); // add a relative link to the working directory included_files.push_back(inc.abs_path); // add a relative link to the source map output file srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD)); // get pointer to the loaded content Sass_Import_Entry import = sass_make_import( inc.imp_path.c_str(), inc.abs_path.c_str(), res.contents, res.srcmap ); // add the entry to the stack import_stack.push_back(import); // get pointer to the loaded content const char* contents = resources[idx].contents; SourceFileObj source = SASS_MEMORY_NEW(SourceFile, inc.abs_path.c_str(), contents, idx); // create the initial parser state from resource SourceSpan pstate(source); // check existing import stack for possible recursion for (size_t i = 0; i < import_stack.size() - 2; ++i) { auto parent = import_stack[i]; if (std::strcmp(parent->abs_path, import->abs_path) == 0) { sass::string cwd(File::get_cwd()); // make path relative to the current directory sass::string stack("An @import loop has been found:"); for (size_t n = 1; n < i + 2; ++n) { stack += "\n " + sass::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) + " imports " + sass::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd)); } // implement error throw directly until we // decided how to handle full stack traces throw Exception::InvalidSyntax(pstate, traces, stack); // error(stack, prstate ? *prstate : pstate, import_stack); } } // create a parser instance from the given c_str buffer Parser p(source, *this, traces); // do not yet dispose these buffers sass_import_take_source(import); sass_import_take_srcmap(import); // then parse the root block Block_Obj root = p.parse(); // delete memory of current stack frame sass_delete_import(import_stack.back()); // remove current stack frame import_stack.pop_back(); // create key/value pair for ast node std::pair ast_pair(inc.abs_path, { res, root }); // register resulting resource sheets.insert(ast_pair); } // register include with resolved path and its content // memory of the resources will be freed by us on exit void Context::register_resource(const Include& inc, const Resource& res, SourceSpan& prstate) { traces.push_back(Backtrace(prstate)); register_resource(inc, res); traces.pop_back(); } // Add a new import to the context (called from `import_url`) Include Context::load_import(const Importer& imp, SourceSpan pstate) { // search for valid imports (ie. partials) on the filesystem // this may return more than one valid result (ambiguous imp_path) const sass::vector resolved(find_includes(imp)); // error nicely on ambiguous imp_path if (resolved.size() > 1) { sass::ostream msg_stream; msg_stream << "It's not clear which file to import for "; msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n"; msg_stream << "Candidates:" << "\n"; for (size_t i = 0, L = resolved.size(); i < L; ++i) { msg_stream << " " << resolved[i].imp_path << "\n"; } msg_stream << "Please delete or rename all but one of these files." << "\n"; error(msg_stream.str(), pstate, traces); } // process the resolved entry else if (resolved.size() == 1) { bool use_cache = c_importers.size() == 0; // use cache for the resource loading if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0]; // try to read the content of the resolved file entry // the memory buffer returned must be freed by us! if (char* contents = read_file(resolved[0].abs_path)) { // register the newly resolved file resource register_resource(resolved[0], { contents, 0 }, pstate); // return resolved entry return resolved[0]; } } // nothing found return { imp, "" }; } void Context::import_url (Import* imp, sass::string load_path, const sass::string& ctx_path) { SourceSpan pstate(imp->pstate()); sass::string imp_path(unquote(load_path)); sass::string protocol("file"); using namespace Prelexer; if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) { protocol = sass::string(imp_path.c_str(), proto - 3); // if (protocol.compare("file") && true) { } } // add urls (protocol other than file) and urls without protocol to `urls` member // ToDo: if ctx_path is already a file resource, we should not add it here? if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") { imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path)); } else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") { String_Constant* loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path)); Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc); Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate); loc_args->append(loc_arg); Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), loc_args); imp->urls().push_back(new_url); } else { const Importer importer(imp_path, ctx_path); Include include(load_import(importer, pstate)); if (include.abs_path.empty()) { error("File to import not found or unreadable: " + imp_path + ".", pstate, traces); } imp->incs().push_back(include); } } // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet bool Context::call_loader(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::vector importers, bool only_one) { // unique counter size_t count = 0; // need one correct import bool has_import = false; // process all custom importers (or custom headers) for (Sass_Importer_Entry& importer_ent : importers) { // int priority = sass_importer_get_priority(importer); Sass_Importer_Fn fn = sass_importer_get_function(importer_ent); // skip importer if it returns NULL if (Sass_Import_List includes = fn(load_path.c_str(), importer_ent, c_compiler) ) { // get c pointer copy to iterate over Sass_Import_List it_includes = includes; while (*it_includes) { ++count; // create unique path to use as key sass::string uniq_path = load_path; if (!only_one && count) { sass::ostream path_strm; path_strm << uniq_path << ":" << count; uniq_path = path_strm.str(); } // create the importer struct Importer importer(uniq_path, ctx_path); // query data from the current include Sass_Import_Entry include_ent = *it_includes; char* source = sass_import_take_source(include_ent); char* srcmap = sass_import_take_srcmap(include_ent); size_t line = sass_import_get_error_line(include_ent); size_t column = sass_import_get_error_column(include_ent); const char *abs_path = sass_import_get_abs_path(include_ent); // handle error message passed back from custom importer // it may (or may not) override the line and column info if (const char* err_message = sass_import_get_error_message(include_ent)) { if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate); if (line == sass::string::npos && column == sass::string::npos) error(err_message, pstate, traces); else { error(err_message, { pstate.source, { line, column } }, traces); } } // content for import was set else if (source) { // resolved abs_path should be set by custom importer // use the created uniq_path as fallback (maybe enforce) sass::string path_key(abs_path ? abs_path : uniq_path); // create the importer struct Include include(importer, path_key); // attach information to AST node imp->incs().push_back(include); // register the resource buffers register_resource(include, { source, srcmap }, pstate); } // only a path was retuned // try to load it like normal else if(abs_path) { // checks some urls to preserve // `http://`, `https://` and `//` // or dispatchs to `import_file` // which will check for a `.css` extension // or resolves the file on the filesystem // added and resolved via `add_file` // finally stores everything on `imp` import_url(imp, abs_path, ctx_path); } // move to next ++it_includes; } // deallocate the returned memory sass_delete_import_list(includes); // set success flag has_import = true; // break out of loop if (only_one) break; } } // return result return has_import; } void register_function(Context&, Signature sig, Native_Function f, Env* env); void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env); void register_overload_stub(Context&, sass::string name, Env* env); void register_built_in_functions(Context&, Env* env); void register_c_functions(Context&, Env* env, Sass_Function_List); void register_c_function(Context&, Env* env, Sass_Function_Entry); char* Context::render(Block_Obj root) { // check for valid block if (!root) return 0; // start the render process root->perform(&emitter); // finish emitter stream emitter.finalize(); // get the resulting buffer from stream OutputBuffer emitted = emitter.get_buffer(); // should we append a source map url? if (!c_options.omit_source_map_url) { // generate an embedded source map if (c_options.source_map_embed) { emitted.buffer += linefeed; emitted.buffer += format_embedded_source_map(); } // or just link the generated one else if (source_map_file != "") { emitted.buffer += linefeed; emitted.buffer += format_source_mapping_url(source_map_file); } } // create a copy of the resulting buffer string // this must be freed or taken over by implementor return sass_copy_c_string(emitted.buffer.c_str()); } void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, SourceSpan pstate) { // create a custom import to resolve headers Import_Obj imp = SASS_MEMORY_NEW(Import, pstate); // dispatch headers which will add custom functions // custom headers are added to the import instance call_headers(entry_path, ctx_path, pstate, imp); // increase head count to skip later head_imports += resources.size() - 1; // add the statement if we have urls if (!imp->urls().empty()) root->append(imp); // process all other resources (add Import_Stub nodes) for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i])); } } Block_Obj File_Context::parse() { // check if entry file is given if (input_path.empty()) return {}; // create absolute path from input filename // ToDo: this should be resolved via custom importers sass::string abs_path(rel2abs(input_path, CWD)); // try to load the entry file char* contents = read_file(abs_path); // alternatively also look inside each include path folder // I think this differs from ruby sass (IMO too late to remove) for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) { // build absolute path for this include path entry abs_path = rel2abs(input_path, include_paths[i]); // try to load the resulting path contents = read_file(abs_path); } // abort early if no content could be loaded (various reasons) if (!contents) throw std::runtime_error( "File to read not found or unreadable: " + std::string(input_path.c_str())); // store entry path entry_path = abs_path; // create entry only for import stack Sass_Import_Entry import = sass_make_import( input_path.c_str(), entry_path.c_str(), contents, 0 ); // add the entry to the stack import_stack.push_back(import); // create the source entry for file entry register_resource({{ input_path, "." }, abs_path }, { contents, 0 }); // create root ast tree node return compile(); } Block_Obj Data_Context::parse() { // check if source string is given if (!source_c_str) return {}; // convert indented sass syntax if(c_options.is_indented_syntax_src) { // call sass2scss to convert the string char * converted = sass2scss(source_c_str, // preserve the structure as much as possible SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); // replace old source_c_str with converted free(source_c_str); source_c_str = converted; } // remember entry path (defaults to stdin for string) entry_path = input_path.empty() ? "stdin" : input_path; // ToDo: this may be resolved via custom importers sass::string abs_path(rel2abs(entry_path)); char* abs_path_c_str = sass_copy_c_string(abs_path.c_str()); strings.push_back(abs_path_c_str); // create entry only for the import stack Sass_Import_Entry import = sass_make_import( entry_path.c_str(), abs_path_c_str, source_c_str, srcmap_c_str ); // add the entry to the stack import_stack.push_back(import); // register a synthetic resource (path does not really exist, skip in includes) register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str }); // create root ast tree node return compile(); } // parse root block from includes Block_Obj Context::compile() { // abort if there is no data if (resources.size() == 0) return {}; // get root block from the first style sheet Block_Obj root = sheets.at(entry_path).root; // abort on invalid root if (root.isNull()) return {}; Env global; // create root environment // register built-in functions on env register_built_in_functions(*this, &global); // register custom functions (defined via C-API) for (size_t i = 0, S = c_functions.size(); i < S; ++i) { register_c_function(*this, &global, c_functions[i]); } // create initial backtrace entry // create crtp visitor objects Expand expand(*this, &global); Cssize cssize(*this); CheckNesting check_nesting; // check nesting in all files for (auto sheet : sheets) { auto styles = sheet.second; check_nesting(styles.root); } // expand and eval the tree root = expand(root); Extension unsatisfied; // check that all extends were used if (extender.checkForUnsatisfiedExtends(unsatisfied)) { throw Exception::UnsatisfiedExtend(traces, unsatisfied); } // check nesting check_nesting(root); // merge and bubble certain rules root = cssize(root); // clean up by removing empty placeholders // ToDo: maybe we can do this somewhere else? Remove_Placeholders remove_placeholders; root->perform(&remove_placeholders); // return processed tree return root; } // EO compile sass::string Context::format_embedded_source_map() { sass::string map = emitter.render_srcmap(*this); sass::istream is( map.c_str() ); sass::ostream buffer; base64::encoder E; E.encode(is, buffer); sass::string url = "data:application/json;base64," + buffer.str(); url.erase(url.size() - 1); return "/*# sourceMappingURL=" + url + " */"; } sass::string Context::format_source_mapping_url(const sass::string& file) { sass::string url = abs2rel(file, output_path, CWD); return "/*# sourceMappingURL=" + url + " */"; } char* Context::render_srcmap() { if (source_map_file == "") return 0; sass::string map = emitter.render_srcmap(*this); return sass_copy_c_string(map.c_str()); } // for data context we want to start after "stdin" // we probably always want to skip the header includes? sass::vector Context::get_included_files(bool skip, size_t headers) { // create a copy of the vector for manipulations sass::vector includes = included_files; if (includes.size() == 0) return includes; if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); } else { includes.erase( includes.begin() + 1, includes.begin() + 1 + headers); } includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() ); std::sort( includes.begin() + (skip ? 0 : 1), includes.end() ); return includes; } void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) { Definition* def = make_native_function(sig, f, ctx); def->environment(env); (*env)[def->name() + "[f]"] = def; } void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env) { Definition* def = make_native_function(sig, f, ctx); sass::ostream ss; ss << def->name() << "[f]" << arity; def->environment(env); (*env)[ss.str()] = def; } void register_overload_stub(Context& ctx, sass::string name, Env* env) { Definition* stub = SASS_MEMORY_NEW(Definition, SourceSpan{ "[built-in function]" }, nullptr, name, Parameters_Obj{}, nullptr, true); (*env)[name + "[f]"] = stub; } void register_built_in_functions(Context& ctx, Env* env) { using namespace Functions; // RGB Functions register_function(ctx, rgb_sig, rgb, env); register_overload_stub(ctx, "rgba", env); register_function(ctx, rgba_4_sig, rgba_4, 4, env); register_function(ctx, rgba_2_sig, rgba_2, 2, env); register_function(ctx, red_sig, red, env); register_function(ctx, green_sig, green, env); register_function(ctx, blue_sig, blue, env); register_function(ctx, mix_sig, mix, env); // HSL Functions register_function(ctx, hsl_sig, hsl, env); register_function(ctx, hsla_sig, hsla, env); register_function(ctx, hue_sig, hue, env); register_function(ctx, saturation_sig, saturation, env); register_function(ctx, lightness_sig, lightness, env); register_function(ctx, adjust_hue_sig, adjust_hue, env); register_function(ctx, lighten_sig, lighten, env); register_function(ctx, darken_sig, darken, env); register_function(ctx, saturate_sig, saturate, env); register_function(ctx, desaturate_sig, desaturate, env); register_function(ctx, grayscale_sig, grayscale, env); register_function(ctx, complement_sig, complement, env); register_function(ctx, invert_sig, invert, env); // Opacity Functions register_function(ctx, alpha_sig, alpha, env); register_function(ctx, opacity_sig, alpha, env); register_function(ctx, opacify_sig, opacify, env); register_function(ctx, fade_in_sig, opacify, env); register_function(ctx, transparentize_sig, transparentize, env); register_function(ctx, fade_out_sig, transparentize, env); // Other Color Functions register_function(ctx, adjust_color_sig, adjust_color, env); register_function(ctx, scale_color_sig, scale_color, env); register_function(ctx, change_color_sig, change_color, env); register_function(ctx, ie_hex_str_sig, ie_hex_str, env); // String Functions register_function(ctx, unquote_sig, sass_unquote, env); register_function(ctx, quote_sig, sass_quote, env); register_function(ctx, str_length_sig, str_length, env); register_function(ctx, str_insert_sig, str_insert, env); register_function(ctx, str_index_sig, str_index, env); register_function(ctx, str_slice_sig, str_slice, env); register_function(ctx, to_upper_case_sig, to_upper_case, env); register_function(ctx, to_lower_case_sig, to_lower_case, env); // Number Functions register_function(ctx, percentage_sig, percentage, env); register_function(ctx, round_sig, round, env); register_function(ctx, ceil_sig, ceil, env); register_function(ctx, floor_sig, floor, env); register_function(ctx, abs_sig, abs, env); register_function(ctx, min_sig, min, env); register_function(ctx, max_sig, max, env); register_function(ctx, random_sig, random, env); // List Functions register_function(ctx, length_sig, length, env); register_function(ctx, nth_sig, nth, env); register_function(ctx, set_nth_sig, set_nth, env); register_function(ctx, index_sig, index, env); register_function(ctx, join_sig, join, env); register_function(ctx, append_sig, append, env); register_function(ctx, zip_sig, zip, env); register_function(ctx, list_separator_sig, list_separator, env); register_function(ctx, is_bracketed_sig, is_bracketed, env); // Map Functions register_function(ctx, map_get_sig, map_get, env); register_function(ctx, map_merge_sig, map_merge, env); register_function(ctx, map_remove_sig, map_remove, env); register_function(ctx, map_keys_sig, map_keys, env); register_function(ctx, map_values_sig, map_values, env); register_function(ctx, map_has_key_sig, map_has_key, env); register_function(ctx, keywords_sig, keywords, env); // Introspection Functions register_function(ctx, type_of_sig, type_of, env); register_function(ctx, unit_sig, unit, env); register_function(ctx, unitless_sig, unitless, env); register_function(ctx, comparable_sig, comparable, env); register_function(ctx, variable_exists_sig, variable_exists, env); register_function(ctx, global_variable_exists_sig, global_variable_exists, env); register_function(ctx, function_exists_sig, function_exists, env); register_function(ctx, mixin_exists_sig, mixin_exists, env); register_function(ctx, feature_exists_sig, feature_exists, env); register_function(ctx, call_sig, call, env); register_function(ctx, content_exists_sig, content_exists, env); register_function(ctx, get_function_sig, get_function, env); // Boolean Functions register_function(ctx, not_sig, sass_not, env); register_function(ctx, if_sig, sass_if, env); // Misc Functions register_function(ctx, inspect_sig, inspect, env); register_function(ctx, unique_id_sig, unique_id, env); // Selector functions register_function(ctx, selector_nest_sig, selector_nest, env); register_function(ctx, selector_append_sig, selector_append, env); register_function(ctx, selector_extend_sig, selector_extend, env); register_function(ctx, selector_replace_sig, selector_replace, env); register_function(ctx, selector_unify_sig, selector_unify, env); register_function(ctx, is_superselector_sig, is_superselector, env); register_function(ctx, simple_selectors_sig, simple_selectors, env); register_function(ctx, selector_parse_sig, selector_parse, env); } void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs) { while (descrs && *descrs) { register_c_function(ctx, env, *descrs); ++descrs; } } void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr) { Definition* def = make_c_function(descr, ctx); def->environment(env); (*env)[def->name() + "[f]"] = def; } } golibsass-1.0.0/libsass_src/src/context.hpp000066400000000000000000000113621405214413600207740ustar00rootroot00000000000000#ifndef SASS_CONTEXT_H #define SASS_CONTEXT_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #define BUFFERSIZE 255 #include "b64/encode.h" #include "sass_context.hpp" #include "stylesheet.hpp" #include "plugins.hpp" #include "output.hpp" namespace Sass { class Context { public: void import_url (Import* imp, sass::string load_path, const sass::string& ctx_path); bool call_headers(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp) { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); }; bool call_importers(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp) { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); }; private: bool call_loader(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::vector importers, bool only_one = true); public: const sass::string CWD; struct Sass_Options& c_options; sass::string entry_path; size_t head_imports; Plugins plugins; Output emitter; // generic ast node garbage container // used to avoid possible circular refs CallStack ast_gc; // resources add under our control // these are guaranteed to be freed sass::vector strings; sass::vector resources; std::map sheets; ImporterStack import_stack; sass::vector callee_stack; sass::vector traces; Extender extender; struct Sass_Compiler* c_compiler; // absolute paths to includes sass::vector included_files; // relative includes for sourcemap sass::vector srcmap_links; // vectors above have same size sass::vector plugin_paths; // relative paths to load plugins sass::vector include_paths; // lookup paths for includes void apply_custom_headers(Block_Obj root, const char* path, SourceSpan pstate); sass::vector c_headers; sass::vector c_importers; sass::vector c_functions; void add_c_header(Sass_Importer_Entry header); void add_c_importer(Sass_Importer_Entry importer); void add_c_function(Sass_Function_Entry function); const sass::string indent; // String to be used for indentation const sass::string linefeed; // String to be used for line feeds const sass::string input_path; // for relative paths in src-map const sass::string output_path; // for relative paths to the output const sass::string source_map_file; // path to source map file (enables feature) const sass::string source_map_root; // path for sourceRoot property (pass-through) virtual ~Context(); Context(struct Sass_Context&); virtual Block_Obj parse() = 0; virtual Block_Obj compile(); virtual char* render(Block_Obj root); virtual char* render_srcmap(); void register_resource(const Include&, const Resource&); void register_resource(const Include&, const Resource&, SourceSpan&); sass::vector find_includes(const Importer& import); Include load_import(const Importer&, SourceSpan pstate); Sass_Output_Style output_style() { return c_options.output_style; }; sass::vector get_included_files(bool skip = false, size_t headers = 0); private: void collect_plugin_paths(const char* paths_str); void collect_plugin_paths(string_list* paths_array); void collect_include_paths(const char* paths_str); void collect_include_paths(string_list* paths_array); sass::string format_embedded_source_map(); sass::string format_source_mapping_url(const sass::string& out_path); // void register_built_in_functions(Env* env); // void register_function(Signature sig, Native_Function f, Env* env); // void register_function(Signature sig, Native_Function f, size_t arity, Env* env); // void register_overload_stub(sass::string name, Env* env); public: const sass::string& cwd() { return CWD; }; }; class File_Context : public Context { public: File_Context(struct Sass_File_Context& ctx) : Context(ctx) { } virtual ~File_Context(); virtual Block_Obj parse(); }; class Data_Context : public Context { public: char* source_c_str; char* srcmap_c_str; Data_Context(struct Sass_Data_Context& ctx) : Context(ctx) { source_c_str = ctx.source_string; srcmap_c_str = ctx.srcmap_string; ctx.source_string = 0; // passed away ctx.srcmap_string = 0; // passed away } virtual ~Data_Context(); virtual Block_Obj parse(); }; } #endif golibsass-1.0.0/libsass_src/src/cssize.cpp000066400000000000000000000360141405214413600206040ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include "cssize.hpp" #include "context.hpp" namespace Sass { Cssize::Cssize(Context& ctx) : traces(ctx.traces), block_stack(BlockStack()), p_stack(sass::vector()) { } Statement* Cssize::parent() { return p_stack.size() ? p_stack.back() : block_stack.front(); } Block* Cssize::operator()(Block* b) { Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root()); // bb->tabs(b->tabs()); block_stack.push_back(bb); append_block(b, bb); block_stack.pop_back(); return bb.detach(); } Statement* Cssize::operator()(Trace* t) { traces.push_back(Backtrace(t->pstate())); auto result = t->block()->perform(this); traces.pop_back(); return result; } Statement* Cssize::operator()(Declaration* d) { String_Obj property = Cast(d->property()); if (Declaration* dd = Cast(parent())) { String_Obj parent_property = Cast(dd->property()); property = SASS_MEMORY_NEW(String_Constant, d->property()->pstate(), parent_property->to_string() + "-" + property->to_string()); if (!dd->value()) { d->tabs(dd->tabs() + 1); } } Declaration_Obj dd = SASS_MEMORY_NEW(Declaration, d->pstate(), property, d->value(), d->is_important(), d->is_custom_property()); dd->is_indented(d->is_indented()); dd->tabs(d->tabs()); p_stack.push_back(dd); Block_Obj bb = d->block() ? operator()(d->block()) : NULL; p_stack.pop_back(); if (bb && bb->length()) { if (dd->value() && !dd->value()->is_invisible()) { bb->unshift(dd); } return bb.detach(); } else if (dd->value() && !dd->value()->is_invisible()) { return dd.detach(); } return 0; } Statement* Cssize::operator()(AtRule* r) { if (!r->block() || !r->block()->length()) return r; if (parent()->statement_type() == Statement::RULESET) { return r->is_keyframes() ? SASS_MEMORY_NEW(Bubble, r->pstate(), r) : bubble(r); } p_stack.push_back(r); AtRuleObj rr = SASS_MEMORY_NEW(AtRule, r->pstate(), r->keyword(), r->selector(), r->block() ? operator()(r->block()) : 0); if (r->value()) rr->value(r->value()); p_stack.pop_back(); bool directive_exists = false; size_t L = rr->block() ? rr->block()->length() : 0; for (size_t i = 0; i < L && !directive_exists; ++i) { Statement_Obj s = r->block()->at(i); if (s->statement_type() != Statement::BUBBLE) directive_exists = true; else { Bubble_Obj s_obj = Cast(s); s = s_obj->node(); if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false; else directive_exists = (Cast(s)->keyword() == rr->keyword()); } } Block* result = SASS_MEMORY_NEW(Block, rr->pstate()); if (!(directive_exists || rr->is_keyframes())) { AtRule* empty_node = Cast(rr); empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate())); result->append(empty_node); } Block_Obj db = rr->block(); if (db.isNull()) db = SASS_MEMORY_NEW(Block, rr->pstate()); Block_Obj ss = debubble(db, rr); for (size_t i = 0, L = ss->length(); i < L; ++i) { result->append(ss->at(i)); } return result; } Statement* Cssize::operator()(Keyframe_Rule* r) { if (!r->block() || !r->block()->length()) return r; Keyframe_Rule_Obj rr = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), operator()(r->block())); if (!r->name().isNull()) rr->name(r->name()); return debubble(rr->block(), rr); } Statement* Cssize::operator()(StyleRule* r) { p_stack.push_back(r); // this can return a string schema // string schema is not a statement! // r->block() is already a string schema // and that is coming from propset expand Block* bb = operator()(r->block()); // this should protect us (at least a bit) from our mess // fixing this properly is harder that it should be ... if (Cast(bb) == NULL) { error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces); } StyleRuleObj rr = SASS_MEMORY_NEW(StyleRule, r->pstate(), r->selector(), bb); rr->is_root(r->is_root()); // rr->tabs(r->block()->tabs()); p_stack.pop_back(); if (!rr->block()) { error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces); } Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate()); Block* rules = SASS_MEMORY_NEW(Block, rr->block()->pstate()); for (size_t i = 0, L = rr->block()->length(); i < L; i++) { Statement* s = rr->block()->at(i); if (bubblable(s)) rules->append(s); if (!bubblable(s)) props->append(s); } if (props->length()) { Block_Obj pb = SASS_MEMORY_NEW(Block, rr->block()->pstate()); pb->concat(props); rr->block(pb); for (size_t i = 0, L = rules->length(); i < L; i++) { Statement* stm = rules->at(i); stm->tabs(stm->tabs() + 1); } rules->unshift(rr); } Block* ptr = rules; rules = debubble(rules); void* lp = ptr; void* rp = rules; if (lp != rp) { Block_Obj obj = ptr; } if (!(!rules->length() || !bubblable(rules->last()) || parent()->statement_type() == Statement::RULESET)) { rules->last()->group_end(true); } return rules; } Statement* Cssize::operator()(Null* m) { return 0; } Statement* Cssize::operator()(CssMediaRule* m) { if (parent()->statement_type() == Statement::RULESET) { return bubble(m); } if (parent()->statement_type() == Statement::MEDIA) { return SASS_MEMORY_NEW(Bubble, m->pstate(), m); } p_stack.push_back(m); CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block()); mm->concat(m->elements()); mm->block(operator()(m->block())); mm->tabs(m->tabs()); p_stack.pop_back(); return debubble(mm->block(), mm); } Statement* Cssize::operator()(SupportsRule* m) { if (!m->block()->length()) { return m; } if (parent()->statement_type() == Statement::RULESET) { return bubble(m); } p_stack.push_back(m); SupportsRuleObj mm = SASS_MEMORY_NEW(SupportsRule, m->pstate(), m->condition(), operator()(m->block())); mm->tabs(m->tabs()); p_stack.pop_back(); return debubble(mm->block(), mm); } Statement* Cssize::operator()(AtRootRule* m) { bool tmp = false; for (size_t i = 0, L = p_stack.size(); i < L; ++i) { Statement* s = p_stack[i]; tmp |= m->exclude_node(s); } if (!tmp && m->block()) { Block* bb = operator()(m->block()); for (size_t i = 0, L = bb->length(); i < L; ++i) { // (bb->elements())[i]->tabs(m->tabs()); Statement_Obj stm = bb->at(i); if (bubblable(stm)) stm->tabs(stm->tabs() + m->tabs()); } if (bb->length() && bubblable(bb->last())) bb->last()->group_end(m->group_end()); return bb; } if (m->exclude_node(parent())) { return SASS_MEMORY_NEW(Bubble, m->pstate(), m); } return bubble(m); } Statement* Cssize::bubble(AtRule* m) { Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); ParentStatementObj new_rule = Cast(SASS_MEMORY_COPY(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs()); new_rule->block()->concat(m->block()); Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate()); wrapper_block->append(new_rule); AtRuleObj mm = SASS_MEMORY_NEW(AtRule, m->pstate(), m->keyword(), m->selector(), wrapper_block); if (m->value()) mm->value(m->value()); Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } Statement* Cssize::bubble(AtRootRule* m) { if (!m || !m->block()) return NULL; Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate()); ParentStatementObj new_rule = Cast(SASS_MEMORY_COPY(this->parent())); Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); if (new_rule) { new_rule->block(bb); new_rule->tabs(this->parent()->tabs()); new_rule->block()->concat(m->block()); wrapper_block->append(new_rule); } AtRootRule* mm = SASS_MEMORY_NEW(AtRootRule, m->pstate(), wrapper_block, m->expression()); Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } Statement* Cssize::bubble(SupportsRule* m) { StyleRuleObj parent = Cast(SASS_MEMORY_COPY(this->parent())); Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs()); new_rule->block()->concat(m->block()); Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); wrapper_block->append(new_rule); SupportsRule* mm = SASS_MEMORY_NEW(SupportsRule, m->pstate(), m->condition(), wrapper_block); mm->tabs(m->tabs()); Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); return bubble; } Statement* Cssize::bubble(CssMediaRule* m) { StyleRuleObj parent = Cast(SASS_MEMORY_COPY(this->parent())); Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate()); StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs()); new_rule->block()->concat(m->block()); Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate()); wrapper_block->append(new_rule); CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), wrapper_block); mm->concat(m->elements()); mm->tabs(m->tabs()); return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm); } bool Cssize::bubblable(Statement* s) { return Cast(s) || (s && s->bubbles()); } Block* Cssize::flatten(const Block* b) { Block* result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root()); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* ss = b->at(i); if (const Block* bb = Cast(ss)) { Block_Obj bs = flatten(bb); for (size_t j = 0, K = bs->length(); j < K; ++j) { result->append(bs->at(j)); } } else { result->append(ss); } } return result; } sass::vector> Cssize::slice_by_bubble(Block* b) { sass::vector> results; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj value = b->at(i); bool key = Cast(value) != NULL; if (!results.empty() && results.back().first == key) { Block_Obj wrapper_block = results.back().second; wrapper_block->append(value); } else { Block* wrapper_block = SASS_MEMORY_NEW(Block, value->pstate()); wrapper_block->append(value); results.push_back(std::make_pair(key, wrapper_block)); } } return results; } Block* Cssize::debubble(Block* children, Statement* parent) { ParentStatementObj previous_parent; sass::vector> baz = slice_by_bubble(children); Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate()); for (size_t i = 0, L = baz.size(); i < L; ++i) { bool is_bubble = baz[i].first; Block_Obj slice = baz[i].second; if (!is_bubble) { if (!parent) { result->append(slice); } else if (previous_parent) { previous_parent->block()->concat(slice); } else { previous_parent = SASS_MEMORY_COPY(parent); previous_parent->block(slice); previous_parent->tabs(parent->tabs()); result->append(previous_parent); } continue; } for (size_t j = 0, K = slice->length(); j < K; ++j) { Statement_Obj ss; Statement_Obj stm = slice->at(j); // this has to go now here (too bad) Bubble_Obj node = Cast(stm); CssMediaRule* rule1 = NULL; CssMediaRule* rule2 = NULL; if (parent) rule1 = Cast(parent); if (node) rule2 = Cast(node->node()); if (rule1 || rule2) { ss = node->node(); } ss = node->node(); if (!ss) { continue; } ss->tabs(ss->tabs() + node->tabs()); ss->group_end(node->group_end()); Block_Obj bb = SASS_MEMORY_NEW(Block, children->pstate(), children->length(), children->is_root()); auto evaled = ss->perform(this); if (evaled) bb->append(evaled); Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, children->pstate(), children->length(), children->is_root()); Block* wrapper = flatten(bb); wrapper_block->append(wrapper); if (wrapper->length()) { previous_parent = {}; } if (wrapper_block) { result->append(wrapper_block); } } } return flatten(result); } void Cssize::append_block(Block* b, Block* cur) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj ith = b->at(i)->perform(this); if (Block_Obj bb = Cast(ith)) { for (size_t j = 0, K = bb->length(); j < K; ++j) { cur->append(bb->at(j)); } } else if (ith) { cur->append(ith); } } } } golibsass-1.0.0/libsass_src/src/cssize.hpp000066400000000000000000000036741405214413600206170ustar00rootroot00000000000000#ifndef SASS_CSSIZE_H #define SASS_CSSIZE_H #include "ast.hpp" #include "context.hpp" #include "operation.hpp" #include "environment.hpp" namespace Sass { struct Backtrace; class Cssize : public Operation_CRTP { Backtraces& traces; BlockStack block_stack; sass::vector p_stack; public: Cssize(Context&); ~Cssize() { } Block* operator()(Block*); Statement* operator()(StyleRule*); // Statement* operator()(Bubble*); Statement* operator()(CssMediaRule*); Statement* operator()(SupportsRule*); Statement* operator()(AtRootRule*); Statement* operator()(AtRule*); Statement* operator()(Keyframe_Rule*); Statement* operator()(Trace*); Statement* operator()(Declaration*); // Statement* operator()(Assignment*); // Statement* operator()(Import*); // Statement* operator()(Import_Stub*); // Statement* operator()(WarningRule*); // Statement* operator()(Error*); // Statement* operator()(Comment*); // Statement* operator()(If*); // Statement* operator()(ForRule*); // Statement* operator()(EachRule*); // Statement* operator()(WhileRule*); // Statement* operator()(Return*); // Statement* operator()(ExtendRule*); // Statement* operator()(Definition*); // Statement* operator()(Mixin_Call*); // Statement* operator()(Content*); Statement* operator()(Null*); Statement* parent(); sass::vector> slice_by_bubble(Block*); Statement* bubble(AtRule*); Statement* bubble(AtRootRule*); Statement* bubble(CssMediaRule*); Statement* bubble(SupportsRule*); Block* debubble(Block* children, Statement* parent = 0); Block* flatten(const Block*); bool bubblable(Statement*); // generic fallback template Statement* fallback(U x) { return Cast(x); } void append_block(Block*, Block*); }; } #endif golibsass-1.0.0/libsass_src/src/dart_helpers.hpp000066400000000000000000000135261405214413600217700ustar00rootroot00000000000000#ifndef SASS_DART_HELPERS_H #define SASS_DART_HELPERS_H #include #include #include #include namespace Sass { // ########################################################################## // Flatten `vector>` to `vector` // ########################################################################## template T flatten(const sass::vector& all) { T flattened; for (const auto& sub : all) { std::copy(std::begin(sub), std::end(sub), std::back_inserter(flattened)); } return flattened; } // ########################################################################## // Expands each element of this Iterable into zero or more elements. // Calls a function on every element and ads all results to flat array // ########################################################################## // Equivalent to dart `cnt.any` // Pass additional closure variables to `fn` template T expand(const T& cnt, U fn, Args... args) { T flattened; for (const auto& sub : cnt) { auto rv = fn(sub, args...); flattened.insert(flattened.end(), rv.begin(), rv.end()); } return flattened; } // ########################################################################## // ########################################################################## template T flattenInner(const sass::vector& vec) { T outer; for (const auto& sub : vec) { outer.emplace_back(std::move(flatten(sub))); } return outer; } // EO flattenInner // ########################################################################## // Equivalent to dart `cnt.any` // Pass additional closure variables to `fn` // ########################################################################## template bool hasAny(const T& cnt, U fn, Args... args) { for (const auto& sub : cnt) { if (fn(sub, args...)) { return true; } } return false; } // EO hasAny // ########################################################################## // Equivalent to dart `cnt.take(len).any` // Pass additional closure variables to `fn` // ########################################################################## template bool hasSubAny(const T& cnt, size_t len, U fn, Args... args) { for (size_t i = 0; i < len; i++) { if (fn(cnt[i], args...)) { return true; } } return false; } // ########################################################################## // Default predicate for lcs algorithm // ########################################################################## template inline bool lcsIdentityCmp(const T& X, const T& Y, T& result) { // Assert equality if (!ObjEqualityFn(X, Y)) { return false; } // Store in reference result = X; // Return success return true; } // EO lcsIdentityCmp // ########################################################################## // Longest common subsequence with predicate // ########################################################################## template sass::vector lcs( const sass::vector& X, const sass::vector& Y, bool(*select)(const T&, const T&, T&) = lcsIdentityCmp) { std::size_t m = X.size(), mm = X.size() + 1; std::size_t n = Y.size(), nn = Y.size() + 1; if (m == 0) return {}; if (n == 0) return {}; // MSVC does not support variable-length arrays // To circumvent, allocate one array on the heap // Then use a macro to access via double index // e.g. `size_t L[m][n]` is supported by gcc size_t* len = new size_t[mm * nn + 1]; bool* acc = new bool[mm * nn + 1]; T* res = new T[mm * nn + 1]; #define LEN(x, y) len[(x) * nn + (y)] #define ACC(x, y) acc[(x) * nn + (y)] #define RES(x, y) res[(x) * nn + (y)] /* Following steps build L[m+1][n+1] in bottom up fashion. Note that L[i][j] contains length of LCS of X[0..i-1] and Y[0..j-1] */ for (size_t i = 0; i <= m; i++) { for (size_t j = 0; j <= n; j++) { if (i == 0 || j == 0) LEN(i, j) = 0; else { ACC(i - 1, j - 1) = select(X[i - 1], Y[j - 1], RES(i - 1, j - 1)); if (ACC(i - 1, j - 1)) LEN(i, j) = LEN(i - 1, j - 1) + 1; else LEN(i, j) = std::max(LEN(i - 1, j), LEN(i, j - 1)); } } } // Following code is used to print LCS sass::vector lcs; std::size_t index = LEN(m, n); lcs.reserve(index); // Start from the right-most-bottom-most corner // and one by one store objects in lcs[] std::size_t i = m, j = n; while (i > 0 && j > 0) { // If current objects in X[] and Y are same, // then current object is part of LCS if (ACC(i - 1, j - 1)) { // Put the stored object in result // Note: we push instead of unshift // Note: reverse the vector later // ToDo: is deque more performant? lcs.push_back(RES(i - 1, j - 1)); // reduce values of i, j and index i -= 1; j -= 1; index -= 1; } // If not same, then find the larger of two and // go in the direction of larger value else if (LEN(i - 1, j) > LEN(i, j - 1)) { i--; } else { j--; } } // reverse now as we used push_back std::reverse(lcs.begin(), lcs.end()); // Delete temp memory on heap delete[] len; delete[] acc; delete[] res; #undef LEN #undef ACC #undef RES return lcs; } // EO lcs // ########################################################################## // ########################################################################## } #endif golibsass-1.0.0/libsass_src/src/debug.hpp000066400000000000000000000014561405214413600204010ustar00rootroot00000000000000#ifndef SASS_DEBUG_H #define SASS_DEBUG_H #include #ifndef UINT32_MAX #define UINT32_MAX 0xffffffffU #endif enum dbg_lvl_t : uint32_t { NONE = 0, TRIM = 1, CHUNKS = 2, SUBWEAVE = 4, WEAVE = 8, EXTEND_COMPOUND = 16, EXTEND_COMPLEX = 32, LCS = 64, EXTEND_OBJECT = 128, ALL = UINT32_MAX }; #ifdef DEBUG #ifndef DEBUG_LVL const uint32_t debug_lvl = UINT32_MAX; #else const uint32_t debug_lvl = (DEBUG_LVL); #endif // DEBUG_LVL #define DEBUG_PRINT(lvl, x) if((lvl) & debug_lvl) { std::cerr << x; } #define DEBUG_PRINTLN(lvl, x) if((lvl) & debug_lvl) { std::cerr << x << std::endl; } #define DEBUG_EXEC(lvl, x) if((lvl) & debug_lvl) { x; } #else // DEBUG #define DEBUG_PRINT(lvl, x) #define DEBUG_PRINTLN(lvl, x) #define DEBUG_EXEC(lvl, x) #endif // DEBUG #endif // SASS_DEBUG golibsass-1.0.0/libsass_src/src/debugger.hpp000066400000000000000000001202761405214413600211010ustar00rootroot00000000000000#ifndef SASS_DEBUGGER_H #define SASS_DEBUGGER_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include "ast.hpp" #include "ast_fwd_decl.hpp" #include "extension.hpp" #include "ordered_map.hpp" using namespace Sass; inline void debug_ast(AST_Node* node, sass::string ind = "", Env* env = 0); inline sass::string debug_vec(const AST_Node* node) { if (node == NULL) return "null"; else return node->to_string(); } inline sass::string debug_dude(sass::vector> vec) { sass::sstream out; out << "{"; bool joinOut = false; for (auto ct : vec) { if (joinOut) out << ", "; joinOut = true; out << "{"; bool joinIn = false; for (auto nr : ct) { if (joinIn) out << ", "; joinIn = true; out << nr; } out << "}"; } out << "}"; return out.str(); } inline sass::string debug_vec(sass::string& str) { return str; } inline sass::string debug_vec(Extension& ext) { sass::sstream out; out << debug_vec(ext.extender); out << " {@extend "; out << debug_vec(ext.target); if (ext.isOptional) { out << " !optional"; } out << "}"; return out.str(); } template inline sass::string debug_vec(sass::vector vec) { sass::sstream out; out << "["; for (size_t i = 0; i < vec.size(); i += 1) { if (i > 0) out << ", "; out << debug_vec(vec[i]); } out << "]"; return out.str(); } template inline sass::string debug_vec(std::queue vec) { sass::sstream out; out << "{"; for (size_t i = 0; i < vec.size(); i += 1) { if (i > 0) out << ", "; out << debug_vec(vec[i]); } out << "}"; return out.str(); } template inline sass::string debug_vec(std::map vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(it->first) // string (key) << ": " << debug_vec(it->second); // string's value joinit = true; } out << "}"; return out.str(); } template inline sass::string debug_vec(const ordered_map& vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(*it); // string (key) // << debug_vec(it->second); // string's value joinit = true; } out << "}"; return out.str(); } template inline sass::string debug_vec(std::unordered_map vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(it->first) // string (key) << ": " << debug_vec(it->second); // string's value joinit = true; } out << "}"; return out.str(); } template inline sass::string debug_keys(std::unordered_map vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(it->first); // string (key) joinit = true; } out << "}"; return out.str(); } inline sass::string debug_vec(ExtListSelSet& vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(*it); // string (key) joinit = true; } out << "}"; return out.str(); } /* template inline sass::string debug_values(tsl::ordered_map vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(const_cast(it->second)); // string's value joinit = true; } out << "}"; return out.str(); } template inline sass::string debug_vec(tsl::ordered_map vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(it->first) // string (key) << ": " << debug_vec(const_cast(it->second)); // string's value joinit = true; } out << "}"; return out.str(); } template inline sass::string debug_vals(tsl::ordered_map vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(const_cast(it->second)); // string's value joinit = true; } out << "}"; return out.str(); } template inline sass::string debug_keys(tsl::ordered_map vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto it = vec.begin(); it != vec.end(); it++) { if (joinit) out << ", "; out << debug_vec(it->first); joinit = true; } out << "}"; return out.str(); } */ template inline sass::string debug_vec(std::set vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto item : vec) { if (joinit) out << ", "; out << debug_vec(item); joinit = true; } out << "}"; return out.str(); } /* template inline sass::string debug_vec(tsl::ordered_set vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto item : vec) { if (joinit) out << ", "; out << debug_vec(item); joinit = true; } out << "}"; return out.str(); } */ template inline sass::string debug_vec(std::unordered_set vec) { sass::sstream out; out << "{"; bool joinit = false; for (auto item : vec) { if (joinit) out << ", "; out << debug_vec(item); joinit = true; } out << "}"; return out.str(); } inline sass::string debug_bool(bool val) { return val ? "true" : "false"; } inline sass::string debug_vec(ExtSmplSelSet* node) { if (node == NULL) return "null"; else return debug_vec(*node); } inline void debug_ast(const AST_Node* node, sass::string ind = "", Env* env = 0) { debug_ast(const_cast(node), ind, env); } inline sass::string str_replace(sass::string str, const sass::string& oldStr, const sass::string& newStr) { size_t pos = 0; while((pos = str.find(oldStr, pos)) != sass::string::npos) { str.replace(pos, oldStr.length(), newStr); pos += newStr.length(); } return str; } inline sass::string prettyprint(const sass::string& str) { sass::string clean = str_replace(str, "\n", "\\n"); clean = str_replace(clean, " ", "\\t"); clean = str_replace(clean, "\r", "\\r"); return clean; } inline sass::string longToHex(long long t) { sass::sstream is; is << std::hex << t; return is.str(); } inline sass::string pstate_source_position(AST_Node* node) { sass::sstream str; Offset start(node->pstate().position); Offset end(start + node->pstate().offset); size_t file = node->pstate().getSrcId(); str << (file == sass::string::npos ? 99999999 : file) << "@[" << start.line << ":" << start.column << "]" << "-[" << end.line << ":" << end.column << "]"; #ifdef DEBUG_SHARED_PTR str << "x" << node->getRefCount() << "" << " " << node->getDbgFile() << "@" << node->getDbgLine(); #endif return str.str(); } inline void debug_ast(AST_Node* node, sass::string ind, Env* env) { if (node == 0) return; if (ind == "") std::cerr << "####################################################################\n"; if (Cast(node)) { Bubble* bubble = Cast(node); std::cerr << ind << "Bubble " << bubble; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << bubble->tabs(); std::cerr << std::endl; debug_ast(bubble->node(), ind + " ", env); } else if (Cast(node)) { Trace* trace = Cast(node); std::cerr << ind << "Trace " << trace; std::cerr << " (" << pstate_source_position(node) << ")" << " [name:" << trace->name() << ", type: " << trace->type() << "]" << std::endl; debug_ast(trace->block(), ind + " ", env); } else if (Cast(node)) { AtRootRule* root_block = Cast(node); std::cerr << ind << "AtRootRule " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << root_block->tabs(); std::cerr << std::endl; debug_ast(root_block->expression(), ind + ":", env); debug_ast(root_block->block(), ind + " ", env); } else if (Cast(node)) { SelectorList* selector = Cast(node); std::cerr << ind << "SelectorList " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -"); std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -"); std::cerr << (selector->has_real_parent_ref() ? " [real-parent]": " -"); std::cerr << std::endl; for(const ComplexSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { ComplexSelector* selector = Cast(node); std::cerr << ind << "ComplexSelector " << selector << " (" << pstate_source_position(node) << ")" << " <" << selector->hash() << ">" << " [" << (selector->chroots() ? "CHROOT" : "CONNECT") << "]" << " [length:" << longToHex(selector->length()) << "]" << " [weight:" << longToHex(selector->specificity()) << "]" << (selector->is_invisible() ? " [is_invisible]" : " -") << (selector->isInvisible() ? " [isInvisible]" : " -") << (selector->hasPreLineFeed() ? " [hasPreLineFeed]" : " -") // << (selector->is_invisible() ? " [INVISIBLE]": " -") // << (selector->has_placeholder() ? " [PLACEHOLDER]": " -") // << (selector->is_optional() ? " [is_optional]": " -") << (selector->has_real_parent_ref() ? " [real parent]": " -") // << (selector->has_line_feed() ? " [line-feed]": " -") // << (selector->has_line_break() ? " [line-break]": " -") << " -- \n"; for(const SelectorComponentObj& i : selector->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { SelectorCombinator* selector = Cast(node); std::cerr << ind << "SelectorCombinator " << selector << " (" << pstate_source_position(node) << ")" << " <" << selector->hash() << ">" << " [weight:" << longToHex(selector->specificity()) << "]" << (selector->has_real_parent_ref() ? " [real parent]": " -") << " -- "; sass::string del; switch (selector->combinator()) { case SelectorCombinator::CHILD: del = ">"; break; case SelectorCombinator::GENERAL: del = "~"; break; case SelectorCombinator::ADJACENT: del = "+"; break; } std::cerr << "[" << del << "]" << "\n"; } else if (Cast(node)) { CompoundSelector* selector = Cast(node); std::cerr << ind << "CompoundSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << (selector->hasRealParent() ? " [REAL PARENT]" : "") << ">"; std::cerr << " [weight:" << longToHex(selector->specificity()) << "]"; std::cerr << (selector->hasPostLineBreak() ? " [hasPostLineBreak]" : " -"); std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -"); std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -"); std::cerr << "\n"; for(const SimpleSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Parent_Reference* selector = Cast(node); std::cerr << ind << "Parent_Reference " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << std::endl; } else if (Cast(node)) { PseudoSelector* selector = Cast(node); std::cerr << ind << "PseudoSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; std::cerr << (selector->isClass() ? " [isClass]": " -"); std::cerr << (selector->isSyntacticClass() ? " [isSyntacticClass]": " -"); std::cerr << std::endl; debug_ast(selector->argument(), ind + " <= ", env); debug_ast(selector->selector(), ind + " || ", env); } else if (Cast(node)) { AttributeSelector* selector = Cast(node); std::cerr << ind << "AttributeSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; std::cerr << std::endl; debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env); } else if (Cast(node)) { ClassSelector* selector = Cast(node); std::cerr << ind << "ClassSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; std::cerr << std::endl; } else if (Cast(node)) { IDSelector* selector = Cast(node); std::cerr << ind << "IDSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; std::cerr << std::endl; } else if (Cast(node)) { TypeSelector* selector = Cast(node); std::cerr << ind << "TypeSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; std::cerr << std::endl; } else if (Cast(node)) { PlaceholderSelector* selector = Cast(node); std::cerr << ind << "PlaceholderSelector [" << selector->ns_name() << "] " << selector; std::cerr << " (" << pstate_source_position(selector) << ")" << " <" << selector->hash() << ">" << (selector->isInvisible() ? " [isInvisible]" : " -") << std::endl; } else if (Cast(node)) { SimpleSelector* selector = Cast(node); std::cerr << ind << "SimpleSelector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; } else if (Cast(node)) { Selector_Schema* selector = Cast(node); std::cerr << ind << "Selector_Schema " << selector; std::cerr << " (" << pstate_source_position(node) << ")" << (selector->connect_parent() ? " [connect-parent]": " -") << std::endl; debug_ast(selector->contents(), ind + " "); // for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Selector* selector = Cast(node); std::cerr << ind << "Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; } else if (Cast(node)) { Media_Query_Expression* block = Cast(node); std::cerr << ind << "Media_Query_Expression " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -") << std::endl; debug_ast(block->feature(), ind + " feature) "); debug_ast(block->value(), ind + " value) "); } else if (Cast(node)) { Media_Query* block = Cast(node); std::cerr << ind << "Media_Query " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_negated() ? " [is_negated]": " -") << (block->is_restricted() ? " [is_restricted]": " -") << std::endl; debug_ast(block->media_type(), ind + " "); for(const auto& i : block->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { MediaRule* rule = Cast(node); std::cerr << ind << "MediaRule " << rule; std::cerr << " (" << pstate_source_position(rule) << ")"; std::cerr << " " << rule->tabs() << std::endl; debug_ast(rule->schema(), ind + " =@ "); debug_ast(rule->block(), ind + " "); } else if (Cast(node)) { CssMediaRule* rule = Cast(node); std::cerr << ind << "CssMediaRule " << rule; std::cerr << " (" << pstate_source_position(rule) << ")"; std::cerr << " " << rule->tabs() << std::endl; for (auto item : rule->elements()) { debug_ast(item, ind + " == "); } debug_ast(rule->block(), ind + " "); } else if (Cast(node)) { CssMediaQuery* query = Cast(node); std::cerr << ind << "CssMediaQuery " << query; std::cerr << " (" << pstate_source_position(query) << ")"; std::cerr << " [" << (query->modifier()) << "] "; std::cerr << " [" << (query->type()) << "] "; std::cerr << " " << debug_vec(query->features()); std::cerr << std::endl; } else if (Cast(node)) { SupportsRule* block = Cast(node); std::cerr << ind << "SupportsRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->condition(), ind + " =@ "); debug_ast(block->block(), ind + " <>"); } else if (Cast(node)) { SupportsOperation* block = Cast(node); std::cerr << ind << "SupportsOperation " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->left(), ind + " left) "); debug_ast(block->right(), ind + " right) "); } else if (Cast(node)) { SupportsNegation* block = Cast(node); std::cerr << ind << "SupportsNegation " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->condition(), ind + " condition) "); } else if (Cast(node)) { At_Root_Query* block = Cast(node); std::cerr << ind << "At_Root_Query " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->feature(), ind + " feature) "); debug_ast(block->value(), ind + " value) "); } else if (Cast(node)) { SupportsDeclaration* block = Cast(node); std::cerr << ind << "SupportsDeclaration " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->feature(), ind + " feature) "); debug_ast(block->value(), ind + " value) "); } else if (Cast(node)) { Block* root_block = Cast(node); std::cerr << ind << "Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; if (root_block->is_root()) std::cerr << " [root]"; if (root_block->isInvisible()) std::cerr << " [isInvisible]"; std::cerr << " " << root_block->tabs() << std::endl; for(const Statement_Obj& i : root_block->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { WarningRule* block = Cast(node); std::cerr << ind << "WarningRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->message(), ind + " : "); } else if (Cast(node)) { ErrorRule* block = Cast(node); std::cerr << ind << "ErrorRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; } else if (Cast(node)) { DebugRule* block = Cast(node); std::cerr << ind << "DebugRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->value(), ind + " "); } else if (Cast(node)) { Comment* block = Cast(node); std::cerr << ind << "Comment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->text(), ind + "// ", env); } else if (Cast(node)) { If* block = Cast(node); std::cerr << ind << "If " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->predicate(), ind + " = "); debug_ast(block->block(), ind + " <>"); debug_ast(block->alternative(), ind + " ><"); } else if (Cast(node)) { Return* block = Cast(node); std::cerr << ind << "Return " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs(); std::cerr << " [" << block->value()->to_string() << "]" << std::endl; } else if (Cast(node)) { ExtendRule* block = Cast(node); std::cerr << ind << "ExtendRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->selector(), ind + "-> ", env); } else if (Cast(node)) { Content* block = Cast(node); std::cerr << ind << "Content " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->arguments(), ind + " args: ", env); } else if (Cast(node)) { Import_Stub* block = Cast(node); std::cerr << ind << "Import_Stub " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->imp_path() << "] "; std::cerr << " " << block->tabs() << std::endl; } else if (Cast(node)) { Import* block = Cast(node); std::cerr << ind << "Import " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; // sass::vector files_; for (auto imp : block->urls()) debug_ast(imp, ind + "@: ", env); debug_ast(block->import_queries(), ind + "@@ "); } else if (Cast(node)) { Assignment* block = Cast(node); std::cerr << ind << "Assignment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <<" << block->variable() << ">> " << block->tabs() << std::endl; debug_ast(block->value(), ind + "=", env); } else if (Cast(node)) { Declaration* block = Cast(node); std::cerr << ind << "Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [is_custom_property: " << block->is_custom_property() << "] "; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->property(), ind + " prop: ", env); debug_ast(block->value(), ind + " value: ", env); debug_ast(block->block(), ind + " ", env); } else if (Cast(node)) { Keyframe_Rule* ParentStatement = Cast(node); std::cerr << ind << "Keyframe_Rule " << ParentStatement; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << ParentStatement->tabs() << std::endl; if (ParentStatement->name()) debug_ast(ParentStatement->name(), ind + "@"); if (ParentStatement->block()) for(const Statement_Obj& i : ParentStatement->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { AtRule* block = Cast(node); std::cerr << ind << "AtRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl; debug_ast(block->selector(), ind + "~", env); debug_ast(block->value(), ind + "+", env); if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { EachRule* block = Cast(node); std::cerr << ind << "EachRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { ForRule* block = Cast(node); std::cerr << ind << "ForRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { WhileRule* block = Cast(node); std::cerr << ind << "WhileRule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Definition* block = Cast(node); std::cerr << ind << "Definition " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << block->name() << "] "; std::cerr << " [type: " << (block->type() == Sass::Definition::Type::MIXIN ? "Mixin " : "Function ") << "] "; // this seems to lead to segfaults some times? // std::cerr << " [signature: " << block->signature() << "] "; std::cerr << " [native: " << block->native_function() << "] "; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->parameters(), ind + " params: ", env); if (block->block()) debug_ast(block->block(), ind + " ", env); } else if (Cast(node)) { Mixin_Call* block = Cast(node); std::cerr << ind << "Mixin_Call " << block << " " << block->tabs(); std::cerr << " (" << pstate_source_position(block) << ")"; std::cerr << " [" << block->name() << "]"; std::cerr << " [has_content: " << block->has_content() << "] " << std::endl; debug_ast(block->arguments(), ind + " args: ", env); debug_ast(block->block_parameters(), ind + " block_params: ", env); if (block->block()) debug_ast(block->block(), ind + " ", env); } else if (StyleRule* ruleset = Cast(node)) { std::cerr << ind << "StyleRule " << ruleset; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [indent: " << ruleset->tabs() << "]"; std::cerr << (ruleset->is_invisible() ? " [INVISIBLE]" : ""); std::cerr << (ruleset->is_root() ? " [root]" : ""); std::cerr << std::endl; debug_ast(ruleset->selector(), ind + ">"); debug_ast(ruleset->block(), ind + " "); } else if (Cast(node)) { Block* block = Cast(node); std::cerr << ind << "Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_invisible() ? " [INVISIBLE]" : ""); std::cerr << " [indent: " << block->tabs() << "]" << std::endl; for(const Statement_Obj& i : block->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Variable* expression = Cast(node); std::cerr << ind << "Variable " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]" << std::endl; sass::string name(expression->name()); if (env && env->has(name)) debug_ast(Cast((*env)[name]), ind + " -> ", env); } else if (Cast(node)) { Function_Call* expression = Cast(node); std::cerr << ind << "Function_Call " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; if (expression->is_css()) std::cerr << " [css]"; std::cerr << std::endl; debug_ast(expression->arguments(), ind + " args: ", env); debug_ast(expression->func(), ind + " func: ", env); } else if (Cast(node)) { Function* expression = Cast(node); std::cerr << ind << "Function " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->is_css()) std::cerr << " [css]"; std::cerr << std::endl; debug_ast(expression->definition(), ind + " definition: ", env); } else if (Cast(node)) { Arguments* expression = Cast(node); std::cerr << ind << "Arguments " << expression; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->has_named_arguments()) std::cerr << " [has_named_arguments]"; if (expression->has_rest_argument()) std::cerr << " [has_rest_argument]"; if (expression->has_keyword_argument()) std::cerr << " [has_keyword_argument]"; std::cerr << std::endl; for(const Argument_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Argument* expression = Cast(node); std::cerr << ind << "Argument " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->value().ptr() << "]"; std::cerr << " [name: " << expression->name() << "] "; std::cerr << " [rest: " << expression->is_rest_argument() << "] "; std::cerr << " [keyword: " << expression->is_keyword_argument() << "] " << std::endl; debug_ast(expression->value(), ind + " value: ", env); } else if (Cast(node)) { Parameters* expression = Cast(node); std::cerr << ind << "Parameters " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [has_optional: " << expression->has_optional_parameters() << "] "; std::cerr << " [has_rest: " << expression->has_rest_parameter() << "] "; std::cerr << std::endl; for(const Parameter_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Parameter* expression = Cast(node); std::cerr << ind << "Parameter " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << expression->name() << "] "; std::cerr << " [default: " << expression->default_value().ptr() << "] "; std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl; } else if (Cast(node)) { Unary_Expression* expression = Cast(node); std::cerr << ind << "Unary_Expression " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type() << "]" << std::endl; debug_ast(expression->operand(), ind + " operand: ", env); } else if (Cast(node)) { Binary_Expression* expression = Cast(node); std::cerr << ind << "Binary_Expression " << expression; if (expression->is_interpolant()) std::cerr << " [is interpolant] "; if (expression->is_left_interpolant()) std::cerr << " [left interpolant] "; if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [ws_before: " << expression->op().ws_before << "] "; std::cerr << " [ws_after: " << expression->op().ws_after << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type_name() << "]" << std::endl; debug_ast(expression->left(), ind + " left: ", env); debug_ast(expression->right(), ind + " right: ", env); } else if (Cast(node)) { Map* expression = Cast(node); std::cerr << ind << "Map " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [Hashed]" << std::endl; for (const auto& i : expression->elements()) { debug_ast(i.first, ind + " key: "); debug_ast(i.second, ind + " val: "); } } else if (Cast(node)) { List* expression = Cast(node); std::cerr << ind << "List " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << expression->length() << ") " << (expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map " : "Space ") << " [delayed: " << expression->is_delayed() << "] " << " [interpolant: " << expression->is_interpolant() << "] " << " [listized: " << expression->from_selector() << "] " << " [arglist: " << expression->is_arglist() << "] " << " [bracketed: " << expression->is_bracketed() << "] " << " [expanded: " << expression->is_expanded() << "] " << " [hash: " << expression->hash() << "] " << std::endl; for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Boolean* expression = Cast(node); std::cerr << ind << "Boolean " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << "]" << std::endl; } else if (Cast(node)) { Color_RGBA* expression = Cast(node); std::cerr << ind << "Color " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << expression->disp() << "] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " rgba[" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl; } else if (Cast(node)) { Color_HSLA* expression = Cast(node); std::cerr << ind << "Color " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << expression->disp() << "] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " hsla[" << expression->h() << ":" << expression->s() << ":" << expression->l() << "@" << expression->a() << "]" << std::endl; } else if (Cast(node)) { Number* expression = Cast(node); std::cerr << ind << "Number " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << expression->unit() << "]" << " [hash: " << expression->hash() << "] " << std::endl; } else if (Cast(node)) { Null* expression = Cast(node); std::cerr << ind << "Null " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] " // " [hash: " << expression->hash() << "] " << std::endl; } else if (Cast(node)) { String_Quoted* expression = Cast(node); std::cerr << ind << "String_Quoted " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]"; std::cerr << std::endl; } else if (Cast(node)) { String_Constant* expression = Cast(node); std::cerr << ind << "String_Constant " << expression; if (expression->concrete_type()) { std::cerr << " " << expression->concrete_type(); } std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << std::endl; } else if (Cast(node)) { String_Schema* expression = Cast(node); std::cerr << ind << "String_Schema " << expression; std::cerr << " (" << pstate_source_position(expression) << ")"; std::cerr << " " << expression->concrete_type(); std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->css()) std::cerr << " [css]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [is interpolant]"; if (expression->has_interpolant()) std::cerr << " [has interpolant]"; if (expression->is_left_interpolant()) std::cerr << " [left interpolant] "; if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << std::endl; for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { String* expression = Cast(node); std::cerr << ind << "String " << expression; std::cerr << " " << expression->concrete_type(); std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << std::endl; } else if (Cast(node)) { Expression* expression = Cast(node); std::cerr << ind << "Expression " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; switch (expression->concrete_type()) { case Expression::Type::NONE: std::cerr << " [NONE]"; break; case Expression::Type::BOOLEAN: std::cerr << " [BOOLEAN]"; break; case Expression::Type::NUMBER: std::cerr << " [NUMBER]"; break; case Expression::Type::COLOR: std::cerr << " [COLOR]"; break; case Expression::Type::STRING: std::cerr << " [STRING]"; break; case Expression::Type::LIST: std::cerr << " [LIST]"; break; case Expression::Type::MAP: std::cerr << " [MAP]"; break; case Expression::Type::SELECTOR: std::cerr << " [SELECTOR]"; break; case Expression::Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break; case Expression::Type::C_WARNING: std::cerr << " [C_WARNING]"; break; case Expression::Type::C_ERROR: std::cerr << " [C_ERROR]"; break; case Expression::Type::FUNCTION: std::cerr << " [FUNCTION]"; break; case Expression::Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break; case Expression::Type::VARIABLE: std::cerr << " [VARIABLE]"; break; case Expression::Type::FUNCTION_VAL: std::cerr << " [FUNCTION_VAL]"; break; case Expression::Type::PARENT: std::cerr << " [PARENT]"; break; } std::cerr << std::endl; } else if (Cast(node)) { ParentStatement* parent = Cast(node); std::cerr << ind << "ParentStatement " << parent; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << parent->tabs() << std::endl; if (parent->block()) for(const Statement_Obj& i : parent->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { Statement* statement = Cast(node); std::cerr << ind << "Statement " << statement; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << statement->tabs() << std::endl; } if (ind == "") std::cerr << "####################################################################\n"; } /* inline void debug_ast(const AST_Node* node, sass::string ind = "", Env* env = 0) { debug_ast(const_cast(node), ind, env); } */ #endif // SASS_DEBUGGER golibsass-1.0.0/libsass_src/src/emitter.cpp000066400000000000000000000162601405214413600207560ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "emitter.hpp" #include "util_string.hpp" #include "util.hpp" namespace Sass { Emitter::Emitter(struct Sass_Output_Options& opt) : wbuf(), opt(opt), indentation(0), scheduled_space(0), scheduled_linefeed(0), scheduled_delimiter(false), scheduled_crutch(0), scheduled_mapping(0), in_custom_property(false), in_comment(false), in_wrapped(false), in_media_block(false), in_declaration(false), in_space_array(false), in_comma_array(false) { } // return buffer as string sass::string Emitter::get_buffer(void) { return wbuf.buffer; } Sass_Output_Style Emitter::output_style(void) const { return opt.output_style; } // PROXY METHODS FOR SOURCE MAPS void Emitter::add_source_index(size_t idx) { wbuf.smap.source_index.push_back(idx); } sass::string Emitter::render_srcmap(Context &ctx) { return wbuf.smap.render_srcmap(ctx); } void Emitter::set_filename(const sass::string& str) { wbuf.smap.file = str; } void Emitter::schedule_mapping(const AST_Node* node) { scheduled_mapping = node; } void Emitter::add_open_mapping(const AST_Node* node) { wbuf.smap.add_open_mapping(node); } void Emitter::add_close_mapping(const AST_Node* node) { wbuf.smap.add_close_mapping(node); } SourceSpan Emitter::remap(const SourceSpan& pstate) { return wbuf.smap.remap(pstate); } // MAIN BUFFER MANIPULATION // add outstanding delimiter void Emitter::finalize(bool final) { scheduled_space = 0; if (output_style() == SASS_STYLE_COMPRESSED) if (final) scheduled_delimiter = false; if (scheduled_linefeed) scheduled_linefeed = 1; flush_schedules(); } // flush scheduled space/linefeed void Emitter::flush_schedules(void) { // check the schedule if (scheduled_linefeed) { sass::string linefeeds = ""; for (size_t i = 0; i < scheduled_linefeed; i++) linefeeds += opt.linefeed; scheduled_space = 0; scheduled_linefeed = 0; append_string(linefeeds); } else if (scheduled_space) { sass::string spaces(scheduled_space, ' '); scheduled_space = 0; append_string(spaces); } if (scheduled_delimiter) { scheduled_delimiter = false; append_string(";"); } } // prepend some text or token to the buffer void Emitter::prepend_output(const OutputBuffer& output) { wbuf.smap.prepend(output); wbuf.buffer = output.buffer + wbuf.buffer; } // prepend some text or token to the buffer void Emitter::prepend_string(const sass::string& text) { // do not adjust mappings for utf8 bom // seems they are not counted in any UA if (text.compare("\xEF\xBB\xBF") != 0) { wbuf.smap.prepend(Offset(text)); } wbuf.buffer = text + wbuf.buffer; } char Emitter::last_char() { return wbuf.buffer.back(); } // append a single char to the buffer void Emitter::append_char(const char chr) { // write space/lf flush_schedules(); // add to buffer wbuf.buffer += chr; // account for data in source-maps wbuf.smap.append(Offset(chr)); } // append some text or token to the buffer void Emitter::append_string(const sass::string& text) { // write space/lf flush_schedules(); if (in_comment) { sass::string out = Util::normalize_newlines(text); if (output_style() == COMPACT) { out = comment_to_compact_string(out); } wbuf.smap.append(Offset(out)); wbuf.buffer += std::move(out); } else { // add to buffer wbuf.buffer += text; // account for data in source-maps wbuf.smap.append(Offset(text)); } } // append some white-space only text void Emitter::append_wspace(const sass::string& text) { if (text.empty()) return; if (peek_linefeed(text.c_str())) { scheduled_space = 0; append_mandatory_linefeed(); } } // append some text or token to the buffer // this adds source-mappings for node start and end void Emitter::append_token(const sass::string& text, const AST_Node* node) { flush_schedules(); add_open_mapping(node); // hotfix for browser issues // this is pretty ugly indeed if (scheduled_crutch) { add_open_mapping(scheduled_crutch); scheduled_crutch = 0; } append_string(text); add_close_mapping(node); } // HELPER METHODS void Emitter::append_indentation() { if (output_style() == COMPRESSED) return; if (output_style() == COMPACT) return; if (in_declaration && in_comma_array) return; if (scheduled_linefeed && indentation) scheduled_linefeed = 1; sass::string indent = ""; for (size_t i = 0; i < indentation; i++) indent += opt.indent; append_string(indent); } void Emitter::append_delimiter() { scheduled_delimiter = true; if (output_style() == COMPACT) { if (indentation == 0) { append_mandatory_linefeed(); } else { append_mandatory_space(); } } else if (output_style() != COMPRESSED) { append_optional_linefeed(); } } void Emitter::append_comma_separator() { // scheduled_space = 0; append_string(","); append_optional_space(); } void Emitter::append_colon_separator() { scheduled_space = 0; append_string(":"); if (!in_custom_property) append_optional_space(); } void Emitter::append_mandatory_space() { scheduled_space = 1; } void Emitter::append_optional_space() { if ((output_style() != COMPRESSED) && buffer().size()) { unsigned char lst = buffer().at(buffer().length() - 1); if (!isspace(lst) || scheduled_delimiter) { if (last_char() != '(') { append_mandatory_space(); } } } } void Emitter::append_special_linefeed() { if (output_style() == COMPACT) { append_mandatory_linefeed(); for (size_t p = 0; p < indentation; p++) append_string(opt.indent); } } void Emitter::append_optional_linefeed() { if (in_declaration && in_comma_array) return; if (output_style() == COMPACT) { append_mandatory_space(); } else { append_mandatory_linefeed(); } } void Emitter::append_mandatory_linefeed() { if (output_style() != COMPRESSED) { scheduled_linefeed = 1; scheduled_space = 0; // flush_schedules(); } } void Emitter::append_scope_opener(AST_Node* node) { scheduled_linefeed = 0; append_optional_space(); flush_schedules(); if (node) add_open_mapping(node); append_string("{"); append_optional_linefeed(); // append_optional_space(); ++ indentation; } void Emitter::append_scope_closer(AST_Node* node) { -- indentation; scheduled_linefeed = 0; if (output_style() == COMPRESSED) scheduled_delimiter = false; if (output_style() == EXPANDED) { append_optional_linefeed(); append_indentation(); } else { append_optional_space(); } append_string("}"); if (node) add_close_mapping(node); append_optional_linefeed(); if (indentation != 0) return; if (output_style() != COMPRESSED) scheduled_linefeed = 2; } } golibsass-1.0.0/libsass_src/src/emitter.hpp000066400000000000000000000062531405214413600207640ustar00rootroot00000000000000#ifndef SASS_EMITTER_H #define SASS_EMITTER_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "sass/base.h" #include "source_map.hpp" #include "ast_fwd_decl.hpp" namespace Sass { class Context; class Emitter { public: Emitter(struct Sass_Output_Options& opt); virtual ~Emitter() { } protected: OutputBuffer wbuf; public: const sass::string& buffer(void) { return wbuf.buffer; } const SourceMap smap(void) { return wbuf.smap; } const OutputBuffer output(void) { return wbuf; } // proxy methods for source maps void add_source_index(size_t idx); void set_filename(const sass::string& str); void add_open_mapping(const AST_Node* node); void add_close_mapping(const AST_Node* node); void schedule_mapping(const AST_Node* node); sass::string render_srcmap(Context &ctx); SourceSpan remap(const SourceSpan& pstate); public: struct Sass_Output_Options& opt; size_t indentation; size_t scheduled_space; size_t scheduled_linefeed; bool scheduled_delimiter; const AST_Node* scheduled_crutch; const AST_Node* scheduled_mapping; public: // output strings different in custom css properties bool in_custom_property; // output strings different in comments bool in_comment; // selector list does not get linefeeds bool in_wrapped; // lists always get a space after delimiter bool in_media_block; // nested list must not have parentheses bool in_declaration; // nested lists need parentheses bool in_space_array; bool in_comma_array; public: // return buffer as sass::string sass::string get_buffer(void); // flush scheduled space/linefeed Sass_Output_Style output_style(void) const; // add outstanding linefeed void finalize(bool final = true); // flush scheduled space/linefeed void flush_schedules(void); // prepend some text or token to the buffer void prepend_string(const sass::string& text); void prepend_output(const OutputBuffer& out); // append some text or token to the buffer void append_string(const sass::string& text); // append a single character to buffer void append_char(const char chr); // append some white-space only text void append_wspace(const sass::string& text); // append some text or token to the buffer // this adds source-mappings for node start and end void append_token(const sass::string& text, const AST_Node* node); // query last appended character char last_char(); public: // syntax sugar void append_indentation(); void append_optional_space(void); void append_mandatory_space(void); void append_special_linefeed(void); void append_optional_linefeed(void); void append_mandatory_linefeed(void); void append_scope_opener(AST_Node* node = 0); void append_scope_closer(AST_Node* node = 0); void append_comma_separator(void); void append_colon_separator(void); void append_delimiter(void); }; } #endif golibsass-1.0.0/libsass_src/src/environment.cpp000066400000000000000000000154061405214413600216520ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "environment.hpp" namespace Sass { template Environment::Environment(bool is_shadow) : local_frame_(environment_map()), parent_(0), is_shadow_(false) { } template Environment::Environment(Environment* env, bool is_shadow) : local_frame_(environment_map()), parent_(env), is_shadow_(is_shadow) { } template Environment::Environment(Environment& env, bool is_shadow) : local_frame_(environment_map()), parent_(&env), is_shadow_(is_shadow) { } // link parent to create a stack template void Environment::link(Environment& env) { parent_ = &env; } template void Environment::link(Environment* env) { parent_ = env; } // this is used to find the global frame // which is the second last on the stack template bool Environment::is_lexical() const { return !! parent_ && parent_->parent_; } // only match the real root scope // there is still a parent around // not sure what it is actually use for // I guess we store functions etc. there template bool Environment::is_global() const { return parent_ && ! parent_->parent_; } template environment_map& Environment::local_frame() { return local_frame_; } template bool Environment::has_local(const sass::string& key) const { return local_frame_.find(key) != local_frame_.end(); } template EnvResult Environment::find_local(const sass::string& key) { auto end = local_frame_.end(); auto it = local_frame_.find(key); return EnvResult(it, it != end); } template T& Environment::get_local(const sass::string& key) { return local_frame_[key]; } template void Environment::set_local(const sass::string& key, const T& val) { local_frame_[key] = val; } template void Environment::set_local(const sass::string& key, T&& val) { local_frame_[key] = val; } template void Environment::del_local(const sass::string& key) { local_frame_.erase(key); } template Environment* Environment::global_env() { Environment* cur = this; while (cur->is_lexical()) { cur = cur->parent_; } return cur; } template bool Environment::has_global(const sass::string& key) { return global_env()->has(key); } template T& Environment::get_global(const sass::string& key) { return (*global_env())[key]; } template void Environment::set_global(const sass::string& key, const T& val) { global_env()->local_frame_[key] = val; } template void Environment::set_global(const sass::string& key, T&& val) { global_env()->local_frame_[key] = val; } template void Environment::del_global(const sass::string& key) { global_env()->local_frame_.erase(key); } template Environment* Environment::lexical_env(const sass::string& key) { Environment* cur = this; while (cur) { if (cur->has_local(key)) { return cur; } cur = cur->parent_; } return this; } // see if we have a lexical variable // move down the stack but stop before we // reach the global frame (is not included) template bool Environment::has_lexical(const sass::string& key) const { auto cur = this; while (cur->is_lexical()) { if (cur->has_local(key)) return true; cur = cur->parent_; } return false; } // see if we have a lexical we could update // either update already existing lexical value // or if flag is set, we create one if no lexical found template void Environment::set_lexical(const sass::string& key, const T& val) { Environment* cur = this; bool shadow = false; while ((cur && cur->is_lexical()) || shadow) { EnvResult rv(cur->find_local(key)); if (rv.found) { rv.it->second = val; return; } shadow = cur->is_shadow(); cur = cur->parent_; } set_local(key, val); } // this one moves the value template void Environment::set_lexical(const sass::string& key, T&& val) { Environment* cur = this; bool shadow = false; while ((cur && cur->is_lexical()) || shadow) { EnvResult rv(cur->find_local(key)); if (rv.found) { rv.it->second = val; return; } shadow = cur->is_shadow(); cur = cur->parent_; } set_local(key, val); } // look on the full stack for key // include all scopes available template bool Environment::has(const sass::string& key) const { auto cur = this; while (cur) { if (cur->has_local(key)) { return true; } cur = cur->parent_; } return false; } // look on the full stack for key // include all scopes available template EnvResult Environment::find(const sass::string& key) { auto cur = this; while (true) { EnvResult rv(cur->find_local(key)); if (rv.found) return rv; cur = cur->parent_; if (!cur) return rv; } }; // use array access for getter and setter functions template T& Environment::get(const sass::string& key) { auto cur = this; while (cur) { if (cur->has_local(key)) { return cur->get_local(key); } cur = cur->parent_; } return get_local(key); } // use array access for getter and setter functions template T& Environment::operator[](const sass::string& key) { auto cur = this; while (cur) { if (cur->has_local(key)) { return cur->get_local(key); } cur = cur->parent_; } return get_local(key); } /* #ifdef DEBUG template size_t Environment::print(sass::string prefix) { size_t indent = 0; if (parent_) indent = parent_->print(prefix) + 1; std::cerr << prefix << sass::string(indent, ' ') << "== " << this << std::endl; for (typename environment_map::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) { if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) { std::cerr << prefix << sass::string(indent, ' ') << i->first << " " << i->second; if (Value* val = Cast(i->second)) { std::cerr << " : " << val->to_string(); } std::cerr << std::endl; } } return indent ; } #endif */ // compile implementation for AST_Node template class Environment; } golibsass-1.0.0/libsass_src/src/environment.hpp000066400000000000000000000066671405214413600216700ustar00rootroot00000000000000#ifndef SASS_ENVIRONMENT_H #define SASS_ENVIRONMENT_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp" namespace Sass { // this defeats the whole purpose of environment being templatable!! typedef environment_map::iterator EnvIter; class EnvResult { public: EnvIter it; bool found; public: EnvResult(EnvIter it, bool found) : it(it), found(found) {} }; template class Environment { // TODO: test with map environment_map local_frame_; ADD_PROPERTY(Environment*, parent) ADD_PROPERTY(bool, is_shadow) public: Environment(bool is_shadow = false); Environment(Environment* env, bool is_shadow = false); Environment(Environment& env, bool is_shadow = false); // link parent to create a stack void link(Environment& env); void link(Environment* env); // this is used to find the global frame // which is the second last on the stack bool is_lexical() const; // only match the real root scope // there is still a parent around // not sure what it is actually use for // I guess we store functions etc. there bool is_global() const; // scope operates on the current frame environment_map& local_frame(); bool has_local(const sass::string& key) const; EnvResult find_local(const sass::string& key); T& get_local(const sass::string& key); // set variable on the current frame void set_local(const sass::string& key, const T& val); void set_local(const sass::string& key, T&& val); void del_local(const sass::string& key); // global operates on the global frame // which is the second last on the stack Environment* global_env(); // get the env where the variable already exists // if it does not yet exist, we return current env Environment* lexical_env(const sass::string& key); bool has_global(const sass::string& key); T& get_global(const sass::string& key); // set a variable on the global frame void set_global(const sass::string& key, const T& val); void set_global(const sass::string& key, T&& val); void del_global(const sass::string& key); // see if we have a lexical variable // move down the stack but stop before we // reach the global frame (is not included) bool has_lexical(const sass::string& key) const; // see if we have a lexical we could update // either update already existing lexical value // or we create a new one on the current frame void set_lexical(const sass::string& key, T&& val); void set_lexical(const sass::string& key, const T& val); // look on the full stack for key // include all scopes available bool has(const sass::string& key) const; // look on the full stack for key // include all scopes available T& get(const sass::string& key); // look on the full stack for key // include all scopes available EnvResult find(const sass::string& key); // use array access for getter and setter functions T& operator[](const sass::string& key); #ifdef DEBUG size_t print(sass::string prefix = ""); #endif }; // define typedef for our use case typedef Environment Env; typedef sass::vector EnvStack; } #endif golibsass-1.0.0/libsass_src/src/error_handling.cpp000066400000000000000000000213301405214413600222740ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "prelexer.hpp" #include "backtrace.hpp" #include "error_handling.hpp" #include namespace Sass { namespace Exception { Base::Base(SourceSpan pstate, sass::string msg, Backtraces traces) : std::runtime_error(msg.c_str()), msg(msg), prefix("Error"), pstate(pstate), traces(traces) { } InvalidSass::InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg) : Base(pstate, msg, traces) { } InvalidParent::InvalidParent(Selector* parent, Backtraces traces, Selector* selector) : Base(selector->pstate(), def_msg, traces), parent(parent), selector(selector) { msg = "Invalid parent selector for " "\"" + selector->to_string(Sass_Inspect_Options()) + "\": " "\"" + parent->to_string(Sass_Inspect_Options()) + "\""; } InvalidVarKwdType::InvalidVarKwdType(SourceSpan pstate, Backtraces traces, sass::string name, const Argument* arg) : Base(pstate, def_msg, traces), name(name), arg(arg) { msg = "Variable keyword argument map must have string keys.\n" + name + " is not a string in " + arg->to_string() + "."; } InvalidArgumentType::InvalidArgumentType(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string type, const Value* value) : Base(pstate, def_msg, traces), fn(fn), arg(arg), type(type), value(value) { msg = arg + ": \""; if (value) msg += value->to_string(Sass_Inspect_Options()); msg += "\" is not a " + type + " for `" + fn + "'"; } MissingArgument::MissingArgument(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string fntype) : Base(pstate, def_msg, traces), fn(fn), arg(arg), fntype(fntype) { msg = fntype + " " + fn + " is missing argument " + arg + "."; } InvalidSyntax::InvalidSyntax(SourceSpan pstate, Backtraces traces, sass::string msg) : Base(pstate, msg, traces) { } NestingLimitError::NestingLimitError(SourceSpan pstate, Backtraces traces, sass::string msg) : Base(pstate, msg, traces) { } DuplicateKeyError::DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org) : Base(org.pstate(), def_msg, traces), dup(dup), org(org) { msg = "Duplicate key " + dup.get_duplicate_key()->inspect() + " in map (" + org.inspect() + ")."; } TypeMismatch::TypeMismatch(Backtraces traces, const Expression& var, const sass::string type) : Base(var.pstate(), def_msg, traces), var(var), type(type) { msg = var.to_string() + " is not an " + type + "."; } InvalidValue::InvalidValue(Backtraces traces, const Expression& val) : Base(val.pstate(), def_msg, traces), val(val) { msg = val.to_string() + " isn't a valid CSS value."; } StackError::StackError(Backtraces traces, const AST_Node& node) : Base(node.pstate(), def_msg, traces), node(node) { msg = "stack level too deep"; } EndlessExtendError::EndlessExtendError(Backtraces traces, const AST_Node& node) : Base(node.pstate(), def_msg, traces), node(node) { msg = "Extend is creating an absurdly big selector, aborting!"; } IncompatibleUnits::IncompatibleUnits(const Units& lhs, const Units& rhs) { msg = "Incompatible units: '" + rhs.unit() + "' and '" + lhs.unit() + "'."; } IncompatibleUnits::IncompatibleUnits(const UnitType lhs, const UnitType rhs) { msg = sass::string("Incompatible units: '") + unit_to_string(rhs) + "' and '" + unit_to_string(lhs) + "'."; } AlphaChannelsNotEqual::AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op) : OperationError(), lhs(lhs), rhs(rhs), op(op) { msg = "Alpha channels must be equal: " + lhs->to_string({ NESTED, 5 }) + " " + sass_op_to_name(op) + " " + rhs->to_string({ NESTED, 5 }) + "."; } ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs) : OperationError(), lhs(lhs), rhs(rhs) { msg = "divided by 0"; } UndefinedOperation::UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op) : OperationError(), lhs(lhs), rhs(rhs), op(op) { msg = def_op_msg + ": \"" + lhs->to_string({ NESTED, 5 }) + " " + sass_op_to_name(op) + " " + rhs->to_string({ TO_SASS, 5 }) + "\"."; } InvalidNullOperation::InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op) : UndefinedOperation(lhs, rhs, op) { msg = def_op_null_msg + ": \"" + lhs->inspect() + " " + sass_op_to_name(op) + " " + rhs->inspect() + "\"."; } SassValueError::SassValueError(Backtraces traces, SourceSpan pstate, OperationError& err) : Base(pstate, err.what(), traces) { msg = err.what(); prefix = err.errtype(); } TopLevelParent::TopLevelParent(Backtraces traces, SourceSpan pstate) : Base(pstate, "Top-level selectors may not contain the parent selector \"&\".", traces) { } UnsatisfiedExtend::UnsatisfiedExtend(Backtraces traces, Extension extension) : Base(extension.target->pstate(), "The target selector was not found.\n" "Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces) { } ExtendAcrossMedia::ExtendAcrossMedia(Backtraces traces, Extension extension) : Base(extension.target->pstate(), "You may not @extend selectors across media queries.\n" "Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces) { } } void warn(sass::string msg, SourceSpan pstate) { std::cerr << "Warning: " << msg << std::endl; } void warning(sass::string msg, SourceSpan pstate) { sass::string cwd(Sass::File::get_cwd()); sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd)); sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd)); sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.getPath())); std::cerr << "WARNING on line " << pstate.getLine() << ", column " << pstate.getColumn() << " of " << output_path << ":" << std::endl; std::cerr << msg << std::endl << std::endl; } void warn(sass::string msg, SourceSpan pstate, Backtrace* bt) { warn(msg, pstate); } void deprecated_function(sass::string msg, SourceSpan pstate) { sass::string cwd(Sass::File::get_cwd()); sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd)); sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd)); sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.getPath())); std::cerr << "DEPRECATION WARNING: " << msg << std::endl; std::cerr << "will be an error in future versions of Sass." << std::endl; std::cerr << " on line " << pstate.getLine() << " of " << output_path << std::endl; } void deprecated(sass::string msg, sass::string msg2, bool with_column, SourceSpan pstate) { sass::string cwd(Sass::File::get_cwd()); sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd)); sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd)); sass::string output_path(Sass::File::path_for_console(rel_path, pstate.getPath(), pstate.getPath())); std::cerr << "DEPRECATION WARNING on line " << pstate.getLine(); // if (with_column) std::cerr << ", column " << pstate.column + pstate.offset.column + 1; if (output_path.length()) std::cerr << " of " << output_path; std::cerr << ":" << std::endl; std::cerr << msg << std::endl; if (msg2.length()) std::cerr << msg2 << std::endl; std::cerr << std::endl; } void deprecated_bind(sass::string msg, SourceSpan pstate) { sass::string cwd(Sass::File::get_cwd()); sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd)); sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd)); sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.getPath())); std::cerr << "WARNING: " << msg << std::endl; std::cerr << " on line " << pstate.getLine() << " of " << output_path << std::endl; std::cerr << "This will be an error in future versions of Sass." << std::endl; } // should be replaced with error with backtraces void coreError(sass::string msg, SourceSpan pstate) { Backtraces traces; throw Exception::InvalidSyntax(pstate, traces, msg); } void error(sass::string msg, SourceSpan pstate, Backtraces& traces) { traces.push_back(Backtrace(pstate)); throw Exception::InvalidSyntax(pstate, traces, msg); } } golibsass-1.0.0/libsass_src/src/error_handling.hpp000066400000000000000000000175541405214413600223160ustar00rootroot00000000000000#ifndef SASS_ERROR_HANDLING_H #define SASS_ERROR_HANDLING_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include "units.hpp" #include "position.hpp" #include "backtrace.hpp" #include "ast_fwd_decl.hpp" #include "sass/functions.h" namespace Sass { struct Backtrace; namespace Exception { const sass::string def_msg = "Invalid sass detected"; const sass::string def_op_msg = "Undefined operation"; const sass::string def_op_null_msg = "Invalid null operation"; const sass::string def_nesting_limit = "Code too deeply nested"; class Base : public std::runtime_error { protected: sass::string msg; sass::string prefix; public: SourceSpan pstate; Backtraces traces; public: Base(SourceSpan pstate, sass::string msg, Backtraces traces); virtual const char* errtype() const { return prefix.c_str(); } virtual const char* what() const throw() { return msg.c_str(); } virtual ~Base() throw() {}; }; class InvalidSass : public Base { public: InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg); virtual ~InvalidSass() throw() {}; }; class InvalidParent : public Base { protected: Selector* parent; Selector* selector; public: InvalidParent(Selector* parent, Backtraces traces, Selector* selector); virtual ~InvalidParent() throw() {}; }; class MissingArgument : public Base { protected: sass::string fn; sass::string arg; sass::string fntype; public: MissingArgument(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string fntype); virtual ~MissingArgument() throw() {}; }; class InvalidArgumentType : public Base { protected: sass::string fn; sass::string arg; sass::string type; const Value* value; public: InvalidArgumentType(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string type, const Value* value = 0); virtual ~InvalidArgumentType() throw() {}; }; class InvalidVarKwdType : public Base { protected: sass::string name; const Argument* arg; public: InvalidVarKwdType(SourceSpan pstate, Backtraces traces, sass::string name, const Argument* arg = 0); virtual ~InvalidVarKwdType() throw() {}; }; class InvalidSyntax : public Base { public: InvalidSyntax(SourceSpan pstate, Backtraces traces, sass::string msg); virtual ~InvalidSyntax() throw() {}; }; class NestingLimitError : public Base { public: NestingLimitError(SourceSpan pstate, Backtraces traces, sass::string msg = def_nesting_limit); virtual ~NestingLimitError() throw() {}; }; class DuplicateKeyError : public Base { protected: const Map& dup; const Expression& org; public: DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org); virtual const char* errtype() const { return "Error"; } virtual ~DuplicateKeyError() throw() {}; }; class TypeMismatch : public Base { protected: const Expression& var; const sass::string type; public: TypeMismatch(Backtraces traces, const Expression& var, const sass::string type); virtual const char* errtype() const { return "Error"; } virtual ~TypeMismatch() throw() {}; }; class InvalidValue : public Base { protected: const Expression& val; public: InvalidValue(Backtraces traces, const Expression& val); virtual const char* errtype() const { return "Error"; } virtual ~InvalidValue() throw() {}; }; class StackError : public Base { protected: const AST_Node& node; public: StackError(Backtraces traces, const AST_Node& node); virtual const char* errtype() const { return "SystemStackError"; } virtual ~StackError() throw() {}; }; class EndlessExtendError : public Base { protected: const AST_Node& node; public: EndlessExtendError(Backtraces traces, const AST_Node& node); virtual const char* errtype() const { return "EndlessExtendError"; } virtual ~EndlessExtendError() throw() {}; }; /* common virtual base class (has no pstate or trace) */ class OperationError : public std::runtime_error { protected: sass::string msg; public: OperationError(sass::string msg = def_op_msg) : std::runtime_error(msg.c_str()), msg(msg) {}; public: virtual const char* errtype() const { return "Error"; } virtual const char* what() const throw() { return msg.c_str(); } virtual ~OperationError() throw() {}; }; class ZeroDivisionError : public OperationError { protected: const Expression& lhs; const Expression& rhs; public: ZeroDivisionError(const Expression& lhs, const Expression& rhs); virtual const char* errtype() const { return "ZeroDivisionError"; } virtual ~ZeroDivisionError() throw() {}; }; class IncompatibleUnits : public OperationError { protected: // const Sass::UnitType lhs; // const Sass::UnitType rhs; public: IncompatibleUnits(const Units& lhs, const Units& rhs); IncompatibleUnits(const UnitType lhs, const UnitType rhs); virtual ~IncompatibleUnits() throw() {}; }; class UndefinedOperation : public OperationError { protected: const Expression* lhs; const Expression* rhs; const Sass_OP op; public: UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op); // virtual const char* errtype() const { return "Error"; } virtual ~UndefinedOperation() throw() {}; }; class InvalidNullOperation : public UndefinedOperation { public: InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op); virtual ~InvalidNullOperation() throw() {}; }; class AlphaChannelsNotEqual : public OperationError { protected: const Expression* lhs; const Expression* rhs; const Sass_OP op; public: AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op); // virtual const char* errtype() const { return "Error"; } virtual ~AlphaChannelsNotEqual() throw() {}; }; class SassValueError : public Base { public: SassValueError(Backtraces traces, SourceSpan pstate, OperationError& err); virtual ~SassValueError() throw() {}; }; class TopLevelParent : public Base { public: TopLevelParent(Backtraces traces, SourceSpan pstate); virtual ~TopLevelParent() throw() {}; }; class UnsatisfiedExtend : public Base { public: UnsatisfiedExtend(Backtraces traces, Extension extension); virtual ~UnsatisfiedExtend() throw() {}; }; class ExtendAcrossMedia : public Base { public: ExtendAcrossMedia(Backtraces traces, Extension extension); virtual ~ExtendAcrossMedia() throw() {}; }; } void warn(sass::string msg, SourceSpan pstate); void warn(sass::string msg, SourceSpan pstate, Backtrace* bt); void warning(sass::string msg, SourceSpan pstate); void deprecated_function(sass::string msg, SourceSpan pstate); void deprecated(sass::string msg, sass::string msg2, bool with_column, SourceSpan pstate); void deprecated_bind(sass::string msg, SourceSpan pstate); // void deprecated(sass::string msg, SourceSpan pstate, Backtrace* bt); void coreError(sass::string msg, SourceSpan pstate); void error(sass::string msg, SourceSpan pstate, Backtraces& traces); } #endif golibsass-1.0.0/libsass_src/src/eval.cpp000066400000000000000000001504241405214413600202350ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include #include #include "file.hpp" #include "eval.hpp" #include "ast.hpp" #include "bind.hpp" #include "util.hpp" #include "inspect.hpp" #include "operators.hpp" #include "environment.hpp" #include "position.hpp" #include "sass/values.h" #include "to_value.hpp" #include "ast2c.hpp" #include "c2ast.hpp" #include "context.hpp" #include "backtrace.hpp" #include "lexer.hpp" #include "prelexer.hpp" #include "parser.hpp" #include "expand.hpp" #include "color_maps.hpp" #include "sass_functions.hpp" #include "error_handling.hpp" #include "util_string.hpp" namespace Sass { Eval::Eval(Expand& exp) : exp(exp), ctx(exp.ctx), traces(exp.traces), force(false), is_in_comment(false), is_in_selector_schema(false) { bool_true = SASS_MEMORY_NEW(Boolean, "[NA]", true); bool_false = SASS_MEMORY_NEW(Boolean, "[NA]", false); } Eval::~Eval() { } Env* Eval::environment() { return exp.environment(); } const sass::string Eval::cwd() { return ctx.cwd(); } struct Sass_Inspect_Options& Eval::options() { return ctx.c_options; } struct Sass_Compiler* Eval::compiler() { return ctx.c_compiler; } EnvStack& Eval::env_stack() { return exp.env_stack; } sass::vector& Eval::callee_stack() { return ctx.callee_stack; } Expression* Eval::operator()(Block* b) { Expression* val = 0; for (size_t i = 0, L = b->length(); i < L; ++i) { val = b->at(i)->perform(this); if (val) return val; } return val; } Expression* Eval::operator()(Assignment* a) { Env* env = environment(); sass::string var(a->variable()); if (a->is_global()) { if (!env->has_global(var)) { deprecated( "!global assignments won't be able to declare new variables in future versions.", "Consider adding `" + var + ": null` at the top level.", true, a->pstate()); } if (a->is_default()) { if (env->has_global(var)) { Expression* e = Cast(env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(this)); } } else { env->set_global(var, a->value()->perform(this)); } } else { env->set_global(var, a->value()->perform(this)); } } else if (a->is_default()) { if (env->has_lexical(var)) { auto cur = env; while (cur && cur->is_lexical()) { if (cur->has_local(var)) { if (AST_Node_Obj node = cur->get_local(var)) { Expression* e = Cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { cur->set_local(var, a->value()->perform(this)); } } else { throw std::runtime_error("Env not in sync"); } return 0; } cur = cur->parent(); } throw std::runtime_error("Env not in sync"); } else if (env->has_global(var)) { if (AST_Node_Obj node = env->get_global(var)) { Expression* e = Cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(this)); } } } else if (env->is_lexical()) { env->set_local(var, a->value()->perform(this)); } else { env->set_local(var, a->value()->perform(this)); } } else { env->set_lexical(var, a->value()->perform(this)); } return 0; } Expression* Eval::operator()(If* i) { ExpressionObj rv; Env env(environment()); env_stack().push_back(&env); ExpressionObj cond = i->predicate()->perform(this); if (!cond->is_false()) { rv = i->block()->perform(this); } else { Block_Obj alt = i->alternative(); if (alt) rv = alt->perform(this); } env_stack().pop_back(); return rv.detach(); } // For does not create a new env scope // But iteration vars are reset afterwards Expression* Eval::operator()(ForRule* f) { sass::string variable(f->variable()); ExpressionObj low = f->lower_bound()->perform(this); if (low->concrete_type() != Expression::NUMBER) { traces.push_back(Backtrace(low->pstate())); throw Exception::TypeMismatch(traces, *low, "integer"); } ExpressionObj high = f->upper_bound()->perform(this); if (high->concrete_type() != Expression::NUMBER) { traces.push_back(Backtrace(high->pstate())); throw Exception::TypeMismatch(traces, *high, "integer"); } Number_Obj sass_start = Cast(low); Number_Obj sass_end = Cast(high); // check if units are valid for sequence if (sass_start->unit() != sass_end->unit()) { sass::ostream msg; msg << "Incompatible units: '" << sass_end->unit() << "' and '" << sass_start->unit() << "'."; error(msg.str(), low->pstate(), traces); } double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment Env env(environment(), true); env_stack().push_back(&env); Block_Obj body = f->block(); Expression* val = 0; if (start < end) { if (f->is_inclusive()) ++end; for (double i = start; i < end; ++i) { Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); env.set_local(variable, it); val = body->perform(this); if (val) break; } } else { if (f->is_inclusive()) --end; for (double i = start; i > end; --i) { Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); env.set_local(variable, it); val = body->perform(this); if (val) break; } } env_stack().pop_back(); return val; } // Eval does not create a new env scope // But iteration vars are reset afterwards Expression* Eval::operator()(EachRule* e) { sass::vector variables(e->variables()); ExpressionObj expr = e->list()->perform(this); Env env(environment(), true); env_stack().push_back(&env); List_Obj list; Map* map = nullptr; if (expr->concrete_type() == Expression::MAP) { map = Cast(expr); } else if (SelectorList * ls = Cast(expr)) { ExpressionObj rv = Listize::perform(ls); list = Cast(rv); } else if (expr->concrete_type() != Expression::LIST) { list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA); list->append(expr); } else { list = Cast(expr); } Block_Obj body = e->block(); ExpressionObj val; if (map) { for (ExpressionObj key : map->keys()) { ExpressionObj value = map->at(key); if (variables.size() == 1) { List* variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE); variable->append(key); variable->append(value); env.set_local(variables[0], variable); } else { env.set_local(variables[0], key); env.set_local(variables[1], value); } val = body->perform(this); if (val) break; } } else { if (list->length() == 1 && Cast(list)) { list = Cast(list); } for (size_t i = 0, L = list->length(); i < L; ++i) { Expression* item = list->at(i); // unwrap value if the expression is an argument if (Argument* arg = Cast(item)) item = arg->value(); // check if we got passed a list of args (investigate) if (List* scalars = Cast(item)) { if (variables.size() == 1) { Expression* var = scalars; env.set_local(variables[0], var); } else { // https://github.com/sass/libsass/issues/3078 for (size_t j = 0, K = variables.size(); j < K; ++j) { env.set_local(variables[j], j >= scalars->length() ? SASS_MEMORY_NEW(Null, expr->pstate()) : scalars->at(j)); } } } else { if (variables.size() > 0) { env.set_local(variables.at(0), item); for (size_t j = 1, K = variables.size(); j < K; ++j) { // XXX: this is never hit via spec tests Expression* res = SASS_MEMORY_NEW(Null, expr->pstate()); env.set_local(variables[j], res); } } } val = body->perform(this); if (val) break; } } env_stack().pop_back(); return val.detach(); } Expression* Eval::operator()(WhileRule* w) { ExpressionObj pred = w->predicate(); Block_Obj body = w->block(); Env env(environment(), true); env_stack().push_back(&env); ExpressionObj cond = pred->perform(this); while (!cond->is_false()) { ExpressionObj val = body->perform(this); if (val) { env_stack().pop_back(); return val.detach(); } cond = pred->perform(this); } env_stack().pop_back(); return 0; } Expression* Eval::operator()(Return* r) { return r->value()->perform(this); } Expression* Eval::operator()(WarningRule* w) { Sass_Output_Style outstyle = options().output_style; options().output_style = NESTED; ExpressionObj message = w->message()->perform(this); Env* env = environment(); // try to use generic function if (env->has("@warn[f]")) { // add call stack entry callee_stack().push_back({ "@warn", w->pstate().getPath(), w->pstate().getLine(), w->pstate().getColumn(), SASS_CALLEE_FUNCTION, { env } }); Definition* def = Cast((*env)["@warn[f]"]); // Block_Obj body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false); sass_list_set_value(c_args, 0, message->perform(&ast2c)); union Sass_Value* c_val = c_func(c_args, c_function, compiler()); options().output_style = outstyle; callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0; } sass::string result(unquote(message->to_sass())); std::cerr << "WARNING: " << result << std::endl; traces.push_back(Backtrace(w->pstate())); std::cerr << traces_to_string(traces, " "); std::cerr << std::endl; options().output_style = outstyle; traces.pop_back(); return 0; } Expression* Eval::operator()(ErrorRule* e) { Sass_Output_Style outstyle = options().output_style; options().output_style = NESTED; ExpressionObj message = e->message()->perform(this); Env* env = environment(); // try to use generic function if (env->has("@error[f]")) { // add call stack entry callee_stack().push_back({ "@error", e->pstate().getPath(), e->pstate().getLine(), e->pstate().getColumn(), SASS_CALLEE_FUNCTION, { env } }); Definition* def = Cast((*env)["@error[f]"]); // Block_Obj body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false); sass_list_set_value(c_args, 0, message->perform(&ast2c)); union Sass_Value* c_val = c_func(c_args, c_function, compiler()); options().output_style = outstyle; callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0; } sass::string result(unquote(message->to_sass())); options().output_style = outstyle; error(result, e->pstate(), traces); return 0; } Expression* Eval::operator()(DebugRule* d) { Sass_Output_Style outstyle = options().output_style; options().output_style = NESTED; ExpressionObj message = d->value()->perform(this); Env* env = environment(); // try to use generic function if (env->has("@debug[f]")) { // add call stack entry callee_stack().push_back({ "@debug", d->pstate().getPath(), d->pstate().getLine(), d->pstate().getColumn(), SASS_CALLEE_FUNCTION, { env } }); Definition* def = Cast((*env)["@debug[f]"]); // Block_Obj body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); AST2C ast2c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA, false); sass_list_set_value(c_args, 0, message->perform(&ast2c)); union Sass_Value* c_val = c_func(c_args, c_function, compiler()); options().output_style = outstyle; callee_stack().pop_back(); sass_delete_value(c_args); sass_delete_value(c_val); return 0; } sass::string result(unquote(message->to_sass())); sass::string abs_path(Sass::File::rel2abs(d->pstate().getPath(), cwd(), cwd())); sass::string rel_path(Sass::File::abs2rel(d->pstate().getPath(), cwd(), cwd())); sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().getPath())); options().output_style = outstyle; std::cerr << output_path << ":" << d->pstate().getLine() << " DEBUG: " << result; std::cerr << std::endl; return 0; } Expression* Eval::operator()(List* l) { // special case for unevaluated map if (l->separator() == SASS_HASH) { Map_Obj lm = SASS_MEMORY_NEW(Map, l->pstate(), l->length() / 2); for (size_t i = 0, L = l->length(); i < L; i += 2) { ExpressionObj key = (*l)[i+0]->perform(this); ExpressionObj val = (*l)[i+1]->perform(this); // make sure the color key never displays its real name key->is_delayed(true); // verified *lm << std::make_pair(key, val); } if (lm->has_duplicate_key()) { traces.push_back(Backtrace(l->pstate())); throw Exception::DuplicateKeyError(traces, *lm, *l); } lm->is_interpolant(l->is_interpolant()); return lm->perform(this); } // check if we should expand it if (l->is_expanded()) return l; // regular case for unevaluated lists List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), l->length(), l->separator(), l->is_arglist(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { ll->append((*l)[i]->perform(this)); } ll->is_interpolant(l->is_interpolant()); ll->from_selector(l->from_selector()); ll->is_expanded(true); return ll.detach(); } Expression* Eval::operator()(Map* m) { if (m->is_expanded()) return m; // make sure we're not starting with duplicate keys. // the duplicate key state will have been set in the parser phase. if (m->has_duplicate_key()) { traces.push_back(Backtrace(m->pstate())); throw Exception::DuplicateKeyError(traces, *m, *m); } Map_Obj mm = SASS_MEMORY_NEW(Map, m->pstate(), m->length()); for (auto key : m->keys()) { Expression* ex_key = key->perform(this); Expression* ex_val = m->at(key); if (ex_val == NULL) continue; ex_val = ex_val->perform(this); *mm << std::make_pair(ex_key, ex_val); } // check the evaluated keys aren't duplicates. if (mm->has_duplicate_key()) { traces.push_back(Backtrace(m->pstate())); throw Exception::DuplicateKeyError(traces, *mm, *m); } mm->is_expanded(true); return mm.detach(); } Expression* Eval::operator()(Binary_Expression* b_in) { ExpressionObj lhs = b_in->left(); ExpressionObj rhs = b_in->right(); enum Sass_OP op_type = b_in->optype(); if (op_type == Sass_OP::AND) { // LOCAL_FLAG(force, true); lhs = lhs->perform(this); if (!*lhs) return lhs.detach(); return rhs->perform(this); } else if (op_type == Sass_OP::OR) { // LOCAL_FLAG(force, true); lhs = lhs->perform(this); if (*lhs) return lhs.detach(); return rhs->perform(this); } // Evaluate variables as early o while (Variable* l_v = Cast(lhs)) { lhs = operator()(l_v); } while (Variable* r_v = Cast(rhs)) { rhs = operator()(r_v); } Binary_ExpressionObj b = b_in; // Evaluate sub-expressions early on while (Binary_Expression* l_b = Cast(lhs)) { if (!force && l_b->is_delayed()) break; lhs = operator()(l_b); } while (Binary_Expression* r_b = Cast(rhs)) { if (!force && r_b->is_delayed()) break; rhs = operator()(r_b); } // don't eval delayed expressions (the '/' when used as a separator) if (!force && op_type == Sass_OP::DIV && b->is_delayed()) { b->right(b->right()->perform(this)); b->left(b->left()->perform(this)); return b.detach(); } // specific types we know are final // handle them early to avoid overhead if (Number* l_n = Cast(lhs)) { // lhs is number and rhs is number if (Number* r_n = Cast(rhs)) { try { switch (op_type) { case Sass_OP::EQ: return *l_n == *r_n ? bool_true : bool_false; case Sass_OP::NEQ: return *l_n == *r_n ? bool_false : bool_true; case Sass_OP::LT: return *l_n < *r_n ? bool_true : bool_false; case Sass_OP::GTE: return *l_n < *r_n ? bool_false : bool_true; case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false; case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: return Operators::op_numbers(op_type, *l_n, *r_n, options(), b_in->pstate()); default: break; } } catch (Exception::OperationError& err) { traces.push_back(Backtrace(b_in->pstate())); throw Exception::SassValueError(traces, b_in->pstate(), err); } } // lhs is number and rhs is color // Todo: allow to work with HSLA colors else if (Color* r_col = Cast(rhs)) { Color_RGBA_Obj r_c = r_col->toRGBA(); try { switch (op_type) { case Sass_OP::EQ: return *l_n == *r_c ? bool_true : bool_false; case Sass_OP::NEQ: return *l_n == *r_c ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: return Operators::op_number_color(op_type, *l_n, *r_c, options(), b_in->pstate()); default: break; } } catch (Exception::OperationError& err) { traces.push_back(Backtrace(b_in->pstate())); throw Exception::SassValueError(traces, b_in->pstate(), err); } } } else if (Color* l_col = Cast(lhs)) { Color_RGBA_Obj l_c = l_col->toRGBA(); // lhs is color and rhs is color if (Color* r_col = Cast(rhs)) { Color_RGBA_Obj r_c = r_col->toRGBA(); try { switch (op_type) { case Sass_OP::EQ: return *l_c == *r_c ? bool_true : bool_false; case Sass_OP::NEQ: return *l_c == *r_c ? bool_false : bool_true; case Sass_OP::LT: return *l_c < *r_c ? bool_true : bool_false; case Sass_OP::GTE: return *l_c < *r_c ? bool_false : bool_true; case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false; case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: return Operators::op_colors(op_type, *l_c, *r_c, options(), b_in->pstate()); default: break; } } catch (Exception::OperationError& err) { traces.push_back(Backtrace(b_in->pstate())); throw Exception::SassValueError(traces, b_in->pstate(), err); } } // lhs is color and rhs is number else if (Number* r_n = Cast(rhs)) { try { switch (op_type) { case Sass_OP::EQ: return *l_c == *r_n ? bool_true : bool_false; case Sass_OP::NEQ: return *l_c == *r_n ? bool_false : bool_true; case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD: return Operators::op_color_number(op_type, *l_c, *r_n, options(), b_in->pstate()); default: break; } } catch (Exception::OperationError& err) { traces.push_back(Backtrace(b_in->pstate())); throw Exception::SassValueError(traces, b_in->pstate(), err); } } } String_Schema_Obj ret_schema; // only the last item will be used to eval the binary expression if (String_Schema* s_l = Cast(b->left())) { if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) { ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate()); Binary_ExpressionObj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(), b->op(), s_l->last(), b->right()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // unverified for (size_t i = 0; i < s_l->length() - 1; ++i) { ret_schema->append(s_l->at(i)->perform(this)); } ret_schema->append(bin_ex->perform(this)); return ret_schema->perform(this); } } if (String_Schema* s_r = Cast(b->right())) { if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) { ret_schema = SASS_MEMORY_NEW(String_Schema, b->pstate()); Binary_ExpressionObj bin_ex = SASS_MEMORY_NEW(Binary_Expression, b->pstate(), b->op(), b->left(), s_r->first()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // verified ret_schema->append(bin_ex->perform(this)); for (size_t i = 1; i < s_r->length(); ++i) { ret_schema->append(s_r->at(i)->perform(this)); } return ret_schema->perform(this); } } // fully evaluate their values if (op_type == Sass_OP::EQ || op_type == Sass_OP::NEQ || op_type == Sass_OP::GT || op_type == Sass_OP::GTE || op_type == Sass_OP::LT || op_type == Sass_OP::LTE) { LOCAL_FLAG(force, true); lhs->is_expanded(false); lhs->set_delayed(false); lhs = lhs->perform(this); rhs->is_expanded(false); rhs->set_delayed(false); rhs = rhs->perform(this); } else { lhs = lhs->perform(this); } // not a logical connective, so go ahead and eval the rhs rhs = rhs->perform(this); AST_Node_Obj lu = lhs; AST_Node_Obj ru = rhs; Expression::Type l_type; Expression::Type r_type; // Is one of the operands an interpolant? String_Schema_Obj s1 = Cast(b->left()); String_Schema_Obj s2 = Cast(b->right()); Binary_ExpressionObj b1 = Cast(b->left()); Binary_ExpressionObj b2 = Cast(b->right()); bool schema_op = false; bool force_delay = (s2 && s2->is_left_interpolant()) || (s1 && s1->is_right_interpolant()) || (b1 && b1->is_right_interpolant()) || (b2 && b2->is_left_interpolant()); if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay) { if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB || op_type == Sass_OP::EQ) { // If possible upgrade LHS to a number (for number to string compare) if (String_Constant* str = Cast(lhs)) { sass::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) { lhs = Parser::lexed_dimension(b->pstate(), str->value()); } } // If possible upgrade RHS to a number (for string to number compare) if (String_Constant* str = Cast(rhs)) { sass::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::dimension, Prelexer::number >(start) != 0) { rhs = Parser::lexed_dimension(b->pstate(), str->value()); } } } To_Value to_value(ctx); ValueObj v_l = Cast(lhs->perform(&to_value)); ValueObj v_r = Cast(rhs->perform(&to_value)); if (force_delay) { sass::string str(""); str += v_l->to_string(options()); if (b->op().ws_before) str += " "; str += b->separator(); if (b->op().ws_after) str += " "; str += v_r->to_string(options()); String_Constant* val = SASS_MEMORY_NEW(String_Constant, b->pstate(), str); val->is_interpolant(b->left()->has_interpolant()); return val; } } // see if it's a relational expression try { switch(op_type) { case Sass_OP::EQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::eq(lhs, rhs)); case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::neq(lhs, rhs)); case Sass_OP::GT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gt(lhs, rhs)); case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gte(lhs, rhs)); case Sass_OP::LT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lt(lhs, rhs)); case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lte(lhs, rhs)); default: break; } } catch (Exception::OperationError& err) { traces.push_back(Backtrace(b->pstate())); throw Exception::SassValueError(traces, b->pstate(), err); } l_type = lhs->concrete_type(); r_type = rhs->concrete_type(); // ToDo: throw error in op functions // ToDo: then catch and re-throw them ExpressionObj rv; try { SourceSpan pstate(b->pstate()); if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { Number* l_n = Cast(lhs); Number* r_n = Cast(rhs); l_n->reduce(); r_n->reduce(); rv = Operators::op_numbers(op_type, *l_n, *r_n, options(), pstate); } else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { Number* l_n = Cast(lhs); Color_RGBA_Obj r_c = Cast(rhs)->toRGBA(); rv = Operators::op_number_color(op_type, *l_n, *r_c, options(), pstate); } else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) { Color_RGBA_Obj l_c = Cast(lhs)->toRGBA(); Number* r_n = Cast(rhs); rv = Operators::op_color_number(op_type, *l_c, *r_n, options(), pstate); } else if (l_type == Expression::COLOR && r_type == Expression::COLOR) { Color_RGBA_Obj l_c = Cast(lhs)->toRGBA(); Color_RGBA_Obj r_c = Cast(rhs)->toRGBA(); rv = Operators::op_colors(op_type, *l_c, *r_c, options(), pstate); } else { To_Value to_value(ctx); // this will leak if perform does not return a value! ValueObj v_l = Cast(lhs->perform(&to_value)); ValueObj v_r = Cast(rhs->perform(&to_value)); bool interpolant = b->is_right_interpolant() || b->is_left_interpolant() || b->is_interpolant(); if (op_type == Sass_OP::SUB) interpolant = false; // if (op_type == Sass_OP::DIV) interpolant = true; // check for type violations if (l_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) { traces.push_back(Backtrace(v_l->pstate())); throw Exception::InvalidValue(traces, *v_l); } if (r_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) { traces.push_back(Backtrace(v_r->pstate())); throw Exception::InvalidValue(traces, *v_r); } Value* ex = Operators::op_strings(b->op(), *v_l, *v_r, options(), pstate, !interpolant); // pass true to compress if (String_Constant* str = Cast(ex)) { if (str->concrete_type() == Expression::STRING) { String_Constant* lstr = Cast(lhs); String_Constant* rstr = Cast(rhs); if (op_type != Sass_OP::SUB) { if (String_Constant* org = lstr ? lstr : rstr) { str->quote_mark(org->quote_mark()); } } } } ex->is_interpolant(b->is_interpolant()); rv = ex; } } catch (Exception::OperationError& err) { traces.push_back(Backtrace(b->pstate())); // throw Exception::Base(b->pstate(), err.what()); throw Exception::SassValueError(traces, b->pstate(), err); } if (rv) { if (schema_op) { // XXX: this is never hit via spec tests (*s2)[0] = rv; rv = s2->perform(this); } } return rv.detach(); } Expression* Eval::operator()(Unary_Expression* u) { ExpressionObj operand = u->operand()->perform(this); if (u->optype() == Unary_Expression::NOT) { Boolean* result = SASS_MEMORY_NEW(Boolean, u->pstate(), (bool)*operand); result->value(!result->value()); return result; } else if (Number_Obj nr = Cast(operand)) { // negate value for minus unary expression if (u->optype() == Unary_Expression::MINUS) { Number_Obj cpy = SASS_MEMORY_COPY(nr); cpy->value( - cpy->value() ); // negate value return cpy.detach(); // return the copy } else if (u->optype() == Unary_Expression::SLASH) { sass::string str = '/' + nr->to_string(options()); return SASS_MEMORY_NEW(String_Constant, u->pstate(), str); } // nothing for positive return nr.detach(); } else { // Special cases: +/- variables which evaluate to null output just +/-, // but +/- null itself outputs the string if (operand->concrete_type() == Expression::NULL_VAL && Cast(u->operand())) { u->operand(SASS_MEMORY_NEW(String_Quoted, u->pstate(), "")); } // Never apply unary opertions on colors @see #2140 else if (Color* color = Cast(operand)) { // Use the color name if this was eval with one if (color->disp().length() > 0) { Unary_ExpressionObj cpy = SASS_MEMORY_COPY(u); cpy->operand(SASS_MEMORY_NEW(String_Constant, operand->pstate(), color->disp())); return SASS_MEMORY_NEW(String_Quoted, cpy->pstate(), cpy->inspect()); } } else { Unary_ExpressionObj cpy = SASS_MEMORY_COPY(u); cpy->operand(operand); return SASS_MEMORY_NEW(String_Quoted, cpy->pstate(), cpy->inspect()); } return SASS_MEMORY_NEW(String_Quoted, u->pstate(), u->inspect()); } // unreachable return u; } Expression* Eval::operator()(Function_Call* c) { if (traces.size() > Constants::MaxCallStack) { // XXX: this is never hit via spec tests sass::ostream stm; stm << "Stack depth exceeded max of " << Constants::MaxCallStack; error(stm.str(), c->pstate(), traces); } if (Cast(c->sname())) { ExpressionObj evaluated_name = c->sname()->perform(this); ExpressionObj evaluated_args = c->arguments()->perform(this); sass::string str(evaluated_name->to_string()); str += evaluated_args->to_string(); return SASS_MEMORY_NEW(String_Constant, c->pstate(), str); } sass::string name(Util::normalize_underscores(c->name())); sass::string full_name(name + "[f]"); // we make a clone here, need to implement that further Arguments_Obj args = c->arguments(); Env* env = environment(); if (!env->has(full_name) || (!c->via_call() && Prelexer::re_special_fun(name.c_str()))) { if (!env->has("*[f]")) { for (Argument_Obj arg : args->elements()) { if (List_Obj ls = Cast(arg->value())) { if (ls->size() == 0) error("() isn't a valid CSS value.", c->pstate(), traces); } } args = Cast(args->perform(this)); Function_Call_Obj lit = SASS_MEMORY_NEW(Function_Call, c->pstate(), c->name(), args); if (args->has_named_arguments()) { error("Plain CSS function " + c->name() + " doesn't support keyword arguments", c->pstate(), traces); } String_Quoted* str = SASS_MEMORY_NEW(String_Quoted, c->pstate(), lit->to_string(options())); str->is_interpolant(c->is_interpolant()); return str; } else { // call generic function full_name = "*[f]"; } } // further delay for calls if (full_name != "call[f]") { args->set_delayed(false); // verified } if (full_name != "if[f]") { args = Cast(args->perform(this)); } Definition* def = Cast((*env)[full_name]); if (c->func()) def = c->func()->definition(); if (def->is_overload_stub()) { sass::ostream ss; size_t L = args->length(); // account for rest arguments if (args->has_rest_argument() && args->length() > 0) { // get the rest arguments list List* rest = Cast(args->last()->value()); // arguments before rest argument plus rest if (rest) L += rest->length() - 1; } ss << full_name << L; full_name = ss.str(); sass::string resolved_name(full_name); if (!env->has(resolved_name)) error("overloaded function `" + sass::string(c->name()) + "` given wrong number of arguments", c->pstate(), traces); def = Cast((*env)[resolved_name]); } ExpressionObj result = c; Block_Obj body = def->block(); Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); if (c->is_css()) return result.detach(); Parameters_Obj params = def->parameters(); Env fn_env(def->environment()); env_stack().push_back(&fn_env); if (func || body) { bind(sass::string("Function"), c->name(), params, args, &fn_env, this, traces); sass::string msg(", in function `" + c->name() + "`"); traces.push_back(Backtrace(c->pstate(), msg)); callee_stack().push_back({ c->name().c_str(), c->pstate().getPath(), c->pstate().getLine(), c->pstate().getColumn(), SASS_CALLEE_FUNCTION, { env } }); // eval the body if user-defined or special, invoke underlying CPP function if native if (body /* && !Prelexer::re_special_fun(name.c_str()) */) { result = body->perform(this); } else if (func) { result = func(fn_env, *env, ctx, def->signature(), c->pstate(), traces, exp.getSelectorStack(), exp.originalStack); } if (!result) { error(sass::string("Function ") + c->name() + " finished without @return", c->pstate(), traces); } callee_stack().pop_back(); traces.pop_back(); } // else if it's a user-defined c function // convert call into C-API compatible form else if (c_function) { Sass_Function_Fn c_func = sass_function_get_function(c_function); if (full_name == "*[f]") { String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, c->pstate(), c->name()); Arguments_Obj new_args = SASS_MEMORY_NEW(Arguments, c->pstate()); new_args->append(SASS_MEMORY_NEW(Argument, c->pstate(), str)); new_args->concat(args); args = new_args; } // populates env with default values for params sass::string ff(c->name()); bind(sass::string("Function"), c->name(), params, args, &fn_env, this, traces); sass::string msg(", in function `" + c->name() + "`"); traces.push_back(Backtrace(c->pstate(), msg)); callee_stack().push_back({ c->name().c_str(), c->pstate().getPath(), c->pstate().getLine(), c->pstate().getColumn(), SASS_CALLEE_C_FUNCTION, { env } }); AST2C ast2c; union Sass_Value* c_args = sass_make_list(params->length(), SASS_COMMA, false); for(size_t i = 0; i < params->length(); i++) { Parameter_Obj param = params->at(i); sass::string key = param->name(); AST_Node_Obj node = fn_env.get_local(key); ExpressionObj arg = Cast(node); sass_list_set_value(c_args, i, arg->perform(&ast2c)); } union Sass_Value* c_val = c_func(c_args, c_function, compiler()); if (sass_value_get_tag(c_val) == SASS_ERROR) { sass::string message("error in C function " + c->name() + ": " + sass_error_get_message(c_val)); sass_delete_value(c_val); sass_delete_value(c_args); error(message, c->pstate(), traces); } else if (sass_value_get_tag(c_val) == SASS_WARNING) { sass::string message("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val)); sass_delete_value(c_val); sass_delete_value(c_args); error(message, c->pstate(), traces); } result = c2ast(c_val, traces, c->pstate()); callee_stack().pop_back(); traces.pop_back(); sass_delete_value(c_args); if (c_val != c_args) sass_delete_value(c_val); } // link back to function definition // only do this for custom functions if (result->pstate().getSrcId() == sass::string::npos) result->pstate(c->pstate()); result = result->perform(this); result->is_interpolant(c->is_interpolant()); env_stack().pop_back(); return result.detach(); } Expression* Eval::operator()(Variable* v) { ExpressionObj value; Env* env = environment(); const sass::string& name(v->name()); EnvResult rv(env->find(name)); if (rv.found) value = static_cast(rv.it->second.ptr()); else error("Undefined variable: \"" + v->name() + "\".", v->pstate(), traces); if (Argument* arg = Cast(value)) value = arg->value(); if (Number* nr = Cast(value)) nr->zero(true); // force flag value->is_interpolant(v->is_interpolant()); if (force) value->is_expanded(false); value->set_delayed(false); // verified value = value->perform(this); if(!force) rv.it->second = value; return value.detach(); } Expression* Eval::operator()(Color_RGBA* c) { return c; } Expression* Eval::operator()(Color_HSLA* c) { return c; } Expression* Eval::operator()(Number* n) { return n; } Expression* Eval::operator()(Boolean* b) { return b; } void Eval::interpolation(Context& ctx, sass::string& res, ExpressionObj ex, bool into_quotes, bool was_itpl) { bool needs_closing_brace = false; if (Arguments* args = Cast(ex)) { List* ll = SASS_MEMORY_NEW(List, args->pstate(), 0, SASS_COMMA); for(auto arg : args->elements()) { ll->append(arg->value()); } ll->is_interpolant(args->is_interpolant()); needs_closing_brace = true; res += "("; ex = ll; } if (Number* nr = Cast(ex)) { Number reduced(nr); reduced.reduce(); if (!reduced.is_valid_css_unit()) { traces.push_back(Backtrace(nr->pstate())); throw Exception::InvalidValue(traces, *nr); } } if (Argument* arg = Cast(ex)) { ex = arg->value(); } if (String_Quoted* sq = Cast(ex)) { if (was_itpl) { bool was_interpolant = ex->is_interpolant(); ex = SASS_MEMORY_NEW(String_Constant, sq->pstate(), sq->value()); ex->is_interpolant(was_interpolant); } } if (Cast(ex)) { return; } // parent selector needs another go if (Cast(ex)) { // XXX: this is never hit via spec tests ex = ex->perform(this); } if (List* l = Cast(ex)) { List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), 0, l->separator()); // this fixes an issue with bourbon sample, not really sure why // if (l->size() && Cast((*l)[0])) { res += ""; } for(ExpressionObj item : *l) { item->is_interpolant(l->is_interpolant()); sass::string rl(""); interpolation(ctx, rl, item, into_quotes, l->is_interpolant()); bool is_null = Cast(item) != 0; // rl != "" if (!is_null) ll->append(SASS_MEMORY_NEW(String_Quoted, item->pstate(), rl)); } // Check indicates that we probably should not get a list // here. Normally single list items are already unwrapped. if (l->size() > 1) { // string_to_output would fail "#{'_\a' '_\a'}"; sass::string str(ll->to_string(options())); str = read_hex_escapes(str); // read escapes newline_to_space(str); // replace directly res += str; // append to result string } else { res += (ll->to_string(options())); } ll->is_interpolant(l->is_interpolant()); } // Value // Function_Call // Selector_List // String_Quoted // String_Constant // Binary_Expression else { // ex = ex->perform(this); if (into_quotes && ex->is_interpolant()) { res += evacuate_escapes(ex ? ex->to_string(options()) : ""); } else { sass::string str(ex ? ex->to_string(options()) : ""); if (into_quotes) str = read_hex_escapes(str); res += str; // append to result string } } if (needs_closing_brace) res += ")"; } Expression* Eval::operator()(String_Schema* s) { size_t L = s->length(); bool into_quotes = false; if (L > 1) { if (!Cast((*s)[0]) && !Cast((*s)[L - 1])) { if (String_Constant* l = Cast((*s)[0])) { if (String_Constant* r = Cast((*s)[L - 1])) { if (r->value().size() > 0) { if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true; if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true; } } } } } bool was_quoted = false; bool was_interpolant = false; sass::string res(""); for (size_t i = 0; i < L; ++i) { bool is_quoted = Cast((*s)[i]) != NULL; if (was_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; } else if (i > 0 && is_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; } ExpressionObj ex = (*s)[i]->perform(this); interpolation(ctx, res, ex, into_quotes, ex->is_interpolant()); was_quoted = Cast((*s)[i]) != NULL; was_interpolant = (*s)[i]->is_interpolant(); } if (!s->is_interpolant()) { if (s->length() > 1 && res == "") return SASS_MEMORY_NEW(Null, s->pstate()); String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res, s->css()); return str.detach(); } // string schema seems to have a special unquoting behavior (also handles "nested" quotes) String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false, s->css()); // if (s->is_interpolant()) str->quote_mark(0); // String_Constant* str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res); if (str->quote_mark()) str->quote_mark('*'); else if (!is_in_comment) str->value(string_to_output(str->value())); str->is_interpolant(s->is_interpolant()); return str.detach(); } Expression* Eval::operator()(String_Constant* s) { return s; } Expression* Eval::operator()(String_Quoted* s) { String_Quoted* str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), ""); str->value(s->value()); str->quote_mark(s->quote_mark()); str->is_interpolant(s->is_interpolant()); return str; } Expression* Eval::operator()(SupportsOperation* c) { Expression* left = c->left()->perform(this); Expression* right = c->right()->perform(this); SupportsOperation* cc = SASS_MEMORY_NEW(SupportsOperation, c->pstate(), Cast(left), Cast(right), c->operand()); return cc; } Expression* Eval::operator()(SupportsNegation* c) { Expression* condition = c->condition()->perform(this); SupportsNegation* cc = SASS_MEMORY_NEW(SupportsNegation, c->pstate(), Cast(condition)); return cc; } Expression* Eval::operator()(SupportsDeclaration* c) { Expression* feature = c->feature()->perform(this); Expression* value = c->value()->perform(this); SupportsDeclaration* cc = SASS_MEMORY_NEW(SupportsDeclaration, c->pstate(), feature, value); return cc; } Expression* Eval::operator()(Supports_Interpolation* c) { Expression* value = c->value()->perform(this); Supports_Interpolation* cc = SASS_MEMORY_NEW(Supports_Interpolation, c->pstate(), value); return cc; } Expression* Eval::operator()(At_Root_Query* e) { ExpressionObj feature = e->feature(); feature = (feature ? feature->perform(this) : 0); ExpressionObj value = e->value(); value = (value ? value->perform(this) : 0); Expression* ee = SASS_MEMORY_NEW(At_Root_Query, e->pstate(), Cast(feature), value); return ee; } Media_Query* Eval::operator()(Media_Query* q) { String_Obj t = q->media_type(); t = static_cast(t.isNull() ? 0 : t->perform(this)); Media_Query_Obj qq = SASS_MEMORY_NEW(Media_Query, q->pstate(), t, q->length(), q->is_negated(), q->is_restricted()); for (size_t i = 0, L = q->length(); i < L; ++i) { qq->append(static_cast((*q)[i]->perform(this))); } return qq.detach(); } Expression* Eval::operator()(Media_Query_Expression* e) { ExpressionObj feature = e->feature(); feature = (feature ? feature->perform(this) : 0); if (feature && Cast(feature)) { feature = SASS_MEMORY_NEW(String_Quoted, feature->pstate(), Cast(feature)->value()); } ExpressionObj value = e->value(); value = (value ? value->perform(this) : 0); if (value && Cast(value)) { // XXX: this is never hit via spec tests value = SASS_MEMORY_NEW(String_Quoted, value->pstate(), Cast(value)->value()); } return SASS_MEMORY_NEW(Media_Query_Expression, e->pstate(), feature, value, e->is_interpolated()); } Expression* Eval::operator()(Null* n) { return n; } Expression* Eval::operator()(Argument* a) { ExpressionObj val = a->value()->perform(this); bool is_rest_argument = a->is_rest_argument(); bool is_keyword_argument = a->is_keyword_argument(); if (a->is_rest_argument()) { if (val->concrete_type() == Expression::MAP) { is_rest_argument = false; is_keyword_argument = true; } else if(val->concrete_type() != Expression::LIST) { List_Obj wrapper = SASS_MEMORY_NEW(List, val->pstate(), 0, SASS_COMMA, true); wrapper->append(val); val = wrapper; } } return SASS_MEMORY_NEW(Argument, a->pstate(), val, a->name(), is_rest_argument, is_keyword_argument); } Expression* Eval::operator()(Arguments* a) { Arguments_Obj aa = SASS_MEMORY_NEW(Arguments, a->pstate()); if (a->length() == 0) return aa.detach(); for (size_t i = 0, L = a->length(); i < L; ++i) { ExpressionObj rv = (*a)[i]->perform(this); Argument* arg = Cast(rv); if (!(arg->is_rest_argument() || arg->is_keyword_argument())) { aa->append(arg); } } if (a->has_rest_argument()) { ExpressionObj rest = a->get_rest_argument()->perform(this); ExpressionObj splat = Cast(rest)->value()->perform(this); Sass_Separator separator = SASS_COMMA; List* ls = Cast(splat); Map* ms = Cast(splat); List_Obj arglist = SASS_MEMORY_NEW(List, splat->pstate(), 0, ls ? ls->separator() : separator, true); if (ls && ls->is_arglist()) { arglist->concat(ls); } else if (ms) { aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), ms, "", false, true)); } else if (ls) { arglist->concat(ls); } else { arglist->append(splat); } if (arglist->length()) { aa->append(SASS_MEMORY_NEW(Argument, splat->pstate(), arglist, "", true)); } } if (a->has_keyword_argument()) { ExpressionObj rv = a->get_keyword_argument()->perform(this); Argument* rvarg = Cast(rv); ExpressionObj kwarg = rvarg->value()->perform(this); aa->append(SASS_MEMORY_NEW(Argument, kwarg->pstate(), kwarg, "", false, true)); } return aa.detach(); } Expression* Eval::operator()(Comment* c) { return 0; } SelectorList* Eval::operator()(Selector_Schema* s) { LOCAL_FLAG(is_in_selector_schema, true); // the parser will look for a brace to end the selector ExpressionObj sel = s->contents()->perform(this); sass::string result_str(sel->to_string(options())); result_str = unquote(Util::rtrim(result_str)); ItplFile* source = SASS_MEMORY_NEW(ItplFile, result_str.c_str(), s->pstate()); Parser p(source, ctx, traces); // If a schema contains a reference to parent it is already // connected to it, so don't connect implicitly anymore SelectorListObj parsed = p.parseSelectorList(true); flag_is_in_selector_schema.reset(); return parsed.detach(); } Expression* Eval::operator()(Parent_Reference* p) { if (SelectorListObj pr = exp.original()) { return operator()(pr); } else { return SASS_MEMORY_NEW(Null, p->pstate()); } } SimpleSelector* Eval::operator()(SimpleSelector* s) { return s; } PseudoSelector* Eval::operator()(PseudoSelector* pseudo) { // ToDo: should we eval selector? return pseudo; }; } golibsass-1.0.0/libsass_src/src/eval.hpp000066400000000000000000000064701405214413600202430ustar00rootroot00000000000000#ifndef SASS_EVAL_H #define SASS_EVAL_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "context.hpp" #include "listize.hpp" #include "operation.hpp" #include "environment.hpp" namespace Sass { class Expand; class Context; class Eval : public Operation_CRTP { public: Expand& exp; Context& ctx; Backtraces& traces; Eval(Expand& exp); ~Eval(); bool force; bool is_in_comment; bool is_in_selector_schema; Boolean_Obj bool_true; Boolean_Obj bool_false; Env* environment(); EnvStack& env_stack(); const sass::string cwd(); CalleeStack& callee_stack(); struct Sass_Inspect_Options& options(); struct Sass_Compiler* compiler(); // for evaluating function bodies Expression* operator()(Block*); Expression* operator()(Assignment*); Expression* operator()(If*); Expression* operator()(ForRule*); Expression* operator()(EachRule*); Expression* operator()(WhileRule*); Expression* operator()(Return*); Expression* operator()(WarningRule*); Expression* operator()(ErrorRule*); Expression* operator()(DebugRule*); Expression* operator()(List*); Expression* operator()(Map*); Expression* operator()(Binary_Expression*); Expression* operator()(Unary_Expression*); Expression* operator()(Function_Call*); Expression* operator()(Variable*); Expression* operator()(Number*); Expression* operator()(Color_RGBA*); Expression* operator()(Color_HSLA*); Expression* operator()(Boolean*); Expression* operator()(String_Schema*); Expression* operator()(String_Quoted*); Expression* operator()(String_Constant*); Media_Query* operator()(Media_Query*); Expression* operator()(Media_Query_Expression*); Expression* operator()(At_Root_Query*); Expression* operator()(SupportsOperation*); Expression* operator()(SupportsNegation*); Expression* operator()(SupportsDeclaration*); Expression* operator()(Supports_Interpolation*); Expression* operator()(Null*); Expression* operator()(Argument*); Expression* operator()(Arguments*); Expression* operator()(Comment*); // these will return selectors SelectorList* operator()(SelectorList*); SelectorList* operator()(ComplexSelector*); CompoundSelector* operator()(CompoundSelector*); SelectorComponent* operator()(SelectorComponent*); SimpleSelector* operator()(SimpleSelector* s); PseudoSelector* operator()(PseudoSelector* s); // they don't have any specific implementation (yet) IDSelector* operator()(IDSelector* s) { return s; }; ClassSelector* operator()(ClassSelector* s) { return s; }; TypeSelector* operator()(TypeSelector* s) { return s; }; AttributeSelector* operator()(AttributeSelector* s) { return s; }; PlaceholderSelector* operator()(PlaceholderSelector* s) { return s; }; // actual evaluated selectors SelectorList* operator()(Selector_Schema*); Expression* operator()(Parent_Reference*); // generic fallback template Expression* fallback(U x) { return Cast(x); } private: void interpolation(Context& ctx, sass::string& res, ExpressionObj ex, bool into_quotes, bool was_itpl = false); }; } #endif golibsass-1.0.0/libsass_src/src/eval_selectors.cpp000066400000000000000000000037121405214413600223150ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "expand.hpp" #include "eval.hpp" #include "ast.hpp" namespace Sass { SelectorList* Eval::operator()(SelectorList* s) { sass::vector rv; SelectorListObj sl = SASS_MEMORY_NEW(SelectorList, s->pstate()); for (size_t i = 0, iL = s->length(); i < iL; ++i) { rv.push_back(operator()(s->get(i))); } // we should actually permutate parent first // but here we have permutated the selector first size_t round = 0; while (round != sass::string::npos) { bool abort = true; for (size_t i = 0, iL = rv.size(); i < iL; ++i) { if (rv[i]->length() > round) { sl->append((*rv[i])[round]); abort = false; } } if (abort) { round = sass::string::npos; } else { ++round; } } return sl.detach(); } SelectorComponent* Eval::operator()(SelectorComponent* s) { return {}; } SelectorList* Eval::operator()(ComplexSelector* s) { bool implicit_parent = !exp.old_at_root_without_rule; if (is_in_selector_schema) exp.pushNullSelector(); SelectorListObj other = s->resolve_parent_refs( exp.getOriginalStack(), traces, implicit_parent); if (is_in_selector_schema) exp.popNullSelector(); for (size_t i = 0; i < other->length(); i++) { ComplexSelectorObj sel = other->at(i); for (size_t n = 0; n < sel->length(); n++) { if (CompoundSelectorObj comp = Cast(sel->at(n))) { sel->at(n) = operator()(comp); } } } return other.detach(); } CompoundSelector* Eval::operator()(CompoundSelector* s) { for (size_t i = 0; i < s->length(); i++) { SimpleSelector* ss = s->at(i); // skip parents here (called via resolve_parent_refs) s->at(i) = Cast(ss->perform(this)); } return s; } } golibsass-1.0.0/libsass_src/src/expand.cpp000066400000000000000000000641701405214413600205670ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "ast.hpp" #include "expand.hpp" #include "bind.hpp" #include "eval.hpp" #include "backtrace.hpp" #include "context.hpp" #include "parser.hpp" #include "sass_functions.hpp" #include "error_handling.hpp" namespace Sass { // simple endless recursion protection const size_t maxRecursion = 500; Expand::Expand(Context& ctx, Env* env, SelectorStack* stack, SelectorStack* originals) : ctx(ctx), traces(ctx.traces), eval(Eval(*this)), recursions(0), in_keyframes(false), at_root_without_rule(false), old_at_root_without_rule(false), env_stack(), block_stack(), call_stack(), selector_stack(), originalStack(), mediaStack() { env_stack.push_back(nullptr); env_stack.push_back(env); block_stack.push_back(nullptr); call_stack.push_back({}); if (stack == NULL) { pushToSelectorStack({}); } else { for (auto item : *stack) { if (item.isNull()) pushToSelectorStack({}); else pushToSelectorStack(item); } } if (originals == NULL) { pushToOriginalStack({}); } else { for (auto item : *stack) { if (item.isNull()) pushToOriginalStack({}); else pushToOriginalStack(item); } } mediaStack.push_back({}); } Env* Expand::environment() { if (env_stack.size() > 0) return env_stack.back(); return 0; } void Expand::pushNullSelector() { pushToSelectorStack({}); pushToOriginalStack({}); } void Expand::popNullSelector() { popFromOriginalStack(); popFromSelectorStack(); } SelectorStack Expand::getOriginalStack() { return originalStack; } SelectorStack Expand::getSelectorStack() { return selector_stack; } SelectorListObj& Expand::selector() { if (selector_stack.size() > 0) { auto& sel = selector_stack.back(); if (sel.isNull()) return sel; return sel; } // Avoid the need to return copies // We always want an empty first item selector_stack.push_back({}); return selector_stack.back();; } SelectorListObj& Expand::original() { if (originalStack.size() > 0) { auto& sel = originalStack.back(); if (sel.isNull()) return sel; return sel; } // Avoid the need to return copies // We always want an empty first item originalStack.push_back({}); return originalStack.back(); } SelectorListObj Expand::popFromSelectorStack() { SelectorListObj last = selector_stack.back(); if (selector_stack.size() > 0) selector_stack.pop_back(); if (last.isNull()) return {}; return last; } void Expand::pushToSelectorStack(SelectorListObj selector) { selector_stack.push_back(selector); } SelectorListObj Expand::popFromOriginalStack() { SelectorListObj last = originalStack.back(); if (originalStack.size() > 0) originalStack.pop_back(); if (last.isNull()) return {}; return last; } void Expand::pushToOriginalStack(SelectorListObj selector) { originalStack.push_back(selector); } // blocks create new variable scopes Block* Expand::operator()(Block* b) { // create new local environment // set the current env as parent Env env(environment()); // copy the block object (add items later) Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root()); // setup block and env stack this->block_stack.push_back(bb); this->env_stack.push_back(&env); // operate on block // this may throw up! this->append_block(b); // revert block and env stack this->block_stack.pop_back(); this->env_stack.pop_back(); // return copy return bb.detach(); } Statement* Expand::operator()(StyleRule* r) { LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule); if (in_keyframes) { Block* bb = operator()(r->block()); Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb); if (r->schema()) { pushNullSelector(); k->name(eval(r->schema())); popNullSelector(); } else if (r->selector()) { if (SelectorListObj s = r->selector()) { pushNullSelector(); k->name(eval(s)); popNullSelector(); } } return k.detach(); } if (r->schema()) { SelectorListObj sel = eval(r->schema()); r->selector(sel); for (auto complex : sel->elements()) { // ToDo: maybe we can get rid of chroots? complex->chroots(complex->has_real_parent_ref()); } } // reset when leaving scope LOCAL_FLAG(at_root_without_rule, false); SelectorListObj evaled = eval(r->selector()); // do not connect parent again Env env(environment()); if (block_stack.back()->is_root()) { env_stack.push_back(&env); } Block_Obj blk; pushToSelectorStack(evaled); // The copy is needed for parent reference evaluation // dart-sass stores it as `originalSelector` member pushToOriginalStack(SASS_MEMORY_COPY(evaled)); ctx.extender.addSelector(evaled, mediaStack.back()); if (r->block()) blk = operator()(r->block()); popFromOriginalStack(); popFromSelectorStack(); StyleRule* rr = SASS_MEMORY_NEW(StyleRule, r->pstate(), evaled, blk); if (block_stack.back()->is_root()) { env_stack.pop_back(); } rr->is_root(r->is_root()); rr->tabs(r->tabs()); return rr; } Statement* Expand::operator()(SupportsRule* f) { ExpressionObj condition = f->condition()->perform(&eval); SupportsRuleObj ff = SASS_MEMORY_NEW(SupportsRule, f->pstate(), Cast(condition), operator()(f->block())); return ff.detach(); } sass::vector Expand::mergeMediaQueries( const sass::vector& lhs, const sass::vector& rhs) { sass::vector queries; for (CssMediaQuery_Obj query1 : lhs) { for (CssMediaQuery_Obj query2 : rhs) { CssMediaQuery_Obj result = query1->merge(query2); if (result && !result->empty()) { queries.push_back(result); } } } return queries; } Statement* Expand::operator()(MediaRule* m) { ExpressionObj mq = eval(m->schema()); sass::string str_mq(mq->to_css(ctx.c_options)); ItplFile* source = SASS_MEMORY_NEW(ItplFile, str_mq.c_str(), m->pstate()); Parser parser(source, ctx, traces); // Create a new CSS only representation of the media rule CssMediaRuleObj css = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block()); sass::vector parsed = parser.parseCssMediaQueries(); if (mediaStack.size() && mediaStack.back()) { auto& parent = mediaStack.back()->elements(); css->concat(mergeMediaQueries(parent, parsed)); } else { css->concat(parsed); } mediaStack.push_back(css); css->block(operator()(m->block())); mediaStack.pop_back(); return css.detach(); } Statement* Expand::operator()(AtRootRule* a) { Block_Obj ab = a->block(); ExpressionObj ae = a->expression(); if (ae) ae = ae->perform(&eval); else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate()); LOCAL_FLAG(at_root_without_rule, Cast(ae)->exclude("rule")); LOCAL_FLAG(in_keyframes, false); ; Block_Obj bb = ab ? operator()(ab) : NULL; AtRootRuleObj aa = SASS_MEMORY_NEW(AtRootRule, a->pstate(), bb, Cast(ae)); return aa.detach(); } Statement* Expand::operator()(AtRule* a) { LOCAL_FLAG(in_keyframes, a->is_keyframes()); Block* ab = a->block(); SelectorList* as = a->selector(); Expression* av = a->value(); pushNullSelector(); if (av) av = av->perform(&eval); if (as) as = eval(as); popNullSelector(); Block* bb = ab ? operator()(ab) : NULL; AtRule* aa = SASS_MEMORY_NEW(AtRule, a->pstate(), a->keyword(), as, bb, av); return aa; } Statement* Expand::operator()(Declaration* d) { Block_Obj ab = d->block(); String_Obj old_p = d->property(); ExpressionObj prop = old_p->perform(&eval); String_Obj new_p = Cast(prop); // we might get a color back if (!new_p) { sass::string str(prop->to_string(ctx.c_options)); new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str); } ExpressionObj value = d->value(); if (value) value = value->perform(&eval); Block_Obj bb = ab ? operator()(ab) : NULL; if (!bb) { if (!value || (value->is_invisible() && !d->is_important())) { if (d->is_custom_property()) { error("Custom property values may not be empty.", d->value()->pstate(), traces); } else { return nullptr; } } } Declaration* decl = SASS_MEMORY_NEW(Declaration, d->pstate(), new_p, value, d->is_important(), d->is_custom_property(), bb); decl->tabs(d->tabs()); return decl; } Statement* Expand::operator()(Assignment* a) { Env* env = environment(); const sass::string& var(a->variable()); if (a->is_global()) { if (!env->has_global(var)) { deprecated( "!global assignments won't be able to declare new variables in future versions.", "Consider adding `" + var + ": null` at the top level.", true, a->pstate()); } if (a->is_default()) { if (env->has_global(var)) { ExpressionObj e = Cast(env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); } } else { env->set_global(var, a->value()->perform(&eval)); } } else { env->set_global(var, a->value()->perform(&eval)); } } else if (a->is_default()) { if (env->has_lexical(var)) { auto cur = env; while (cur && cur->is_lexical()) { if (cur->has_local(var)) { if (AST_Node_Obj node = cur->get_local(var)) { ExpressionObj e = Cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { cur->set_local(var, a->value()->perform(&eval)); } } else { throw std::runtime_error("Env not in sync"); } return 0; } cur = cur->parent(); } throw std::runtime_error("Env not in sync"); } else if (env->has_global(var)) { if (AST_Node_Obj node = env->get_global(var)) { ExpressionObj e = Cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); } } } else if (env->is_lexical()) { env->set_local(var, a->value()->perform(&eval)); } else { env->set_local(var, a->value()->perform(&eval)); } } else { env->set_lexical(var, a->value()->perform(&eval)); } return 0; } Statement* Expand::operator()(Import* imp) { Import_Obj result = SASS_MEMORY_NEW(Import, imp->pstate()); if (imp->import_queries() && imp->import_queries()->size()) { ExpressionObj ex = imp->import_queries()->perform(&eval); result->import_queries(Cast(ex)); } for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { result->urls().push_back(imp->urls()[i]->perform(&eval)); } // all resources have been dropped for Input_Stubs // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {} return result.detach(); } Statement* Expand::operator()(Import_Stub* i) { traces.push_back(Backtrace(i->pstate())); // get parent node from call stack AST_Node_Obj parent = call_stack.back(); if (Cast(parent) == NULL) { error("Import directives may not be used within control directives or mixins.", i->pstate(), traces); } // we don't seem to need that actually afterall Sass_Import_Entry import = sass_make_import( i->imp_path().c_str(), i->abs_path().c_str(), 0, 0 ); ctx.import_stack.push_back(import); Block_Obj trace_block = SASS_MEMORY_NEW(Block, i->pstate()); Trace_Obj trace = SASS_MEMORY_NEW(Trace, i->pstate(), i->imp_path(), trace_block, 'i'); block_stack.back()->append(trace); block_stack.push_back(trace_block); const sass::string& abs_path(i->resource().abs_path); append_block(ctx.sheets.at(abs_path).root); sass_delete_import(ctx.import_stack.back()); ctx.import_stack.pop_back(); block_stack.pop_back(); traces.pop_back(); return 0; } Statement* Expand::operator()(WarningRule* w) { // eval handles this too, because warnings may occur in functions w->perform(&eval); return 0; } Statement* Expand::operator()(ErrorRule* e) { // eval handles this too, because errors may occur in functions e->perform(&eval); return 0; } Statement* Expand::operator()(DebugRule* d) { // eval handles this too, because warnings may occur in functions d->perform(&eval); return 0; } Statement* Expand::operator()(Comment* c) { if (ctx.output_style() == COMPRESSED) { // comments should not be evaluated in compact // https://github.com/sass/libsass/issues/2359 if (!c->is_important()) return NULL; } eval.is_in_comment = true; Comment* rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast(c->text()->perform(&eval)), c->is_important()); eval.is_in_comment = false; // TODO: eval the text, once we're parsing/storing it as a String_Schema return rv; } Statement* Expand::operator()(If* i) { Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(i); ExpressionObj rv = i->predicate()->perform(&eval); if (*rv) { append_block(i->block()); } else { Block* alt = i->alternative(); if (alt) append_block(alt); } call_stack.pop_back(); env_stack.pop_back(); return 0; } // For does not create a new env scope // But iteration vars are reset afterwards Statement* Expand::operator()(ForRule* f) { sass::string variable(f->variable()); ExpressionObj low = f->lower_bound()->perform(&eval); if (low->concrete_type() != Expression::NUMBER) { traces.push_back(Backtrace(low->pstate())); throw Exception::TypeMismatch(traces, *low, "integer"); } ExpressionObj high = f->upper_bound()->perform(&eval); if (high->concrete_type() != Expression::NUMBER) { traces.push_back(Backtrace(high->pstate())); throw Exception::TypeMismatch(traces, *high, "integer"); } Number_Obj sass_start = Cast(low); Number_Obj sass_end = Cast(high); // check if units are valid for sequence if (sass_start->unit() != sass_end->unit()) { sass::ostream msg; msg << "Incompatible units: '" << sass_start->unit() << "' and '" << sass_end->unit() << "'."; error(msg.str(), low->pstate(), traces); } double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(f); Block* body = f->block(); if (start < end) { if (f->is_inclusive()) ++end; for (double i = start; i < end; ++i) { Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); env.set_local(variable, it); append_block(body); } } else { if (f->is_inclusive()) --end; for (double i = start; i > end; --i) { Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); env.set_local(variable, it); append_block(body); } } call_stack.pop_back(); env_stack.pop_back(); return 0; } // Eval does not create a new env scope // But iteration vars are reset afterwards Statement* Expand::operator()(EachRule* e) { sass::vector variables(e->variables()); ExpressionObj expr = e->list()->perform(&eval); List_Obj list; Map_Obj map; if (expr->concrete_type() == Expression::MAP) { map = Cast(expr); } else if (SelectorList * ls = Cast(expr)) { ExpressionObj rv = Listize::perform(ls); list = Cast(rv); } else if (expr->concrete_type() != Expression::LIST) { list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA); list->append(expr); } else { list = Cast(expr); } // remember variables and then reset them Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(e); Block* body = e->block(); if (map) { for (auto key : map->keys()) { ExpressionObj k = key->perform(&eval); ExpressionObj v = map->at(key)->perform(&eval); if (variables.size() == 1) { List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE); variable->append(k); variable->append(v); env.set_local(variables[0], variable); } else { env.set_local(variables[0], k); env.set_local(variables[1], v); } append_block(body); } } else { // bool arglist = list->is_arglist(); if (list->length() == 1 && Cast(list)) { list = Cast(list); } for (size_t i = 0, L = list->length(); i < L; ++i) { ExpressionObj item = list->at(i); // unwrap value if the expression is an argument if (Argument_Obj arg = Cast(item)) item = arg->value(); // check if we got passed a list of args (investigate) if (List_Obj scalars = Cast(item)) { if (variables.size() == 1) { List_Obj var = scalars; // if (arglist) var = (*scalars)[0]; env.set_local(variables[0], var); } else { for (size_t j = 0, K = variables.size(); j < K; ++j) { env.set_local(variables[j], j >= scalars->length() ? SASS_MEMORY_NEW(Null, expr->pstate()) : (*scalars)[j]->perform(&eval)); } } } else { if (variables.size() > 0) { env.set_local(variables.at(0), item); for (size_t j = 1, K = variables.size(); j < K; ++j) { ExpressionObj res = SASS_MEMORY_NEW(Null, expr->pstate()); env.set_local(variables[j], res); } } } append_block(body); } } call_stack.pop_back(); env_stack.pop_back(); return 0; } Statement* Expand::operator()(WhileRule* w) { ExpressionObj pred = w->predicate(); Block* body = w->block(); Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(w); ExpressionObj cond = pred->perform(&eval); while (!cond->is_false()) { append_block(body); cond = pred->perform(&eval); } call_stack.pop_back(); env_stack.pop_back(); return 0; } Statement* Expand::operator()(Return* r) { error("@return may only be used within a function", r->pstate(), traces); return 0; } Statement* Expand::operator()(ExtendRule* e) { // evaluate schema first if (e->schema()) { e->selector(eval(e->schema())); e->isOptional(e->selector()->is_optional()); } // evaluate the selector e->selector(eval(e->selector())); if (e->selector()) { for (auto complex : e->selector()->elements()) { if (complex->length() != 1) { error("complex selectors may not be extended.", complex->pstate(), traces); } if (const CompoundSelector* compound = complex->first()->getCompound()) { if (compound->length() != 1) { sass::ostream sels; bool addComma = false; sels << "Compound selectors may no longer be extended.\n"; sels << "Consider `@extend "; for (auto sel : compound->elements()) { if (addComma) sels << ", "; sels << sel->to_sass(); addComma = true; } sels << "` instead.\n"; sels << "See http://bit.ly/ExtendCompound for details."; warning(sels.str(), compound->pstate()); // Make this an error once deprecation is over for (SimpleSelectorObj simple : compound->elements()) { // Pass every selector we ever see to extender (to make them findable for extend) ctx.extender.addExtension(selector(), simple, mediaStack.back(), e->isOptional()); } } else { // Pass every selector we ever see to extender (to make them findable for extend) ctx.extender.addExtension(selector(), compound->first(), mediaStack.back(), e->isOptional()); } } else { error("complex selectors may not be extended.", complex->pstate(), traces); } } } return nullptr; } Statement* Expand::operator()(Definition* d) { Env* env = environment(); Definition_Obj dd = SASS_MEMORY_COPY(d); env->local_frame()[d->name() + (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd; if (d->type() == Definition::FUNCTION && ( Prelexer::calc_fn_call(d->name().c_str()) || d->name() == "element" || d->name() == "expression" || d->name() == "url" )) { deprecated( "Naming a function \"" + d->name() + "\" is disallowed and will be an error in future versions of Sass.", "This name conflicts with an existing CSS function with special parse rules.", false, d->pstate() ); } // set the static link so we can have lexical scoping dd->environment(env); return 0; } Statement* Expand::operator()(Mixin_Call* c) { if (recursions > maxRecursion) { throw Exception::StackError(traces, *c); } recursions ++; Env* env = environment(); sass::string full_name(c->name() + "[m]"); if (!env->has(full_name)) { error("no mixin named " + c->name(), c->pstate(), traces); } Definition_Obj def = Cast((*env)[full_name]); Block_Obj body = def->block(); Parameters_Obj params = def->parameters(); if (c->block() && c->name() != "@content" && !body->has_content()) { error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), traces); } ExpressionObj rv = c->arguments()->perform(&eval); Arguments_Obj args = Cast(rv); sass::string msg(", in mixin `" + c->name() + "`"); traces.push_back(Backtrace(c->pstate(), msg)); ctx.callee_stack.push_back({ c->name().c_str(), c->pstate().getPath(), c->pstate().getLine(), c->pstate().getColumn(), SASS_CALLEE_MIXIN, { env } }); Env new_env(def->environment()); env_stack.push_back(&new_env); if (c->block()) { Parameters_Obj params = c->block_parameters(); if (!params) params = SASS_MEMORY_NEW(Parameters, c->pstate()); // represent mixin content blocks as thunks/closures Definition_Obj thunk = SASS_MEMORY_NEW(Definition, c->pstate(), "@content", params, c->block(), Definition::MIXIN); thunk->environment(env); new_env.local_frame()["@content[m]"] = thunk; } bind(sass::string("Mixin"), c->name(), params, args, &new_env, &eval, traces); Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate()); Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block); env->set_global("is_in_mixin", bool_true); if (Block* pr = block_stack.back()) { trace_block->is_root(pr->is_root()); } block_stack.push_back(trace_block); for (auto bb : body->elements()) { if (StyleRule* r = Cast(bb)) { r->is_root(trace_block->is_root()); } Statement_Obj ith = bb->perform(this); if (ith) trace->block()->append(ith); } block_stack.pop_back(); env->del_global("is_in_mixin"); ctx.callee_stack.pop_back(); env_stack.pop_back(); traces.pop_back(); recursions --; return trace.detach(); } Statement* Expand::operator()(Content* c) { Env* env = environment(); // convert @content directives into mixin calls to the underlying thunk if (!env->has("@content[m]")) return 0; Arguments_Obj args = c->arguments(); if (!args) args = SASS_MEMORY_NEW(Arguments, c->pstate()); Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, c->pstate(), "@content", args); Trace_Obj trace = Cast(call->perform(this)); return trace.detach(); } // process and add to last block on stack inline void Expand::append_block(Block* b) { if (b->is_root()) call_stack.push_back(b); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = b->at(i); Statement_Obj ith = stm->perform(this); if (ith) block_stack.back()->append(ith); } if (b->is_root()) call_stack.pop_back(); } } golibsass-1.0.0/libsass_src/src/expand.hpp000066400000000000000000000050021405214413600205610ustar00rootroot00000000000000#ifndef SASS_EXPAND_H #define SASS_EXPAND_H #include #include "ast.hpp" #include "eval.hpp" #include "operation.hpp" #include "environment.hpp" namespace Sass { class Listize; class Context; class Eval; struct Backtrace; class Expand : public Operation_CRTP { public: Env* environment(); SelectorListObj& selector(); SelectorListObj& original(); SelectorListObj popFromSelectorStack(); SelectorStack getOriginalStack(); SelectorStack getSelectorStack(); void pushNullSelector(); void popNullSelector(); void pushToSelectorStack(SelectorListObj selector); SelectorListObj popFromOriginalStack(); void pushToOriginalStack(SelectorListObj selector); Context& ctx; Backtraces& traces; Eval eval; size_t recursions; bool in_keyframes; bool at_root_without_rule; bool old_at_root_without_rule; // it's easier to work with vectors EnvStack env_stack; BlockStack block_stack; CallStack call_stack; private: SelectorStack selector_stack; public: SelectorStack originalStack; MediaStack mediaStack; Boolean_Obj bool_true; private: sass::vector mergeMediaQueries(const sass::vector& lhs, const sass::vector& rhs); public: Expand(Context&, Env*, SelectorStack* stack = nullptr, SelectorStack* original = nullptr); ~Expand() { } Block* operator()(Block*); Statement* operator()(StyleRule*); Statement* operator()(MediaRule*); // Css StyleRule is already static // Statement* operator()(CssMediaRule*); Statement* operator()(SupportsRule*); Statement* operator()(AtRootRule*); Statement* operator()(AtRule*); Statement* operator()(Declaration*); Statement* operator()(Assignment*); Statement* operator()(Import*); Statement* operator()(Import_Stub*); Statement* operator()(WarningRule*); Statement* operator()(ErrorRule*); Statement* operator()(DebugRule*); Statement* operator()(Comment*); Statement* operator()(If*); Statement* operator()(ForRule*); Statement* operator()(EachRule*); Statement* operator()(WhileRule*); Statement* operator()(Return*); Statement* operator()(ExtendRule*); Statement* operator()(Definition*); Statement* operator()(Mixin_Call*); Statement* operator()(Content*); void append_block(Block*); }; } #endif golibsass-1.0.0/libsass_src/src/extender.cpp000066400000000000000000001301271405214413600211220ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "extender.hpp" #include "permutate.hpp" #include "dart_helpers.hpp" namespace Sass { // ########################################################################## // Constructor without default [mode]. // [traces] are needed to throw errors. // ########################################################################## Extender::Extender(Backtraces& traces) : mode(NORMAL), traces(traces), selectors(), extensions(), extensionsByExtender(), mediaContexts(), sourceSpecificity(), originals() {} // ########################################################################## // Constructor with specific [mode]. // [traces] are needed to throw errors. // ########################################################################## Extender::Extender(ExtendMode mode, Backtraces& traces) : mode(mode), traces(traces), selectors(), extensions(), extensionsByExtender(), mediaContexts(), sourceSpecificity(), originals() {} // ########################################################################## // Extends [selector] with [source] extender and [targets] extendees. // This works as though `source {@extend target}` were written in the // stylesheet, with the exception that [target] can contain compound // selectors which must be extended as a unit. // ########################################################################## SelectorListObj Extender::extend( SelectorListObj& selector, const SelectorListObj& source, const SelectorListObj& targets, Backtraces& traces) { return extendOrReplace(selector, source, targets, ExtendMode::TARGETS, traces); } // EO Extender::extend // ########################################################################## // Returns a copy of [selector] with [targets] replaced by [source]. // ########################################################################## SelectorListObj Extender::replace( SelectorListObj& selector, const SelectorListObj& source, const SelectorListObj& targets, Backtraces& traces) { return extendOrReplace(selector, source, targets, ExtendMode::REPLACE, traces); } // EO Extender::replace // ########################################################################## // A helper function for [extend] and [replace]. // ########################################################################## SelectorListObj Extender::extendOrReplace( SelectorListObj& selector, const SelectorListObj& source, const SelectorListObj& targets, const ExtendMode mode, Backtraces& traces) { ExtSelExtMapEntry extenders; for (auto complex : source->elements()) { // Extension.oneOff(complex as ComplexSelector) extenders.insert(complex, Extension(complex)); } for (auto complex : targets->elements()) { // This seems superfluous, check is done before!? // if (complex->length() != 1) { // error("complex selectors may not be extended.", complex->pstate(), traces); // } if (const CompoundSelector* compound = complex->first()->getCompound()) { ExtSelExtMap extensions; for (const SimpleSelectorObj& simple : compound->elements()) { extensions.insert(std::make_pair(simple, extenders)); } Extender extender(mode, traces); if (!selector->is_invisible()) { for (auto sel : selector->elements()) { extender.originals.insert(sel); } } selector = extender.extendList(selector, extensions, {}); } } return selector; } // EO extendOrReplace // ########################################################################## // The set of all simple selectors in style rules handled // by this extender. This includes simple selectors that // were added because of downstream extensions. // ########################################################################## ExtSmplSelSet Extender::getSimpleSelectors() const { ExtSmplSelSet set; for (auto& entry : selectors) { set.insert(entry.first); } return set; } // EO getSimpleSelectors // ########################################################################## // Check for extends that have not been satisfied. // Returns true if any non-optional extension did not // extend any selector. Updates the passed reference // to point to that Extension for further analysis. // ########################################################################## bool Extender::checkForUnsatisfiedExtends(Extension& unsatisfied) const { if (selectors.empty()) return false; ExtSmplSelSet originals = getSimpleSelectors(); for (auto target : extensions) { SimpleSelector* key = target.first; ExtSelExtMapEntry& val = target.second; if (val.empty()) continue; if (originals.find(key) == originals.end()) { const Extension& extension = val.front().second; if (extension.isOptional) continue; unsatisfied = extension; return true; } } return false; } // EO checkUnsatisfiedExtends // ########################################################################## // Adds [selector] to this extender, with [selectorSpan] as the span covering // the selector and [ruleSpan] as the span covering the entire style rule. // Extends [selector] using any registered extensions, then returns an empty // [ModifiableCssStyleRule] with the resulting selector. If any more relevant // extensions are added, the returned rule is automatically updated. // The [mediaContext] is the media query context in which the selector was // defined, or `null` if it was defined at the top level of the document. // ########################################################################## void Extender::addSelector( const SelectorListObj& selector, const CssMediaRuleObj& mediaContext) { // Note: dart-sass makes a copy here AFAICT // Note: probably why we have originalStack // SelectorListObj original = selector; if (!selector->isInvisible()) { for (auto complex : selector->elements()) { originals.insert(complex); } } if (!extensions.empty()) { SelectorListObj res = extendList(selector, extensions, mediaContext); selector->elements(res->elements()); } if (!mediaContext.isNull()) { mediaContexts.insert(selector, mediaContext); } registerSelector(selector, selector); } // EO addSelector // ########################################################################## // Registers the [SimpleSelector]s in [list] // to point to [rule] in [selectors]. // ########################################################################## void Extender::registerSelector( const SelectorListObj& list, const SelectorListObj& rule) { if (list.isNull() || list->empty()) return; for (auto complex : list->elements()) { for (auto component : complex->elements()) { if (auto compound = component->getCompound()) { for (SimpleSelector* simple : compound->elements()) { selectors[simple].insert(rule); if (auto pseudo = simple->getPseudoSelector()) { if (pseudo->selector()) { auto sel = pseudo->selector(); registerSelector(sel, rule); } } } } } } } // EO registerSelector // ########################################################################## // Returns an extension that combines [left] and [right]. Throws // a [SassException] if [left] and [right] have incompatible // media contexts. Throws an [ArgumentError] if [left] // and [right] don't have the same extender and target. // ########################################################################## Extension Extender::mergeExtension( const Extension& lhs, const Extension& rhs) { // If one extension is optional and doesn't add a // special media context, it doesn't need to be merged. if (rhs.isOptional && rhs.mediaContext.isNull()) return lhs; if (lhs.isOptional && lhs.mediaContext.isNull()) return rhs; Extension rv(lhs); // ToDo: is this right? rv.isOptional = true; rv.isOriginal = false; return rv; } // EO mergeExtension // ########################################################################## // Helper function to copy extension between maps // ########################################################################## // Seems only relevant for sass 4.0 modules // ########################################################################## /* void mapCopyExts( ExtSelExtMap& dest, const ExtSelExtMap& source) { for (auto it : source) { SimpleSelectorObj key = it.first; ExtSelExtMapEntry& inner = it.second; ExtSelExtMap::iterator dmap = dest.find(key); if (dmap == dest.end()) { dest.insert(std::make_pair(key, inner)); } else { ExtSelExtMapEntry& imap = dmap->second; // ToDo: optimize ordered_map API! // ToDo: we iterate and fetch the value for (ComplexSelectorObj& it2 : inner) { imap.insert(it2, inner.get(it2)); } } } } */ // EO mapCopyExts // ########################################################################## // Adds an extension to this extender. The [extender] is the selector for the // style rule in which the extension is defined, and [target] is the selector // passed to `@extend`. The [extend] provides the extend span and indicates // whether the extension is optional. The [mediaContext] defines the media query // context in which the extension is defined. It can only extend selectors // within the same context. A `null` context indicates no media queries. // ########################################################################## // ToDo: rename extender to parent, since it is not involved in extending stuff // ToDo: check why dart sass passes the ExtendRule around (is this the main selector?) // ########################################################################## // Note: this function could need some logic cleanup // ########################################################################## void Extender::addExtension( const SelectorListObj& extender, const SimpleSelectorObj& target, const CssMediaRuleObj& mediaQueryContext, bool is_optional) { auto rules = selectors.find(target); bool hasRule = rules != selectors.end(); ExtSelExtMapEntry newExtensions; // ToDo: we check this here first and fetch the same? item again after the loop!? bool hasExistingExtensions = extensionsByExtender.find(target) != extensionsByExtender.end(); ExtSelExtMapEntry& sources = extensions[target]; for (auto& complex : extender->elements()) { Extension state(complex); // ToDo: fine-tune public API state.target = target; state.isOptional = is_optional; state.mediaContext = mediaQueryContext; if (sources.hasKey(complex)) { // If there's already an extend from [extender] to [target], // we don't need to re-run the extension. We may need to // mark the extension as mandatory, though. // sources.insert(complex, mergeExtension(existingState->second, state); // ToDo: implement behavior once use case is found!? continue; } sources.insert(complex, state); for (auto& component : complex->elements()) { if (auto compound = component->getCompound()) { for (auto& simple : compound->elements()) { extensionsByExtender[simple].push_back(state); if (sourceSpecificity.find(simple) == sourceSpecificity.end()) { // Only source specificity for the original selector is relevant. // Selectors generated by `@extend` don't get new specificity. sourceSpecificity[simple] = complex->maxSpecificity(); } } } } if (hasRule || hasExistingExtensions) { newExtensions.insert(complex, state); } } // EO foreach complex if (newExtensions.empty()) { return; } ExtSelExtMap newExtensionsByTarget; newExtensionsByTarget.insert(std::make_pair(target, newExtensions)); // ToDo: do we really need to fetch again (see top off fn) auto existingExtensions = extensionsByExtender.find(target); if (existingExtensions != extensionsByExtender.end()) { if (hasExistingExtensions && !existingExtensions->second.empty()) { // Seems only relevant for sass 4.0 modules // auto additionalExtensions = extendExistingExtensions(existingExtensions->second, newExtensionsByTarget); // Seems only relevant for sass 4.0 modules /* if (!additionalExtensions.empty()) { mapCopyExts(newExtensionsByTarget, additionalExtensions); } */ } } if (hasRule) { extendExistingStyleRules(selectors[target], newExtensionsByTarget); } } // EO addExtension // ########################################################################## // Extend [extensions] using [newExtensions]. // ########################################################################## // Note: dart-sass throws an error in here // ########################################################################## void Extender::extendExistingStyleRules( const ExtListSelSet& rules, const ExtSelExtMap& newExtensions) { // Is a modifyableCssStyleRUle in dart sass for (const SelectorListObj& rule : rules) { const SelectorListObj& oldValue = SASS_MEMORY_COPY(rule); CssMediaRuleObj mediaContext; if (mediaContexts.hasKey(rule)) mediaContext = mediaContexts.get(rule); SelectorListObj ext = extendList(rule, newExtensions, mediaContext); // If no extends actually happened (for example because unification // failed), we don't need to re-register the selector. if (ObjEqualityFn(oldValue, ext)) continue; rule->elements(ext->elements()); registerSelector(rule, rule); } } // EO extendExistingStyleRules // ########################################################################## // Extend [extensions] using [newExtensions]. Note that this does duplicate // some work done by [_extendExistingStyleRules], but it's necessary to // expand each extension's extender separately without reference to the full // selector list, so that relevant results don't get trimmed too early. // // Returns extensions that should be added to [newExtensions] before // extending selectors in order to properly handle extension loops such as: // // .c {x: y; @extend .a} // .x.y.a {@extend .b} // .z.b {@extend .c} // // Returns `null` (Note: empty map) if there are no extensions to add. // ########################################################################## // Note: maybe refactor to return `bool` (and pass reference) // Note: dart-sass throws an error in here // ########################################################################## ExtSelExtMap Extender::extendExistingExtensions( // Taking in a reference here makes MSVC debug stuck!? const sass::vector& oldExtensions, const ExtSelExtMap& newExtensions) { ExtSelExtMap additionalExtensions; // During the loop `oldExtensions` vector might be changed. // Callers normally pass this from `extensionsByExtender` and // that points back to the `sources` vector from `extensions`. for (size_t i = 0, iL = oldExtensions.size(); i < iL; i += 1) { const Extension& extension = oldExtensions[i]; ExtSelExtMapEntry& sources = extensions[extension.target]; sass::vector selectors(extendComplex( extension.extender, newExtensions, extension.mediaContext )); if (selectors.empty()) { continue; } // ToDo: "catch" error from extend bool first = false, containsExtension = ObjEqualityFn(selectors.front(), extension.extender); for (const ComplexSelectorObj& complex : selectors) { // If the output contains the original complex // selector, there's no need to recreate it. if (containsExtension && first) { first = false; continue; } const Extension withExtender = extension.withExtender(complex); if (sources.hasKey(complex)) { sources.insert(complex, mergeExtension( sources.get(complex), withExtender)); } else { sources.insert(complex, withExtender); /* // Seems only relevant for sass 4.0 modules for (auto& component : complex->elements()) { if (auto compound = component->getCompound()) { for (auto& simple : compound->elements()) { extensionsByExtender[simple].push_back(withExtender); } } } if (newExtensions.find(extension.target) != newExtensions.end()) { additionalExtensions[extension.target].insert(complex, withExtender); } */ } } // If [selectors] doesn't contain [extension.extender], // for example if it was replaced due to :not() expansion, // we must get rid of the old version. /* // Seems only relevant for sass 4.0 modules if (!containsExtension) { sources.erase(extension.extender); } */ } return additionalExtensions; } // EO extendExistingExtensions // ########################################################################## // Extends [list] using [extensions]. // ########################################################################## SelectorListObj Extender::extendList( const SelectorListObj& list, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext) { // This could be written more simply using [List.map], but we want to // avoid any allocations in the common case where no extends apply. sass::vector extended; for (size_t i = 0; i < list->length(); i++) { const ComplexSelectorObj& complex = list->get(i); sass::vector result = extendComplex(complex, extensions, mediaQueryContext); if (result.empty()) { if (!extended.empty()) { extended.push_back(complex); } } else { if (extended.empty()) { for (size_t n = 0; n < i; n += 1) { extended.push_back(list->get(n)); } } for (auto sel : result) { extended.push_back(sel); } } } if (extended.empty()) { return list; } SelectorListObj rv = SASS_MEMORY_NEW(SelectorList, list->pstate()); rv->concat(trim(extended, originals)); return rv; } // EO extendList // ########################################################################## // Extends [complex] using [extensions], and // returns the contents of a [SelectorList]. // ########################################################################## sass::vector Extender::extendComplex( // Taking in a reference here makes MSVC debug stuck!? const ComplexSelectorObj& complex, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext) { // The complex selectors that each compound selector in [complex.components] // can expand to. // // For example, given // // .a .b {...} // .x .y {@extend .b} // // this will contain // // [ // [.a], // [.b, .x .y] // ] // // This could be written more simply using [List.map], but we want to avoid // any allocations in the common case where no extends apply. sass::vector result; sass::vector> extendedNotExpanded; bool isOriginal = originals.find(complex) != originals.end(); for (size_t i = 0; i < complex->length(); i += 1) { const SelectorComponentObj& component = complex->get(i); if (CompoundSelector* compound = Cast(component)) { sass::vector extended = extendCompound( compound, extensions, mediaQueryContext, isOriginal); if (extended.empty()) { if (!extendedNotExpanded.empty()) { extendedNotExpanded.push_back({ compound->wrapInComplex() }); } } else { // Note: dart-sass checks for null!? if (extendedNotExpanded.empty()) { for (size_t n = 0; n < i; n++) { extendedNotExpanded.push_back({ complex->at(n)->wrapInComplex() }); } } extendedNotExpanded.push_back(extended); } } else { // Note: dart-sass checks for null!? if (!extendedNotExpanded.empty()) { extendedNotExpanded.push_back({ component->wrapInComplex() }); } } } // Note: dart-sass checks for null!? if (extendedNotExpanded.empty()) { return {}; } bool first = true; // ToDo: either change weave or paths to work with the same data? sass::vector> paths = permutate(extendedNotExpanded); for (const sass::vector& path : paths) { // Unpack the inner complex selector to component list sass::vector> _paths; for (const ComplexSelectorObj& sel : path) { _paths.insert(_paths.end(), sel->elements()); } sass::vector> weaved = weave(_paths); for (sass::vector& components : weaved) { ComplexSelectorObj cplx = SASS_MEMORY_NEW(ComplexSelector, complex->pstate()); cplx->hasPreLineFeed(complex->hasPreLineFeed()); for (auto& pp : path) { if (pp->hasPreLineFeed()) { cplx->hasPreLineFeed(true); } } cplx->elements(components); // Make sure that copies of [complex] retain their status // as "original" selectors. This includes selectors that // are modified because a :not() was extended into. if (first && originals.find(complex) != originals.end()) { originals.insert(cplx); } first = false; auto it = result.begin(); while (it != result.end()) { if (ObjEqualityFn(*it, cplx)) break; it += 1; } if (it == result.end()) { result.push_back(cplx); } if (result.size() > 500) { throw Exception::EndlessExtendError(traces, complex); } } } return result; } // EO extendComplex // ########################################################################## // Returns a one-off [Extension] whose // extender is composed solely of [simple]. // ########################################################################## Extension Extender::extensionForSimple( const SimpleSelectorObj& simple) const { Extension extension(simple->wrapInComplex()); extension.specificity = maxSourceSpecificity(simple); extension.isOriginal = true; return extension; } // Extender::extensionForSimple // ########################################################################## // Returns a one-off [Extension] whose extender is composed // solely of a compound selector containing [simples]. // ########################################################################## Extension Extender::extensionForCompound( // Taking in a reference here makes MSVC debug stuck!? const sass::vector& simples) const { CompoundSelectorObj compound = SASS_MEMORY_NEW(CompoundSelector, SourceSpan("[ext]")); compound->concat(simples); Extension extension(compound->wrapInComplex()); // extension.specificity = sourceSpecificity[simple]; extension.isOriginal = true; return extension; } // EO extensionForCompound // ########################################################################## // Extends [compound] using [extensions], and returns the // contents of a [SelectorList]. The [inOriginal] parameter // indicates whether this is in an original complex selector, // meaning that [compound] should not be trimmed out. // ########################################################################## sass::vector Extender::extendCompound( const CompoundSelectorObj& compound, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext, bool inOriginal) { // If there's more than one target and they all need to // match, we track which targets are actually extended. ExtSmplSelSet targetsUsed2; ExtSmplSelSet* targetsUsed = nullptr; if (mode != ExtendMode::NORMAL && extensions.size() > 1) { targetsUsed = &targetsUsed2; } sass::vector result; // The complex selectors produced from each component of [compound]. sass::vector> options; for (size_t i = 0; i < compound->length(); i++) { const SimpleSelectorObj& simple = compound->get(i); auto extended = extendSimple(simple, extensions, mediaQueryContext, targetsUsed); if (extended.empty()) { if (!options.empty()) { options.push_back({ extensionForSimple(simple) }); } } else { if (options.empty()) { if (i != 0) { sass::vector in; for (size_t n = 0; n < i; n += 1) { in.push_back(compound->get(n)); } options.push_back({ extensionForCompound(in) }); } } options.insert(options.end(), extended.begin(), extended.end()); } } if (options.empty()) { return {}; } // If [_mode] isn't [ExtendMode.normal] and we didn't use all // the targets in [extensions], extension fails for [compound]. if (targetsUsed != nullptr) { if (targetsUsed->size() != extensions.size()) { if (!targetsUsed->empty()) { return {}; } } } // Optimize for the simple case of a single simple // selector that doesn't need any unification. if (options.size() == 1) { sass::vector exts = options[0]; for (size_t n = 0; n < exts.size(); n += 1) { exts[n].assertCompatibleMediaContext(mediaQueryContext, traces); // To fix invalid css we need to re-order some // Therefore we need to make copies for them if (exts[n].extender->isInvalidCss()) { exts[n].extender = SASS_MEMORY_COPY(exts[n].extender); for (SelectorComponentObj& component : exts[n].extender->elements()) { if (CompoundSelector* compound = component->getCompound()) { if (compound->isInvalidCss()) { CompoundSelector* copy = SASS_MEMORY_COPY(compound); copy->sortChildren(); component = copy; } } } } result.push_back(exts[n].extender); } return result; } // Find all paths through [options]. In this case, each path represents a // different unification of the base selector. For example, if we have: // // .a.b {...} // .w .x {@extend .a} // .y .z {@extend .b} // // then [options] is `[[.a, .w .x], [.b, .y .z]]` and `paths(options)` is // // [ // [.a, .b], // [.a, .y .z], // [.w .x, .b], // [.w .x, .y .z] // ] // // We then unify each path to get a list of complex selectors: // // [ // [.a.b], // [.y .a.z], // [.w .x.b], // [.w .y .x.z, .y .w .x.z] // ] bool first = mode != ExtendMode::REPLACE; sass::vector unifiedPaths; sass::vector> prePaths = permutate(options); for (size_t i = 0; i < prePaths.size(); i += 1) { sass::vector> complexes; const sass::vector& path = prePaths[i]; if (first) { // The first path is always the original selector. We can't just // return [compound] directly because pseudo selectors may be // modified, but we don't have to do any unification. first = false; CompoundSelectorObj mergedSelector = SASS_MEMORY_NEW(CompoundSelector, "[ext]"); for (size_t n = 0; n < path.size(); n += 1) { const ComplexSelectorObj& sel = path[n].extender; if (CompoundSelectorObj compound = Cast(sel->last())) { mergedSelector->concat(compound->elements()); } } complexes.push_back({ mergedSelector }); } else { sass::vector originals; sass::vector> toUnify; for (auto& state : path) { if (state.isOriginal) { const ComplexSelectorObj& sel = state.extender; if (const CompoundSelector* compound = Cast(sel->last())) { originals.insert(originals.end(), compound->last()); } } else { toUnify.push_back(state.extender->elements()); } } if (!originals.empty()) { CompoundSelectorObj merged = SASS_MEMORY_NEW(CompoundSelector, "[compound]"); merged->concat(originals); toUnify.insert(toUnify.begin(), { merged }); } complexes = unifyComplex(toUnify); if (complexes.empty()) { return {}; } } bool lineBreak = false; // var specificity = _sourceSpecificityFor(compound); for (const Extension& state : path) { state.assertCompatibleMediaContext(mediaQueryContext, traces); lineBreak = lineBreak || state.extender->hasPreLineFeed(); // specificity = math.max(specificity, state.specificity); } for (sass::vector& components : complexes) { auto sel = SASS_MEMORY_NEW(ComplexSelector, "[unified]"); sel->hasPreLineFeed(lineBreak); sel->elements(components); /* This seems to do too much in regard of previous behavior for (SelectorComponentObj& component : sel->elements()) { if (CompoundSelector* compound = component->getCompound()) { if (compound->isInvalidCss()) { CompoundSelector* copy = SASS_MEMORY_COPY(compound); copy->sortChildren(); component = copy; } } }*/ unifiedPaths.push_back(sel); } } return unifiedPaths; } // EO extendCompound // ########################################################################## // Extends [simple] without extending the // contents of any selector pseudos it contains. // ########################################################################## sass::vector Extender::extendWithoutPseudo( const SimpleSelectorObj& simple, const ExtSelExtMap& extensions, ExtSmplSelSet* targetsUsed) const { auto extension = extensions.find(simple); if (extension == extensions.end()) return {}; const ExtSelExtMapEntry& extenders = extension->second; if (targetsUsed != nullptr) { targetsUsed->insert(simple); } if (mode == ExtendMode::REPLACE) { return extenders.values(); } const sass::vector& values = extenders.values(); sass::vector result; result.reserve(values.size() + 1); result.push_back(extensionForSimple(simple)); result.insert(result.end(), values.begin(), values.end()); return result; } // EO extendWithoutPseudo // ########################################################################## // Extends [simple] and also extending the // contents of any selector pseudos it contains. // ########################################################################## sass::vector> Extender::extendSimple( const SimpleSelectorObj& simple, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext, ExtSmplSelSet* targetsUsed) { if (PseudoSelector* pseudo = Cast(simple)) { if (pseudo->selector()) { sass::vector> merged; sass::vector extended = extendPseudo(pseudo, extensions, mediaQueryContext); for (PseudoSelectorObj& extend : extended) { SimpleSelectorObj simple = extend; sass::vector result = extendWithoutPseudo(simple, extensions, targetsUsed); if (result.empty()) result = { extensionForSimple(extend) }; merged.push_back(result); } if (!extended.empty()) { return merged; } } } sass::vector result = extendWithoutPseudo(simple, extensions, targetsUsed); if (result.empty()) return {}; return { result }; } // extendSimple // ########################################################################## // Inner loop helper for [extendPseudo] function // ########################################################################## sass::vector Extender::extendPseudoComplex( const ComplexSelectorObj& complex, const PseudoSelectorObj& pseudo, const CssMediaRuleObj& mediaQueryContext) { if (complex->length() != 1) { return { complex }; } auto compound = Cast(complex->get(0)); if (compound == nullptr) { return { complex }; } if (compound->length() != 1) { return { complex }; } auto innerPseudo = Cast(compound->get(0)); if (innerPseudo == nullptr) { return { complex }; } if (!innerPseudo->selector()) { return { complex }; } sass::string name(pseudo->normalized()); if (name == "not") { // In theory, if there's a `:not` nested within another `:not`, the // inner `:not`'s contents should be unified with the return value. // For example, if `:not(.foo)` extends `.bar`, `:not(.bar)` should // become `.foo:not(.bar)`. However, this is a narrow edge case and // supporting it properly would make this code and the code calling it // a lot more complicated, so it's not supported for now. if (innerPseudo->normalized() != "matches") return {}; return innerPseudo->selector()->elements(); } else if (name == "matches" || name == "any" || name == "current" || name == "nth-child" || name == "nth-last-child") { // As above, we could theoretically support :not within :matches, but // doing so would require this method and its callers to handle much // more complex cases that likely aren't worth the pain. if (innerPseudo->name() != pseudo->name()) return {}; if (!ObjEquality()(innerPseudo->argument(), pseudo->argument())) return {}; return innerPseudo->selector()->elements(); } else if (name == "has" || name == "host" || name == "host-context" || name == "slotted") { // We can't expand nested selectors here, because each layer adds an // additional layer of semantics. For example, `:has(:has(img))` // doesn't match `
` but `:has(img)` does. return { complex }; } return {}; } // EO extendPseudoComplex // ########################################################################## // Extends [pseudo] using [extensions], and returns // a list of resulting pseudo selectors. // ########################################################################## sass::vector Extender::extendPseudo( const PseudoSelectorObj& pseudo, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext) { auto selector = pseudo->selector(); SelectorListObj extended = extendList( selector, extensions, mediaQueryContext); if (!extended || !pseudo || !pseudo->selector()) { return {}; } if (ObjEqualityFn(pseudo->selector(), extended)) { return {}; } // For `:not()`, we usually want to get rid of any complex selectors because // that will cause the selector to fail to parse on all browsers at time of // writing. We can keep them if either the original selector had a complex // selector, or the result of extending has only complex selectors, because // either way we aren't breaking anything that isn't already broken. sass::vector complexes = extended->elements(); if (pseudo->normalized() == "not") { if (!hasAny(pseudo->selector()->elements(), hasMoreThanOne)) { if (hasAny(extended->elements(), hasExactlyOne)) { complexes.clear(); for (auto& complex : extended->elements()) { if (complex->length() <= 1) { complexes.push_back(complex); } } } } } sass::vector expanded = expand( complexes, extendPseudoComplex, pseudo, mediaQueryContext); // Older browsers support `:not`, but only with a single complex selector. // In order to support those browsers, we break up the contents of a `:not` // unless it originally contained a selector list. if (pseudo->normalized() == "not") { if (pseudo->selector()->length() == 1) { sass::vector pseudos; for (size_t i = 0; i < expanded.size(); i += 1) { pseudos.push_back(pseudo->withSelector( expanded[i]->wrapInList() )); } return pseudos; } } SelectorListObj list = SASS_MEMORY_NEW(SelectorList, "[pseudo]"); list->concat(expanded); return { pseudo->withSelector(list) }; } // EO extendPseudo // ########################################################################## // Rotates the element in list from [start] (inclusive) to [end] (exclusive) // one index higher, looping the final element back to [start]. // ########################################################################## void Extender::rotateSlice( sass::vector& list, size_t start, size_t end) { auto element = list[end - 1]; for (size_t i = start; i < end; i++) { auto next = list[i]; list[i] = element; element = next; } } // EO rotateSlice // ########################################################################## // Removes elements from [selectors] if they're subselectors of other // elements. The [isOriginal] callback indicates which selectors are // original to the document, and thus should never be trimmed. // ########################################################################## // Note: for adaption I pass in the set directly, there is some // code path in selector-trim that might need this special callback // ########################################################################## sass::vector Extender::trim( const sass::vector& selectors, const ExtCplxSelSet& existing) const { // Avoid truly horrific quadratic behavior. // TODO(nweiz): I think there may be a way to get perfect trimming // without going quadratic by building some sort of trie-like // data structure that can be used to look up superselectors. // TODO(mgreter): Check how this performs in C++ (up the limit) if (selectors.size() > 100) return selectors; // This is n² on the sequences, but only comparing between separate sequences // should limit the quadratic behavior. We iterate from last to first and reverse // the result so that, if two selectors are identical, we keep the first one. sass::vector result; size_t numOriginals = 0; size_t i = selectors.size(); outer: // Use label to continue loop while (--i != sass::string::npos) { const ComplexSelectorObj& complex1 = selectors[i]; // Check if selector in known in existing "originals" // For custom behavior dart-sass had `isOriginal(complex1)` if (existing.find(complex1) != existing.end()) { // Make sure we don't include duplicate originals, which could // happen if a style rule extends a component of its own selector. for (size_t j = 0; j < numOriginals; j++) { if (ObjEqualityFn(result[j], complex1)) { rotateSlice(result, 0, j + 1); goto outer; } } result.insert(result.begin(), complex1); numOriginals++; continue; } // The maximum specificity of the sources that caused [complex1] // to be generated. In order for [complex1] to be removed, there // must be another selector that's a superselector of it *and* // that has specificity greater or equal to this. size_t maxSpecificity = 0; for (const SelectorComponentObj& component : complex1->elements()) { if (const CompoundSelectorObj compound = Cast(component)) { maxSpecificity = std::max(maxSpecificity, maxSourceSpecificity(compound)); } } // Look in [result] rather than [selectors] for selectors after [i]. This // ensures we aren't comparing against a selector that's already been trimmed, // and thus that if there are two identical selectors only one is trimmed. if (hasAny(result, dontTrimComplex, complex1, maxSpecificity)) { continue; } // Check if any element (up to [i]) from [selector] returns true // when passed to [dontTrimComplex]. The arguments [complex1] and // [maxSepcificity] will be passed to the invoked function. if (hasSubAny(selectors, i, dontTrimComplex, complex1, maxSpecificity)) { continue; } // ToDo: Maybe use deque for front insert? result.insert(result.begin(), complex1); } return result; } // EO trim // ########################################################################## // Returns the maximum specificity of the given [simple] source selector. // ########################################################################## size_t Extender::maxSourceSpecificity(const SimpleSelectorObj& simple) const { auto it = sourceSpecificity.find(simple); if (it == sourceSpecificity.end()) return 0; return it->second; } // EO maxSourceSpecificity(SimpleSelectorObj) // ########################################################################## // Returns the maximum specificity for sources that went into producing [compound]. // ########################################################################## size_t Extender::maxSourceSpecificity(const CompoundSelectorObj& compound) const { size_t specificity = 0; for (auto simple : compound->elements()) { size_t src = maxSourceSpecificity(simple); specificity = std::max(specificity, src); } return specificity; } // EO maxSourceSpecificity(CompoundSelectorObj) // ########################################################################## // Helper function used as callbacks on lists // ########################################################################## bool Extender::dontTrimComplex( const ComplexSelector* complex2, const ComplexSelector* complex1, const size_t maxSpecificity) { if (complex2->minSpecificity() < maxSpecificity) return false; return complex2->isSuperselectorOf(complex1); } // EO dontTrimComplex // ########################################################################## // Helper function used as callbacks on lists // ########################################################################## bool Extender::hasExactlyOne(const ComplexSelectorObj& vec) { return vec->length() == 1; } // EO hasExactlyOne // ########################################################################## // Helper function used as callbacks on lists // ########################################################################## bool Extender::hasMoreThanOne(const ComplexSelectorObj& vec) { return vec->length() > 1; } // hasMoreThanOne } golibsass-1.0.0/libsass_src/src/extender.hpp000066400000000000000000000424041405214413600211270ustar00rootroot00000000000000#ifndef SASS_EXTENDER_H #define SASS_EXTENDER_H #include #include #include #include "ast_helpers.hpp" #include "ast_fwd_decl.hpp" #include "operation.hpp" #include "extension.hpp" #include "backtrace.hpp" #include "ordered_map.hpp" namespace Sass { // ########################################################################## // Different hash map types used by extender // ########################################################################## // This is special (ptrs!) typedef std::unordered_set< ComplexSelectorObj, ObjPtrHash, ObjPtrEquality > ExtCplxSelSet; typedef std::unordered_set< SimpleSelectorObj, ObjHash, ObjEquality > ExtSmplSelSet; typedef std::unordered_set< SelectorListObj, ObjPtrHash, ObjPtrEquality > ExtListSelSet; typedef std::unordered_map< SimpleSelectorObj, ExtListSelSet, ObjHash, ObjEquality > ExtSelMap; typedef ordered_map< ComplexSelectorObj, Extension, ObjHash, ObjEquality > ExtSelExtMapEntry; typedef std::unordered_map< SimpleSelectorObj, ExtSelExtMapEntry, ObjHash, ObjEquality > ExtSelExtMap; typedef std::unordered_map < SimpleSelectorObj, sass::vector< Extension >, ObjHash, ObjEquality > ExtByExtMap; class Extender : public Operation_CRTP { public: enum ExtendMode { TARGETS, REPLACE, NORMAL, }; private: // ########################################################################## // The mode that controls this extender's behavior. // ########################################################################## ExtendMode mode; // ########################################################################## // Shared backtraces with context and expander. Needed the throw // errors when e.g. extending across media query boundaries. // ########################################################################## Backtraces& traces; // ########################################################################## // A map from all simple selectors in the stylesheet to the rules that // contain them.This is used to find which rules an `@extend` applies to. // ########################################################################## ExtSelMap selectors; // ########################################################################## // A map from all extended simple selectors // to the sources of those extensions. // ########################################################################## ExtSelExtMap extensions; // ########################################################################## // A map from all simple selectors in extenders to // the extensions that those extenders define. // ########################################################################## ExtByExtMap extensionsByExtender; // ########################################################################## // A map from CSS rules to the media query contexts they're defined in. // This tracks the contexts in which each style rule is defined. // If a rule is defined at the top level, it doesn't have an entry. // ########################################################################## ordered_map< SelectorListObj, CssMediaRuleObj, ObjPtrHash, ObjPtrEquality > mediaContexts; // ########################################################################## // A map from [SimpleSelector]s to the specificity of their source selectors. // This tracks the maximum specificity of the [ComplexSelector] that originally // contained each [SimpleSelector]. This allows us to ensure we don't trim any // selectors that need to exist to satisfy the [second law that of extend][]. // [second law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184 // ########################################################################## std::unordered_map< SimpleSelectorObj, size_t, ObjPtrHash, ObjPtrEquality > sourceSpecificity; // ########################################################################## // A set of [ComplexSelector]s that were originally part of their // component [SelectorList]s, as opposed to being added by `@extend`. // This allows us to ensure that we don't trim any selectors // that need to exist to satisfy the [first law of extend][]. // ########################################################################## ExtCplxSelSet originals; public: // Constructor without default [mode]. // [traces] are needed to throw errors. Extender(Backtraces& traces); // ########################################################################## // Constructor with specific [mode]. // [traces] are needed to throw errors. // ########################################################################## Extender(ExtendMode mode, Backtraces& traces); // ########################################################################## // Empty desctructor // ########################################################################## ~Extender() {}; // ########################################################################## // Extends [selector] with [source] extender and [targets] extendees. // This works as though `source {@extend target}` were written in the // stylesheet, with the exception that [target] can contain compound // selectors which must be extended as a unit. // ########################################################################## static SelectorListObj extend( SelectorListObj& selector, const SelectorListObj& source, const SelectorListObj& target, Backtraces& traces); // ########################################################################## // Returns a copy of [selector] with [targets] replaced by [source]. // ########################################################################## static SelectorListObj replace( SelectorListObj& selector, const SelectorListObj& source, const SelectorListObj& target, Backtraces& traces); // ########################################################################## // Adds [selector] to this extender, with [selectorSpan] as the span covering // the selector and [ruleSpan] as the span covering the entire style rule. // Extends [selector] using any registered extensions, then returns an empty // [ModifiableCssStyleRule] with the resulting selector. If any more relevant // extensions are added, the returned rule is automatically updated. // The [mediaContext] is the media query context in which the selector was // defined, or `null` if it was defined at the top level of the document. // ########################################################################## void addSelector( const SelectorListObj& selector, const CssMediaRuleObj& mediaContext); // ########################################################################## // Registers the [SimpleSelector]s in [list] // to point to [rule] in [selectors]. // ########################################################################## void registerSelector( const SelectorListObj& list, const SelectorListObj& rule); // ########################################################################## // Adds an extension to this extender. The [extender] is the selector for the // style rule in which the extension is defined, and [target] is the selector // passed to `@extend`. The [extend] provides the extend span and indicates // whether the extension is optional. The [mediaContext] defines the media query // context in which the extension is defined. It can only extend selectors // within the same context. A `null` context indicates no media queries. // ########################################################################## void addExtension( const SelectorListObj& extender, const SimpleSelectorObj& target, const CssMediaRuleObj& mediaQueryContext, bool is_optional = false); // ########################################################################## // The set of all simple selectors in style rules handled // by this extender. This includes simple selectors that // were added because of downstream extensions. // ########################################################################## ExtSmplSelSet getSimpleSelectors() const; // ########################################################################## // Check for extends that have not been satisfied. // Returns true if any non-optional extension did not // extend any selector. Updates the passed reference // to point to that Extension for further analysis. // ########################################################################## bool checkForUnsatisfiedExtends( Extension& unsatisfied) const; private: // ########################################################################## // A helper function for [extend] and [replace]. // ########################################################################## static SelectorListObj extendOrReplace( SelectorListObj& selector, const SelectorListObj& source, const SelectorListObj& target, const ExtendMode mode, Backtraces& traces); // ########################################################################## // Returns an extension that combines [left] and [right]. Throws // a [SassException] if [left] and [right] have incompatible // media contexts. Throws an [ArgumentError] if [left] // and [right] don't have the same extender and target. // ########################################################################## static Extension mergeExtension( const Extension& lhs, const Extension& rhs); // ########################################################################## // Extend [extensions] using [newExtensions]. // ########################################################################## // Note: dart-sass throws an error in here // ########################################################################## void extendExistingStyleRules( const ExtListSelSet& rules, const ExtSelExtMap& newExtensions); // ########################################################################## // Extend [extensions] using [newExtensions]. Note that this does duplicate // some work done by [_extendExistingStyleRules], but it's necessary to // expand each extension's extender separately without reference to the full // selector list, so that relevant results don't get trimmed too early. // Returns `null` (Note: empty map) if there are no extensions to add. // ########################################################################## ExtSelExtMap extendExistingExtensions( // Taking in a reference here makes MSVC debug stuck!? const sass::vector& extensions, const ExtSelExtMap& newExtensions); // ########################################################################## // Extends [list] using [extensions]. // ########################################################################## SelectorListObj extendList( const SelectorListObj& list, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaContext); // ########################################################################## // Extends [complex] using [extensions], and // returns the contents of a [SelectorList]. // ########################################################################## sass::vector extendComplex( // Taking in a reference here makes MSVC debug stuck!? const ComplexSelectorObj& list, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext); // ########################################################################## // Returns a one-off [Extension] whose // extender is composed solely of [simple]. // ########################################################################## Extension extensionForSimple( const SimpleSelectorObj& simple) const; // ########################################################################## // Returns a one-off [Extension] whose extender is composed // solely of a compound selector containing [simples]. // ########################################################################## Extension extensionForCompound( // Taking in a reference here makes MSVC debug stuck!? const sass::vector& simples) const; // ########################################################################## // Extends [compound] using [extensions], and returns the // contents of a [SelectorList]. The [inOriginal] parameter // indicates whether this is in an original complex selector, // meaning that [compound] should not be trimmed out. // ########################################################################## sass::vector extendCompound( const CompoundSelectorObj& compound, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext, bool inOriginal = false); // ########################################################################## // Extends [simple] without extending the // contents of any selector pseudos it contains. // ########################################################################## sass::vector extendWithoutPseudo( const SimpleSelectorObj& simple, const ExtSelExtMap& extensions, ExtSmplSelSet* targetsUsed) const; // ########################################################################## // Extends [simple] and also extending the // contents of any selector pseudos it contains. // ########################################################################## sass::vector> extendSimple( const SimpleSelectorObj& simple, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext, ExtSmplSelSet* targetsUsed); // ########################################################################## // Inner loop helper for [extendPseudo] function // ########################################################################## static sass::vector extendPseudoComplex( const ComplexSelectorObj& complex, const PseudoSelectorObj& pseudo, const CssMediaRuleObj& mediaQueryContext); // ########################################################################## // Extends [pseudo] using [extensions], and returns // a list of resulting pseudo selectors. // ########################################################################## sass::vector extendPseudo( const PseudoSelectorObj& pseudo, const ExtSelExtMap& extensions, const CssMediaRuleObj& mediaQueryContext); // ########################################################################## // Rotates the element in list from [start] (inclusive) to [end] (exclusive) // one index higher, looping the final element back to [start]. // ########################################################################## static void rotateSlice( sass::vector& list, size_t start, size_t end); // ########################################################################## // Removes elements from [selectors] if they're subselectors of other // elements. The [isOriginal] callback indicates which selectors are // original to the document, and thus should never be trimmed. // ########################################################################## sass::vector trim( const sass::vector& selectors, const ExtCplxSelSet& set) const; // ########################################################################## // Returns the maximum specificity of the given [simple] source selector. // ########################################################################## size_t maxSourceSpecificity(const SimpleSelectorObj& simple) const; // ########################################################################## // Returns the maximum specificity for sources that went into producing [compound]. // ########################################################################## size_t maxSourceSpecificity(const CompoundSelectorObj& compound) const; // ########################################################################## // Helper function used as callbacks on lists // ########################################################################## static bool dontTrimComplex( const ComplexSelector* complex2, const ComplexSelector* complex1, const size_t maxSpecificity); // ########################################################################## // Helper function used as callbacks on lists // ########################################################################## static bool hasExactlyOne(const ComplexSelectorObj& vec); static bool hasMoreThanOne(const ComplexSelectorObj& vec); }; } #endif golibsass-1.0.0/libsass_src/src/extension.cpp000066400000000000000000000027661405214413600213270ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast_helpers.hpp" #include "extension.hpp" #include "ast.hpp" namespace Sass { // ########################################################################## // Static function to create a copy with a new extender // ########################################################################## Extension Extension::withExtender(const ComplexSelectorObj& newExtender) const { Extension extension(newExtender); extension.specificity = specificity; extension.isOptional = isOptional; extension.target = target; return extension; } // ########################################################################## // Asserts that the [mediaContext] for a selector is // compatible with the query context for this extender. // ########################################################################## void Extension::assertCompatibleMediaContext(CssMediaRuleObj mediaQueryContext, Backtraces& traces) const { if (this->mediaContext.isNull()) return; if (mediaQueryContext && ObjPtrEqualityFn(mediaContext->block(), mediaQueryContext->block())) return; if (ObjEqualityFn(mediaQueryContext, mediaContext)) return; throw Exception::ExtendAcrossMedia(traces, *this); } // ########################################################################## // ########################################################################## } golibsass-1.0.0/libsass_src/src/extension.hpp000066400000000000000000000043741405214413600213310ustar00rootroot00000000000000#ifndef SASS_EXTENSION_H #define SASS_EXTENSION_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "ast_fwd_decl.hpp" #include "backtrace.hpp" namespace Sass { class Extension { public: // The selector in which the `@extend` appeared. ComplexSelectorObj extender; // The selector that's being extended. // `null` for one-off extensions. SimpleSelectorObj target; // The minimum specificity required for any // selector generated from this extender. size_t specificity; // Whether this extension is optional. bool isOptional; // Whether this is a one-off extender representing a selector that was // originally in the document, rather than one defined with `@extend`. bool isOriginal; bool isSatisfied; // The media query context to which this extend is restricted, // or `null` if it can apply within any context. CssMediaRuleObj mediaContext; // Creates a one-off extension that's not intended to be modified over time. // If [specificity] isn't passed, it defaults to `extender.maxSpecificity`. Extension(ComplexSelectorObj extender) : extender(extender), target({}), specificity(0), isOptional(true), isOriginal(false), isSatisfied(false), mediaContext({}) { } // Copy constructor Extension(const Extension& extension) : extender(extension.extender), target(extension.target), specificity(extension.specificity), isOptional(extension.isOptional), isOriginal(extension.isOriginal), isSatisfied(extension.isSatisfied), mediaContext(extension.mediaContext) { } // Default constructor Extension() : extender({}), target({}), specificity(0), isOptional(false), isOriginal(false), isSatisfied(false), mediaContext({}) { } // Asserts that the [mediaContext] for a selector is // compatible with the query context for this extender. void assertCompatibleMediaContext(CssMediaRuleObj mediaContext, Backtraces& traces) const; Extension withExtender(const ComplexSelectorObj& newExtender) const; }; } #endif golibsass-1.0.0/libsass_src/src/file.cpp000066400000000000000000000464721405214413600202340ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #ifdef _WIN32 # ifdef __MINGW32__ # ifndef off64_t # define off64_t _off64_t /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */ # endif # endif # include # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #else # include #endif #include #include #include #include #include "file.hpp" #include "context.hpp" #include "prelexer.hpp" #include "utf8_string.hpp" #include "sass_functions.hpp" #include "error_handling.hpp" #include "util.hpp" #include "util_string.hpp" #include "sass2scss.h" #ifdef _WIN32 # include # ifdef _MSC_VER # include inline static Sass::sass::string wstring_to_string(const std::wstring& wstr) { std::wstring_convert, wchar_t> wchar_converter; return wchar_converter.to_bytes(wstr); } # else // mingw(/gcc) does not support C++11's codecvt yet. inline static Sass::sass::string wstring_to_string(const std::wstring &wstr) { int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); Sass::sass::string strTo(size_needed, 0); WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); return strTo; } # endif #endif namespace Sass { namespace File { // return the current directory // always with forward slashes // always with trailing slash sass::string get_cwd() { const size_t wd_len = 4096; #ifndef _WIN32 char wd[wd_len]; char* pwd = getcwd(wd, wd_len); // we should check error for more detailed info (e.g. ENOENT) // http://man7.org/linux/man-pages/man2/getcwd.2.html#ERRORS if (pwd == NULL) throw Exception::OperationError("cwd gone missing"); sass::string cwd = pwd; #else wchar_t wd[wd_len]; wchar_t* pwd = _wgetcwd(wd, wd_len); if (pwd == NULL) throw Exception::OperationError("cwd gone missing"); sass::string cwd = wstring_to_string(pwd); //convert backslashes to forward slashes replace(cwd.begin(), cwd.end(), '\\', '/'); #endif if (cwd[cwd.length() - 1] != '/') cwd += '/'; return cwd; } // test if path exists and is a file bool file_exists(const sass::string& path) { #ifdef _WIN32 wchar_t resolved[32768]; // windows unicode filepaths are encoded in utf16 sass::string abspath(join_paths(get_cwd(), path)); if (!(abspath[0] == '/' && abspath[1] == '/')) { abspath = "//?/" + abspath; } std::wstring wpath(UTF_8::convert_to_utf16(abspath)); std::replace(wpath.begin(), wpath.end(), '/', '\\'); DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL); if (rv > 32767) throw Exception::OperationError("Path is too long"); if (rv == 0) throw Exception::OperationError("Path could not be resolved"); DWORD dwAttrib = GetFileAttributesW(resolved); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))); #else struct stat st_buf; return (stat (path.c_str(), &st_buf) == 0) && (!S_ISDIR (st_buf.st_mode)); #endif } // return if given path is absolute // works with *nix and windows paths bool is_absolute_path(const sass::string& path) { #ifdef _WIN32 if (path.length() >= 2 && Util::ascii_isalpha(path[0]) && path[1] == ':') return true; #endif size_t i = 0; // check if we have a protocol if (path[i] && Util::ascii_isalpha(static_cast(path[i]))) { // skip over all alphanumeric characters while (path[i] && Util::ascii_isalnum(static_cast(path[i]))) ++i; i = i && path[i] == ':' ? i + 1 : 0; } return path[i] == '/'; } // helper function to find the last directory separator inline size_t find_last_folder_separator(const sass::string& path, size_t limit = sass::string::npos) { size_t pos; size_t pos_p = path.find_last_of('/', limit); #ifdef _WIN32 size_t pos_w = path.find_last_of('\\', limit); #else size_t pos_w = sass::string::npos; #endif if (pos_p != sass::string::npos && pos_w != sass::string::npos) { pos = std::max(pos_p, pos_w); } else if (pos_p != sass::string::npos) { pos = pos_p; } else { pos = pos_w; } return pos; } // return only the directory part of path sass::string dir_name(const sass::string& path) { size_t pos = find_last_folder_separator(path); if (pos == sass::string::npos) return ""; else return path.substr(0, pos+1); } // return only the filename part of path sass::string base_name(const sass::string& path) { size_t pos = find_last_folder_separator(path); if (pos == sass::string::npos) return path; else return path.substr(pos+1); } // do a logical clean up of the path // no physical check on the filesystem sass::string make_canonical_path (sass::string path) { // declarations size_t pos; #ifdef _WIN32 //convert backslashes to forward slashes replace(path.begin(), path.end(), '\\', '/'); #endif pos = 0; // remove all self references inside the path string while((pos = path.find("/./", pos)) != sass::string::npos) path.erase(pos, 2); // remove all leading and trailing self references while(path.size() >= 2 && path[0] == '.' && path[1] == '/') path.erase(0, 2); while((pos = path.length()) > 1 && path[pos - 2] == '/' && path[pos - 1] == '.') path.erase(pos - 2); size_t proto = 0; // check if we have a protocol if (path[proto] && Util::ascii_isalpha(static_cast(path[proto]))) { // skip over all alphanumeric characters while (path[proto] && Util::ascii_isalnum(static_cast(path[proto++]))) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; } // then skip over start slashes while (path[proto++] == '/') {} pos = proto; // collapse multiple delimiters into a single one while((pos = path.find("//", pos)) != sass::string::npos) path.erase(pos, 1); return path; } // join two path segments cleanly together // but only if right side is not absolute yet sass::string join_paths(sass::string l, sass::string r) { #ifdef _WIN32 // convert Windows backslashes to URL forward slashes replace(l.begin(), l.end(), '\\', '/'); replace(r.begin(), r.end(), '\\', '/'); #endif if (l.empty()) return r; if (r.empty()) return l; if (is_absolute_path(r)) return r; if (l[l.length()-1] != '/') l += '/'; // this does a logical cleanup of the right hand path // Note that this does collapse x/../y sections into y. // This is by design. If /foo on your system is a symlink // to /bar/baz, then /foo/../cd is actually /bar/cd, // not /cd as a naive ../ removal would give you. // will only work on leading double dot dirs on rhs // therefore it is safe if lhs is already resolved cwd while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) { size_t L = l.length(), pos = find_last_folder_separator(l, L - 2); bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\'); bool is_self = pos + 3 == L && (l[pos+1] == '.'); if (!is_self && !is_slash) r = r.substr(3); else if (pos == sass::string::npos) break; l = l.substr(0, pos == sass::string::npos ? pos : pos + 1); } return l + r; } sass::string path_for_console(const sass::string& rel_path, const sass::string& abs_path, const sass::string& orig_path) { // magic algorithm goes here!! // if the file is outside this directory show the absolute path if (rel_path.substr(0, 3) == "../") { return orig_path; } // this seems to work most of the time return abs_path == orig_path ? abs_path : rel_path; } // create an absolute path by resolving relative paths with cwd sass::string rel2abs(const sass::string& path, const sass::string& base, const sass::string& cwd) { sass::string rv = make_canonical_path(join_paths(join_paths(cwd + "/", base + "/"), path)); #ifdef _WIN32 // On windows we may get an absolute path without directory // In that case we should prepend the directory from the root if (rv[0] == '/' && rv[1] != '/') { rv.insert(0, cwd, 0, 2); } #endif return rv; } // create a path that is relative to the given base directory // path and base will first be resolved against cwd to make them absolute sass::string abs2rel(const sass::string& path, const sass::string& base, const sass::string& cwd) { sass::string abs_path = rel2abs(path, cwd); sass::string abs_base = rel2abs(base, cwd); size_t proto = 0; // check if we have a protocol if (path[proto] && Util::ascii_isalpha(static_cast(path[proto]))) { // skip over all alphanumeric characters while (path[proto] && Util::ascii_isalnum(static_cast(path[proto++]))) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; } // distinguish between windows absolute paths and valid protocols // we assume that protocols must at least have two chars to be valid if (proto && path[proto++] == '/' && proto > 3) return path; #ifdef _WIN32 // absolute link must have a drive letter, and we know that we // can only create relative links if both are on the same drive if (abs_base[0] != abs_path[0]) return abs_path; #endif sass::string stripped_uri = ""; sass::string stripped_base = ""; size_t index = 0; size_t minSize = std::min(abs_path.size(), abs_base.size()); for (size_t i = 0; i < minSize; ++i) { #ifdef FS_CASE_SENSITIVE if (abs_path[i] != abs_base[i]) break; #else // compare the charactes in a case insensitive manner // windows fs is only case insensitive in ascii ranges if (Util::ascii_tolower(static_cast(abs_path[i])) != Util::ascii_tolower(static_cast(abs_base[i]))) break; #endif if (abs_path[i] == '/') index = i + 1; } for (size_t i = index; i < abs_path.size(); ++i) { stripped_uri += abs_path[i]; } for (size_t i = index; i < abs_base.size(); ++i) { stripped_base += abs_base[i]; } size_t left = 0; size_t directories = 0; for (size_t right = 0; right < stripped_base.size(); ++right) { if (stripped_base[right] == '/') { if (stripped_base.substr(left, 2) != "..") { ++directories; } else if (directories > 1) { --directories; } else { directories = 0; } left = right + 1; } } sass::string result = ""; for (size_t i = 0; i < directories; ++i) { result += "../"; } result += stripped_uri; return result; } // Resolution order for ambiguous imports: // (1) filename as given // (2) underscore + given // (3) underscore + given + extension // (4) given + extension // (5) given + _index.scss // (6) given + _index.sass sass::vector resolve_includes(const sass::string& root, const sass::string& file, const sass::vector& exts) { sass::string filename = join_paths(root, file); // split the filename sass::string base(dir_name(file)); sass::string name(base_name(file)); sass::vector includes; // create full path (maybe relative) sass::string rel_path(join_paths(base, name)); sass::string abs_path(join_paths(root, rel_path)); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test variation with underscore rel_path = join_paths(base, "_" + name); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test exts plus underscore for(auto ext : exts) { rel_path = join_paths(base, "_" + name + ext); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // next test plain name with exts for(auto ext : exts) { rel_path = join_paths(base, name + ext); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // index files if (includes.size() == 0) { // ignore directories that look like @import'able filename for(auto ext : exts) { if (ends_with(name, ext)) return includes; } // next test underscore index exts for(auto ext : exts) { rel_path = join_paths(base, join_paths(name, "_index" + ext)); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // next test plain index exts for(auto ext : exts) { rel_path = join_paths(base, join_paths(name, "index" + ext)); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } } // nothing found return includes; } sass::vector find_files(const sass::string& file, const sass::vector paths) { sass::vector includes; for (sass::string path : paths) { sass::string abs_path(join_paths(path, file)); if (file_exists(abs_path)) includes.push_back(abs_path); } return includes; } sass::vector find_files(const sass::string& file, struct Sass_Compiler* compiler) { // get the last import entry to get current base directory // struct Sass_Options* options = sass_compiler_get_options(compiler); Sass_Import_Entry import = sass_compiler_get_last_import(compiler); const sass::vector& incs = compiler->cpp_ctx->include_paths; // create the vector with paths to lookup sass::vector paths(1 + incs.size()); paths.push_back(dir_name(import->abs_path)); paths.insert(paths.end(), incs.begin(), incs.end()); // dispatch to find files in paths return find_files(file, paths); } // helper function to search one file in all include paths // this is normally not used internally by libsass (C-API sugar) sass::string find_file(const sass::string& file, const sass::vector paths) { if (file.empty()) return file; auto res = find_files(file, paths); return res.empty() ? "" : res.front(); } // helper function to resolve a filename sass::string find_include(const sass::string& file, const sass::vector paths) { // search in every include path for a match for (size_t i = 0, S = paths.size(); i < S; ++i) { sass::vector resolved(resolve_includes(paths[i], file)); if (resolved.size()) return resolved[0].abs_path; } // nothing found return sass::string(""); } // try to load the given filename // returned memory must be freed // will auto convert .sass files char* read_file(const sass::string& path) { #ifdef _WIN32 BYTE* pBuffer; DWORD dwBytes; wchar_t resolved[32768]; // windows unicode filepaths are encoded in utf16 sass::string abspath(join_paths(get_cwd(), path)); if (!(abspath[0] == '/' && abspath[1] == '/')) { abspath = "//?/" + abspath; } std::wstring wpath(UTF_8::convert_to_utf16(abspath)); std::replace(wpath.begin(), wpath.end(), '/', '\\'); DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL); if (rv > 32767) throw Exception::OperationError("Path is too long"); if (rv == 0) throw Exception::OperationError("Path could not be resolved"); HANDLE hFile = CreateFileW(resolved, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; DWORD dwFileLength = GetFileSize(hFile, NULL); if (dwFileLength == INVALID_FILE_SIZE) return 0; // allocate an extra byte for the null char // and another one for edge-cases in lexer pBuffer = (BYTE*)malloc((dwFileLength+2)*sizeof(BYTE)); ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL); pBuffer[dwFileLength+0] = '\0'; pBuffer[dwFileLength+1] = '\0'; CloseHandle(hFile); // just convert from unsigned char* char* contents = (char*) pBuffer; #else // Read the file using `` instead of `` for better portability. // The `` header initializes `` and this buggy in GCC4/5 with static linking. // See: // https://www.spinics.net/lists/gcchelp/msg46851.html // https://github.com/sass/sassc-ruby/issues/128 struct stat st; if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0; FILE* fd = std::fopen(path.c_str(), "rb"); if (fd == nullptr) return nullptr; const std::size_t size = st.st_size; char* contents = static_cast(malloc(st.st_size + 2 * sizeof(char))); if (std::fread(static_cast(contents), 1, size, fd) != size) { free(contents); std::fclose(fd); return nullptr; } if (std::fclose(fd) != 0) { free(contents); return nullptr; } contents[size] = '\0'; contents[size + 1] = '\0'; #endif sass::string extension; if (path.length() > 5) { extension = path.substr(path.length() - 5, 5); } Util::ascii_str_tolower(&extension); if (extension == ".sass" && contents != 0) { char * converted = sass2scss(contents, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); free(contents); // free the indented contents return converted; // should be freed by caller } else { return contents; } } // split a path string delimited by semicolons or colons (OS dependent) sass::vector split_path_list(const char* str) { sass::vector paths; if (str == NULL) return paths; // find delimiter via prelexer (return zero at end) const char* end = Prelexer::find_first(str); // search until null delimiter while (end) { // add path from current position to delimiter paths.push_back(sass::string(str, end - str)); str = end + 1; // skip delimiter end = Prelexer::find_first(str); } // add path from current position to end paths.push_back(sass::string(str)); // return back return paths; } } } golibsass-1.0.0/libsass_src/src/file.hpp000066400000000000000000000074531405214413600202350ustar00rootroot00000000000000#ifndef SASS_FILE_H #define SASS_FILE_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "sass/context.h" #include "ast_fwd_decl.hpp" namespace Sass { namespace File { // return the current directory // always with forward slashes sass::string get_cwd(); // test if path exists and is a file bool file_exists(const sass::string& file); // return if given path is absolute // works with *nix and windows paths bool is_absolute_path(const sass::string& path); // return only the directory part of path sass::string dir_name(const sass::string& path); // return only the filename part of path sass::string base_name(const sass::string&); // do a locigal clean up of the path // no physical check on the filesystem sass::string make_canonical_path (sass::string path); // join two path segments cleanly together // but only if right side is not absolute yet sass::string join_paths(sass::string root, sass::string name); // if the relative path is outside of the cwd we want want to // show the absolute path in console messages sass::string path_for_console(const sass::string& rel_path, const sass::string& abs_path, const sass::string& orig_path); // create an absolute path by resolving relative paths with cwd sass::string rel2abs(const sass::string& path, const sass::string& base = ".", const sass::string& cwd = get_cwd()); // create a path that is relative to the given base directory // path and base will first be resolved against cwd to make them absolute sass::string abs2rel(const sass::string& path, const sass::string& base = ".", const sass::string& cwd = get_cwd()); // helper function to resolve a filename // searching without variations in all paths sass::string find_file(const sass::string& file, struct Sass_Compiler* options); sass::string find_file(const sass::string& file, const sass::vector paths); // helper function to resolve a include filename // this has the original resolve logic for sass include sass::string find_include(const sass::string& file, const sass::vector paths); // split a path string delimited by semicolons or colons (OS dependent) sass::vector split_path_list(const char* paths); // try to load the given filename // returned memory must be freed // will auto convert .sass files char* read_file(const sass::string& file); } // requested import class Importer { public: // requested import path sass::string imp_path; // parent context path sass::string ctx_path; // base derived from context path // this really just acts as a cache sass::string base_path; public: Importer(sass::string imp_path, sass::string ctx_path) : imp_path(File::make_canonical_path(imp_path)), ctx_path(File::make_canonical_path(ctx_path)), base_path(File::dir_name(ctx_path)) { } }; // a resolved include (final import) class Include : public Importer { public: // resolved absolute path sass::string abs_path; public: Include(const Importer& imp, sass::string abs_path) : Importer(imp), abs_path(abs_path) { } }; // a loaded resource class Resource { public: // the file contents char* contents; // connected sourcemap char* srcmap; public: Resource(char* contents, char* srcmap) : contents(contents), srcmap(srcmap) { } }; namespace File { sass::vector resolve_includes(const sass::string& root, const sass::string& file, const sass::vector& exts = { ".scss", ".sass", ".css" }); } } #endif golibsass-1.0.0/libsass_src/src/fn_colors.cpp000066400000000000000000000522661405214413600212770ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include "ast.hpp" #include "fn_utils.hpp" #include "fn_colors.hpp" #include "util.hpp" #include "util_string.hpp" namespace Sass { namespace Functions { bool string_argument(AST_Node_Obj obj) { String_Constant* s = Cast(obj); if (s == nullptr) return false; const sass::string& str = s->value(); return starts_with(str, "calc(") || starts_with(str, "var("); } void hsla_alpha_percent_deprecation(const SourceSpan& pstate, const sass::string val) { sass::string msg("Passing a percentage as the alpha value to hsla() will be interpreted"); sass::string tail("differently in future versions of Sass. For now, use " + val + " instead."); deprecated(msg, tail, false, pstate); } Signature rgb_sig = "rgb($red, $green, $blue)"; BUILT_IN(rgb) { if ( string_argument(env["$red"]) || string_argument(env["$green"]) || string_argument(env["$blue"]) ) { return SASS_MEMORY_NEW(String_Constant, pstate, "rgb(" + env["$red"]->to_string() + ", " + env["$green"]->to_string() + ", " + env["$blue"]->to_string() + ")" ); } return SASS_MEMORY_NEW(Color_RGBA, pstate, COLOR_NUM("$red"), COLOR_NUM("$green"), COLOR_NUM("$blue")); } Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; BUILT_IN(rgba_4) { if ( string_argument(env["$red"]) || string_argument(env["$green"]) || string_argument(env["$blue"]) || string_argument(env["$alpha"]) ) { return SASS_MEMORY_NEW(String_Constant, pstate, "rgba(" + env["$red"]->to_string() + ", " + env["$green"]->to_string() + ", " + env["$blue"]->to_string() + ", " + env["$alpha"]->to_string() + ")" ); } return SASS_MEMORY_NEW(Color_RGBA, pstate, COLOR_NUM("$red"), COLOR_NUM("$green"), COLOR_NUM("$blue"), ALPHA_NUM("$alpha")); } Signature rgba_2_sig = "rgba($color, $alpha)"; BUILT_IN(rgba_2) { if ( string_argument(env["$color"]) ) { return SASS_MEMORY_NEW(String_Constant, pstate, "rgba(" + env["$color"]->to_string() + ", " + env["$alpha"]->to_string() + ")" ); } Color_RGBA_Obj c_arg = ARG("$color", Color)->toRGBA(); if ( string_argument(env["$alpha"]) ) { sass::ostream strm; strm << "rgba(" << (int)c_arg->r() << ", " << (int)c_arg->g() << ", " << (int)c_arg->b() << ", " << env["$alpha"]->to_string() << ")"; return SASS_MEMORY_NEW(String_Constant, pstate, strm.str()); } Color_RGBA_Obj new_c = SASS_MEMORY_COPY(c_arg); new_c->a(ALPHA_NUM("$alpha")); new_c->disp(""); return new_c.detach(); } //////////////// // RGB FUNCTIONS //////////////// Signature red_sig = "red($color)"; BUILT_IN(red) { Color_RGBA_Obj color = ARG("$color", Color)->toRGBA(); return SASS_MEMORY_NEW(Number, pstate, color->r()); } Signature green_sig = "green($color)"; BUILT_IN(green) { Color_RGBA_Obj color = ARG("$color", Color)->toRGBA(); return SASS_MEMORY_NEW(Number, pstate, color->g()); } Signature blue_sig = "blue($color)"; BUILT_IN(blue) { Color_RGBA_Obj color = ARG("$color", Color)->toRGBA(); return SASS_MEMORY_NEW(Number, pstate, color->b()); } Color_RGBA* colormix(Context& ctx, SourceSpan& pstate, Color* color1, Color* color2, double weight) { Color_RGBA_Obj c1 = color1->toRGBA(); Color_RGBA_Obj c2 = color2->toRGBA(); double p = weight/100; double w = 2*p - 1; double a = c1->a() - c2->a(); double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0; double w2 = 1 - w1; return SASS_MEMORY_NEW(Color_RGBA, pstate, Sass::round(w1*c1->r() + w2*c2->r(), ctx.c_options.precision), Sass::round(w1*c1->g() + w2*c2->g(), ctx.c_options.precision), Sass::round(w1*c1->b() + w2*c2->b(), ctx.c_options.precision), c1->a()*p + c2->a()*(1-p)); } Signature mix_sig = "mix($color1, $color2, $weight: 50%)"; BUILT_IN(mix) { Color_Obj color1 = ARG("$color1", Color); Color_Obj color2 = ARG("$color2", Color); double weight = DARG_U_PRCT("$weight"); return colormix(ctx, pstate, color1, color2, weight); } //////////////// // HSL FUNCTIONS //////////////// Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; BUILT_IN(hsl) { if ( string_argument(env["$hue"]) || string_argument(env["$saturation"]) || string_argument(env["$lightness"]) ) { return SASS_MEMORY_NEW(String_Constant, pstate, "hsl(" + env["$hue"]->to_string() + ", " + env["$saturation"]->to_string() + ", " + env["$lightness"]->to_string() + ")" ); } return SASS_MEMORY_NEW(Color_HSLA, pstate, ARGVAL("$hue"), ARGVAL("$saturation"), ARGVAL("$lightness"), 1.0); } Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; BUILT_IN(hsla) { if ( string_argument(env["$hue"]) || string_argument(env["$saturation"]) || string_argument(env["$lightness"]) || string_argument(env["$alpha"]) ) { return SASS_MEMORY_NEW(String_Constant, pstate, "hsla(" + env["$hue"]->to_string() + ", " + env["$saturation"]->to_string() + ", " + env["$lightness"]->to_string() + ", " + env["$alpha"]->to_string() + ")" ); } Number* alpha = ARG("$alpha", Number); if (alpha && alpha->unit() == "%") { Number_Obj val = SASS_MEMORY_COPY(alpha); val->numerators.clear(); // convert val->value(val->value() / 100.0); sass::string nr(val->to_string(ctx.c_options)); hsla_alpha_percent_deprecation(pstate, nr); } return SASS_MEMORY_NEW(Color_HSLA, pstate, ARGVAL("$hue"), ARGVAL("$saturation"), ARGVAL("$lightness"), ARGVAL("$alpha")); } ///////////////////////////////////////////////////////////////////////// // Query functions ///////////////////////////////////////////////////////////////////////// Signature hue_sig = "hue($color)"; BUILT_IN(hue) { Color_HSLA_Obj col = ARG("$color", Color)->toHSLA(); return SASS_MEMORY_NEW(Number, pstate, col->h(), "deg"); } Signature saturation_sig = "saturation($color)"; BUILT_IN(saturation) { Color_HSLA_Obj col = ARG("$color", Color)->toHSLA(); return SASS_MEMORY_NEW(Number, pstate, col->s(), "%"); } Signature lightness_sig = "lightness($color)"; BUILT_IN(lightness) { Color_HSLA_Obj col = ARG("$color", Color)->toHSLA(); return SASS_MEMORY_NEW(Number, pstate, col->l(), "%"); } ///////////////////////////////////////////////////////////////////////// // HSL manipulation functions ///////////////////////////////////////////////////////////////////////// Signature adjust_hue_sig = "adjust-hue($color, $degrees)"; BUILT_IN(adjust_hue) { Color* col = ARG("$color", Color); double degrees = ARGVAL("$degrees"); Color_HSLA_Obj copy = col->copyAsHSLA(); copy->h(absmod(copy->h() + degrees, 360.0)); return copy.detach(); } Signature lighten_sig = "lighten($color, $amount)"; BUILT_IN(lighten) { Color* col = ARG("$color", Color); double amount = DARG_U_PRCT("$amount"); Color_HSLA_Obj copy = col->copyAsHSLA(); copy->l(clip(copy->l() + amount, 0.0, 100.0)); return copy.detach(); } Signature darken_sig = "darken($color, $amount)"; BUILT_IN(darken) { Color* col = ARG("$color", Color); double amount = DARG_U_PRCT("$amount"); Color_HSLA_Obj copy = col->copyAsHSLA(); copy->l(clip(copy->l() - amount, 0.0, 100.0)); return copy.detach(); } Signature saturate_sig = "saturate($color, $amount: false)"; BUILT_IN(saturate) { // CSS3 filter function overload: pass literal through directly if (!Cast(env["$amount"])) { return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")"); } Color* col = ARG("$color", Color); double amount = DARG_U_PRCT("$amount"); Color_HSLA_Obj copy = col->copyAsHSLA(); copy->s(clip(copy->s() + amount, 0.0, 100.0)); return copy.detach(); } Signature desaturate_sig = "desaturate($color, $amount)"; BUILT_IN(desaturate) { Color* col = ARG("$color", Color); double amount = DARG_U_PRCT("$amount"); Color_HSLA_Obj copy = col->copyAsHSLA(); copy->s(clip(copy->s() - amount, 0.0, 100.0)); return copy.detach(); } Signature grayscale_sig = "grayscale($color)"; BUILT_IN(grayscale) { // CSS3 filter function overload: pass literal through directly Number* amount = Cast(env["$color"]); if (amount) { return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")"); } Color* col = ARG("$color", Color); Color_HSLA_Obj copy = col->copyAsHSLA(); copy->s(0.0); // just reset saturation return copy.detach(); } ///////////////////////////////////////////////////////////////////////// // Misc manipulation functions ///////////////////////////////////////////////////////////////////////// Signature complement_sig = "complement($color)"; BUILT_IN(complement) { Color* col = ARG("$color", Color); Color_HSLA_Obj copy = col->copyAsHSLA(); copy->h(absmod(copy->h() - 180.0, 360.0)); return copy.detach(); } Signature invert_sig = "invert($color, $weight: 100%)"; BUILT_IN(invert) { // CSS3 filter function overload: pass literal through directly Number* amount = Cast(env["$color"]); double weight = DARG_U_PRCT("$weight"); if (amount) { // TODO: does not throw on 100% manually passed as value if (weight < 100.0) { error("Only one argument may be passed to the plain-CSS invert() function.", pstate, traces); } return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")"); } Color* col = ARG("$color", Color); Color_RGBA_Obj inv = col->copyAsRGBA(); inv->r(clip(255.0 - inv->r(), 0.0, 255.0)); inv->g(clip(255.0 - inv->g(), 0.0, 255.0)); inv->b(clip(255.0 - inv->b(), 0.0, 255.0)); return colormix(ctx, pstate, inv, col, weight); } ///////////////////////////////////////////////////////////////////////// // Opacity functions ///////////////////////////////////////////////////////////////////////// Signature alpha_sig = "alpha($color)"; Signature opacity_sig = "opacity($color)"; BUILT_IN(alpha) { String_Constant* ie_kwd = Cast(env["$color"]); if (ie_kwd) { return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")"); } // CSS3 filter function overload: pass literal through directly Number* amount = Cast(env["$color"]); if (amount) { return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")"); } return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a()); } Signature opacify_sig = "opacify($color, $amount)"; Signature fade_in_sig = "fade-in($color, $amount)"; BUILT_IN(opacify) { Color* col = ARG("$color", Color); double amount = DARG_U_FACT("$amount"); Color_Obj copy = SASS_MEMORY_COPY(col); copy->a(clip(col->a() + amount, 0.0, 1.0)); return copy.detach(); } Signature transparentize_sig = "transparentize($color, $amount)"; Signature fade_out_sig = "fade-out($color, $amount)"; BUILT_IN(transparentize) { Color* col = ARG("$color", Color); double amount = DARG_U_FACT("$amount"); Color_Obj copy = SASS_MEMORY_COPY(col); copy->a(std::max(col->a() - amount, 0.0)); return copy.detach(); } //////////////////////// // OTHER COLOR FUNCTIONS //////////////////////// Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; BUILT_IN(adjust_color) { Color* col = ARG("$color", Color); Number* r = Cast(env["$red"]); Number* g = Cast(env["$green"]); Number* b = Cast(env["$blue"]); Number* h = Cast(env["$hue"]); Number* s = Cast(env["$saturation"]); Number* l = Cast(env["$lightness"]); Number* a = Cast(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) { error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces); } else if (rgb) { Color_RGBA_Obj c = col->copyAsRGBA(); if (r) c->r(c->r() + DARG_R_BYTE("$red")); if (g) c->g(c->g() + DARG_R_BYTE("$green")); if (b) c->b(c->b() + DARG_R_BYTE("$blue")); if (a) c->a(c->a() + DARG_R_FACT("$alpha")); return c.detach(); } else if (hsl) { Color_HSLA_Obj c = col->copyAsHSLA(); if (h) c->h(c->h() + absmod(h->value(), 360.0)); if (s) c->s(c->s() + DARG_R_PRCT("$saturation")); if (l) c->l(c->l() + DARG_R_PRCT("$lightness")); if (a) c->a(c->a() + DARG_R_FACT("$alpha")); return c.detach(); } else if (a) { Color_Obj c = SASS_MEMORY_COPY(col); c->a(c->a() + DARG_R_FACT("$alpha")); c->a(clip(c->a(), 0.0, 1.0)); return c.detach(); } error("not enough arguments for `adjust-color'", pstate, traces); // unreachable return col; } Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; BUILT_IN(scale_color) { Color* col = ARG("$color", Color); Number* r = Cast(env["$red"]); Number* g = Cast(env["$green"]); Number* b = Cast(env["$blue"]); Number* h = Cast(env["$hue"]); Number* s = Cast(env["$saturation"]); Number* l = Cast(env["$lightness"]); Number* a = Cast(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) { error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces); } else if (rgb) { Color_RGBA_Obj c = col->copyAsRGBA(); double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0; double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0; double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0; double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; if (rscale) c->r(c->r() + rscale * (rscale > 0.0 ? 255.0 - c->r() : c->r())); if (gscale) c->g(c->g() + gscale * (gscale > 0.0 ? 255.0 - c->g() : c->g())); if (bscale) c->b(c->b() + bscale * (bscale > 0.0 ? 255.0 - c->b() : c->b())); if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a())); return c.detach(); } else if (hsl) { Color_HSLA_Obj c = col->copyAsHSLA(); double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0; double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0; double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0; double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0; if (hscale) c->h(c->h() + hscale * (hscale > 0.0 ? 360.0 - c->h() : c->h())); if (sscale) c->s(c->s() + sscale * (sscale > 0.0 ? 100.0 - c->s() : c->s())); if (lscale) c->l(c->l() + lscale * (lscale > 0.0 ? 100.0 - c->l() : c->l())); if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a())); return c.detach(); } else if (a) { Color_Obj c = SASS_MEMORY_COPY(col); double ascale = DARG_R_PRCT("$alpha") / 100.0; c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a())); c->a(clip(c->a(), 0.0, 1.0)); return c.detach(); } error("not enough arguments for `scale-color'", pstate, traces); // unreachable return col; } Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; BUILT_IN(change_color) { Color* col = ARG("$color", Color); Number* r = Cast(env["$red"]); Number* g = Cast(env["$green"]); Number* b = Cast(env["$blue"]); Number* h = Cast(env["$hue"]); Number* s = Cast(env["$saturation"]); Number* l = Cast(env["$lightness"]); Number* a = Cast(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) { error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces); } else if (rgb) { Color_RGBA_Obj c = col->copyAsRGBA(); if (r) c->r(DARG_U_BYTE("$red")); if (g) c->g(DARG_U_BYTE("$green")); if (b) c->b(DARG_U_BYTE("$blue")); if (a) c->a(DARG_U_FACT("$alpha")); return c.detach(); } else if (hsl) { Color_HSLA_Obj c = col->copyAsHSLA(); if (h) c->h(absmod(h->value(), 360.0)); if (s) c->s(DARG_U_PRCT("$saturation")); if (l) c->l(DARG_U_PRCT("$lightness")); if (a) c->a(DARG_U_FACT("$alpha")); return c.detach(); } else if (a) { Color_Obj c = SASS_MEMORY_COPY(col); c->a(clip(DARG_U_FACT("$alpha"), 0.0, 1.0)); return c.detach(); } error("not enough arguments for `change-color'", pstate, traces); // unreachable return col; } Signature ie_hex_str_sig = "ie-hex-str($color)"; BUILT_IN(ie_hex_str) { Color* col = ARG("$color", Color); Color_RGBA_Obj c = col->toRGBA(); double r = clip(c->r(), 0.0, 255.0); double g = clip(c->g(), 0.0, 255.0); double b = clip(c->b(), 0.0, 255.0); double a = clip(c->a(), 0.0, 1.0) * 255.0; sass::ostream ss; ss << '#' << std::setw(2) << std::setfill('0'); ss << std::hex << std::setw(2) << static_cast(Sass::round(a, ctx.c_options.precision)); ss << std::hex << std::setw(2) << static_cast(Sass::round(r, ctx.c_options.precision)); ss << std::hex << std::setw(2) << static_cast(Sass::round(g, ctx.c_options.precision)); ss << std::hex << std::setw(2) << static_cast(Sass::round(b, ctx.c_options.precision)); sass::string result = ss.str(); Util::ascii_str_toupper(&result); return SASS_MEMORY_NEW(String_Quoted, pstate, result); } } } golibsass-1.0.0/libsass_src/src/fn_colors.hpp000066400000000000000000000052451405214413600212770ustar00rootroot00000000000000#ifndef SASS_FN_COLORS_H #define SASS_FN_COLORS_H #include "fn_utils.hpp" namespace Sass { namespace Functions { // macros for common ranges (u mean unsigned or upper, r for full range) #define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double #define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double #define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double #define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double #define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double #define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double // macros for color related inputs (rbg and alpha/opacity values) #define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double #define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double extern Signature rgb_sig; extern Signature rgba_4_sig; extern Signature rgba_2_sig; extern Signature red_sig; extern Signature green_sig; extern Signature blue_sig; extern Signature mix_sig; extern Signature hsl_sig; extern Signature hsla_sig; extern Signature hue_sig; extern Signature saturation_sig; extern Signature lightness_sig; extern Signature adjust_hue_sig; extern Signature lighten_sig; extern Signature darken_sig; extern Signature saturate_sig; extern Signature desaturate_sig; extern Signature grayscale_sig; extern Signature complement_sig; extern Signature invert_sig; extern Signature alpha_sig; extern Signature opacity_sig; extern Signature opacify_sig; extern Signature fade_in_sig; extern Signature transparentize_sig; extern Signature fade_out_sig; extern Signature adjust_color_sig; extern Signature scale_color_sig; extern Signature change_color_sig; extern Signature ie_hex_str_sig; BUILT_IN(rgb); BUILT_IN(rgba_4); BUILT_IN(rgba_2); BUILT_IN(red); BUILT_IN(green); BUILT_IN(blue); BUILT_IN(mix); BUILT_IN(hsl); BUILT_IN(hsla); BUILT_IN(hue); BUILT_IN(saturation); BUILT_IN(lightness); BUILT_IN(adjust_hue); BUILT_IN(lighten); BUILT_IN(darken); BUILT_IN(saturate); BUILT_IN(desaturate); BUILT_IN(grayscale); BUILT_IN(complement); BUILT_IN(invert); BUILT_IN(alpha); BUILT_IN(opacify); BUILT_IN(transparentize); BUILT_IN(adjust_color); BUILT_IN(scale_color); BUILT_IN(change_color); BUILT_IN(ie_hex_str); } } #endif golibsass-1.0.0/libsass_src/src/fn_lists.cpp000066400000000000000000000247021405214413600211260ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "listize.hpp" #include "operators.hpp" #include "fn_utils.hpp" #include "fn_lists.hpp" namespace Sass { namespace Functions { ///////////////// // LIST FUNCTIONS ///////////////// Signature keywords_sig = "keywords($args)"; BUILT_IN(keywords) { List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1); for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) { ExpressionObj obj = arglist->at(i); Argument_Obj arg = (Argument*) obj.ptr(); // XXX sass::string name = sass::string(arg->name()); name = name.erase(0, 1); // sanitize name (remove dollar sign) *result << std::make_pair(SASS_MEMORY_NEW(String_Quoted, pstate, name), arg->value()); } return result.detach(); } Signature length_sig = "length($list)"; BUILT_IN(length) { if (SelectorList * sl = Cast(env["$list"])) { return SASS_MEMORY_NEW(Number, pstate, (double) sl->length()); } Expression* v = ARG("$list", Expression); if (v->concrete_type() == Expression::MAP) { Map* map = Cast(env["$list"]); return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1)); } if (v->concrete_type() == Expression::SELECTOR) { if (CompoundSelector * h = Cast(v)) { return SASS_MEMORY_NEW(Number, pstate, (double)h->length()); } else if (SelectorList * ls = Cast(v)) { return SASS_MEMORY_NEW(Number, pstate, (double)ls->length()); } else { return SASS_MEMORY_NEW(Number, pstate, 1); } } List* list = Cast(env["$list"]); return SASS_MEMORY_NEW(Number, pstate, (double)(list ? list->size() : 1)); } Signature nth_sig = "nth($list, $n)"; BUILT_IN(nth) { double nr = ARGVAL("$n"); Map* m = Cast(env["$list"]); if (SelectorList * sl = Cast(env["$list"])) { size_t len = m ? m->length() : sl->length(); bool empty = m ? m->empty() : sl->empty(); if (empty) error("argument `$list` of `" + sass::string(sig) + "` must not be empty", pstate, traces); double index = std::floor(nr < 0 ? len + nr : nr - 1); if (index < 0 || index > len - 1) error("index out of bounds for `" + sass::string(sig) + "`", pstate, traces); return Cast(Listize::perform(sl->get(static_cast(index)))); } List_Obj l = Cast(env["$list"]); if (nr == 0) error("argument `$n` of `" + sass::string(sig) + "` must be non-zero", pstate, traces); // if the argument isn't a list, then wrap it in a singleton list if (!m && !l) { l = SASS_MEMORY_NEW(List, pstate, 1); l->append(ARG("$list", Expression)); } size_t len = m ? m->length() : l->length(); bool empty = m ? m->empty() : l->empty(); if (empty) error("argument `$list` of `" + sass::string(sig) + "` must not be empty", pstate, traces); double index = std::floor(nr < 0 ? len + nr : nr - 1); if (index < 0 || index > len - 1) error("index out of bounds for `" + sass::string(sig) + "`", pstate, traces); if (m) { l = SASS_MEMORY_NEW(List, pstate, 2); l->append(m->keys()[static_cast(index)]); l->append(m->at(m->keys()[static_cast(index)])); return l.detach(); } else { ValueObj rv = l->value_at_index(static_cast(index)); rv->set_delayed(false); return rv.detach(); } } Signature set_nth_sig = "set-nth($list, $n, $value)"; BUILT_IN(set_nth) { Map_Obj m = Cast(env["$list"]); List_Obj l = Cast(env["$list"]); Number_Obj n = ARG("$n", Number); ExpressionObj v = ARG("$value", Expression); if (!l) { l = SASS_MEMORY_NEW(List, pstate, 1); l->append(ARG("$list", Expression)); } if (m) { l = m->to_list(pstate); } if (l->empty()) error("argument `$list` of `" + sass::string(sig) + "` must not be empty", pstate, traces); double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1); if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + sass::string(sig) + "`", pstate, traces); List* result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { result->append(((i == index) ? v : (*l)[i])); } return result; } Signature index_sig = "index($list, $value)"; BUILT_IN(index) { Map_Obj m = Cast(env["$list"]); List_Obj l = Cast(env["$list"]); ExpressionObj v = ARG("$value", Expression); if (!l) { l = SASS_MEMORY_NEW(List, pstate, 1); l->append(ARG("$list", Expression)); } if (m) { l = m->to_list(pstate); } for (size_t i = 0, L = l->length(); i < L; ++i) { if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1)); } return SASS_MEMORY_NEW(Null, pstate); } Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)"; BUILT_IN(join) { Map_Obj m1 = Cast(env["$list1"]); Map_Obj m2 = Cast(env["$list2"]); List_Obj l1 = Cast(env["$list1"]); List_Obj l2 = Cast(env["$list2"]); String_Constant_Obj sep = ARG("$separator", String_Constant); enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE); Value* bracketed = ARG("$bracketed", Value); bool is_bracketed = (l1 ? l1->is_bracketed() : false); if (!l1) { l1 = SASS_MEMORY_NEW(List, pstate, 1); l1->append(ARG("$list1", Expression)); sep_val = (l2 ? l2->separator() : SASS_SPACE); is_bracketed = (l2 ? l2->is_bracketed() : false); } if (!l2) { l2 = SASS_MEMORY_NEW(List, pstate, 1); l2->append(ARG("$list2", Expression)); } if (m1) { l1 = m1->to_list(pstate); sep_val = SASS_COMMA; } if (m2) { l2 = m2->to_list(pstate); } size_t len = l1->length() + l2->length(); sass::string sep_str = unquote(sep->value()); if (sep_str == "space") sep_val = SASS_SPACE; else if (sep_str == "comma") sep_val = SASS_COMMA; else if (sep_str != "auto") error("argument `$separator` of `" + sass::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces); String_Constant_Obj bracketed_as_str = Cast(bracketed); bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto"; if (!bracketed_is_auto) { is_bracketed = !bracketed->is_false(); } List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed); result->concat(l1); result->concat(l2); return result.detach(); } Signature append_sig = "append($list, $val, $separator: auto)"; BUILT_IN(append) { Map_Obj m = Cast(env["$list"]); List_Obj l = Cast(env["$list"]); ExpressionObj v = ARG("$val", Expression); if (SelectorList * sl = Cast(env["$list"])) { l = Cast(Listize::perform(sl)); } String_Constant_Obj sep = ARG("$separator", String_Constant); if (!l) { l = SASS_MEMORY_NEW(List, pstate, 1); l->append(ARG("$list", Expression)); } if (m) { l = m->to_list(pstate); } List* result = SASS_MEMORY_COPY(l); sass::string sep_str(unquote(sep->value())); if (sep_str != "auto") { // check default first if (sep_str == "space") result->separator(SASS_SPACE); else if (sep_str == "comma") result->separator(SASS_COMMA); else error("argument `$separator` of `" + sass::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces); } if (l->is_arglist()) { result->append(SASS_MEMORY_NEW(Argument, v->pstate(), v, "", false, false)); } else { result->append(v); } return result; } Signature zip_sig = "zip($lists...)"; BUILT_IN(zip) { List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List)); size_t shortest = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { List_Obj ith = Cast(arglist->value_at_index(i)); Map_Obj mith = Cast(arglist->value_at_index(i)); if (!ith) { if (mith) { ith = mith->to_list(pstate); } else { ith = SASS_MEMORY_NEW(List, pstate, 1); ith->append(arglist->value_at_index(i)); } if (arglist->is_arglist()) { Argument_Obj arg = (Argument*)(arglist->at(i).ptr()); // XXX arg->value(ith); } else { (*arglist)[i] = ith; } } shortest = (i ? std::min(shortest, ith->length()) : ith->length()); } List* zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA); size_t L = arglist->length(); for (size_t i = 0; i < shortest; ++i) { List* zipper = SASS_MEMORY_NEW(List, pstate, L); for (size_t j = 0; j < L; ++j) { zipper->append(Cast(arglist->value_at_index(j))->at(i)); } zippers->append(zipper); } return zippers; } Signature list_separator_sig = "list_separator($list)"; BUILT_IN(list_separator) { List_Obj l = Cast(env["$list"]); if (!l) { l = SASS_MEMORY_NEW(List, pstate, 1); l->append(ARG("$list", Expression)); } return SASS_MEMORY_NEW(String_Quoted, pstate, l->separator() == SASS_COMMA ? "comma" : "space"); } Signature is_bracketed_sig = "is-bracketed($list)"; BUILT_IN(is_bracketed) { ValueObj value = ARG("$list", Value); List_Obj list = Cast(value); return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed()); } } } golibsass-1.0.0/libsass_src/src/fn_lists.hpp000066400000000000000000000012031405214413600211220ustar00rootroot00000000000000#ifndef SASS_FN_LISTS_H #define SASS_FN_LISTS_H #include "fn_utils.hpp" namespace Sass { namespace Functions { extern Signature length_sig; extern Signature nth_sig; extern Signature index_sig; extern Signature join_sig; extern Signature append_sig; extern Signature zip_sig; extern Signature list_separator_sig; extern Signature is_bracketed_sig; extern Signature keywords_sig; BUILT_IN(length); BUILT_IN(nth); BUILT_IN(index); BUILT_IN(join); BUILT_IN(append); BUILT_IN(zip); BUILT_IN(list_separator); BUILT_IN(is_bracketed); BUILT_IN(keywords); } } #endif golibsass-1.0.0/libsass_src/src/fn_maps.cpp000066400000000000000000000047271405214413600207350ustar00rootroot00000000000000#include "operators.hpp" #include "fn_utils.hpp" #include "fn_maps.hpp" namespace Sass { namespace Functions { ///////////////// // MAP FUNCTIONS ///////////////// Signature map_get_sig = "map-get($map, $key)"; BUILT_IN(map_get) { // leaks for "map-get((), foo)" if not Obj // investigate why this is (unexpected) Map_Obj m = ARGM("$map", Map); ExpressionObj v = ARG("$key", Expression); try { ValueObj val = m->at(v); if (!val) return SASS_MEMORY_NEW(Null, pstate); val->set_delayed(false); return val.detach(); } catch (const std::out_of_range&) { return SASS_MEMORY_NEW(Null, pstate); } catch (...) { throw; } } Signature map_has_key_sig = "map-has-key($map, $key)"; BUILT_IN(map_has_key) { Map_Obj m = ARGM("$map", Map); ExpressionObj v = ARG("$key", Expression); return SASS_MEMORY_NEW(Boolean, pstate, m->has(v)); } Signature map_keys_sig = "map-keys($map)"; BUILT_IN(map_keys) { Map_Obj m = ARGM("$map", Map); List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); for ( auto key : m->keys()) { result->append(key); } return result; } Signature map_values_sig = "map-values($map)"; BUILT_IN(map_values) { Map_Obj m = ARGM("$map", Map); List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA); for ( auto key : m->keys()) { result->append(m->at(key)); } return result; } Signature map_merge_sig = "map-merge($map1, $map2)"; BUILT_IN(map_merge) { Map_Obj m1 = ARGM("$map1", Map); Map_Obj m2 = ARGM("$map2", Map); size_t len = m1->length() + m2->length(); Map* result = SASS_MEMORY_NEW(Map, pstate, len); // concat not implemented for maps *result += m1; *result += m2; return result; } Signature map_remove_sig = "map-remove($map, $keys...)"; BUILT_IN(map_remove) { bool remove; Map_Obj m = ARGM("$map", Map); List_Obj arglist = ARG("$keys", List); Map* result = SASS_MEMORY_NEW(Map, pstate, 1); for (auto key : m->keys()) { remove = false; for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) { remove = Operators::eq(key, arglist->value_at_index(j)); } if (!remove) *result << std::make_pair(key, m->at(key)); } return result; } } }golibsass-1.0.0/libsass_src/src/fn_maps.hpp000066400000000000000000000011051405214413600207250ustar00rootroot00000000000000#ifndef SASS_FN_MAPS_H #define SASS_FN_MAPS_H #include "fn_utils.hpp" namespace Sass { namespace Functions { #define ARGM(argname, argtype) get_arg_m(argname, env, sig, pstate, traces) extern Signature map_get_sig; extern Signature map_merge_sig; extern Signature map_remove_sig; extern Signature map_keys_sig; extern Signature map_values_sig; extern Signature map_has_key_sig; BUILT_IN(map_get); BUILT_IN(map_merge); BUILT_IN(map_remove); BUILT_IN(map_keys); BUILT_IN(map_values); BUILT_IN(map_has_key); } } #endif golibsass-1.0.0/libsass_src/src/fn_miscs.cpp000066400000000000000000000205211405214413600211010ustar00rootroot00000000000000#include "ast.hpp" #include "expand.hpp" #include "fn_utils.hpp" #include "fn_miscs.hpp" #include "util_string.hpp" namespace Sass { namespace Functions { ////////////////////////// // INTROSPECTION FUNCTIONS ////////////////////////// Signature type_of_sig = "type-of($value)"; BUILT_IN(type_of) { Expression* v = ARG("$value", Expression); return SASS_MEMORY_NEW(String_Quoted, pstate, v->type()); } Signature variable_exists_sig = "variable-exists($name)"; BUILT_IN(variable_exists) { sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has("$"+s)) { return SASS_MEMORY_NEW(Boolean, pstate, true); } else { return SASS_MEMORY_NEW(Boolean, pstate, false); } } Signature global_variable_exists_sig = "global-variable-exists($name)"; BUILT_IN(global_variable_exists) { sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has_global("$"+s)) { return SASS_MEMORY_NEW(Boolean, pstate, true); } else { return SASS_MEMORY_NEW(Boolean, pstate, false); } } Signature function_exists_sig = "function-exists($name)"; BUILT_IN(function_exists) { String_Constant* ss = Cast(env["$name"]); if (!ss) { error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces); } sass::string name = Util::normalize_underscores(unquote(ss->value())); if(d_env.has(name+"[f]")) { return SASS_MEMORY_NEW(Boolean, pstate, true); } else { return SASS_MEMORY_NEW(Boolean, pstate, false); } } Signature mixin_exists_sig = "mixin-exists($name)"; BUILT_IN(mixin_exists) { sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has(s+"[m]")) { return SASS_MEMORY_NEW(Boolean, pstate, true); } else { return SASS_MEMORY_NEW(Boolean, pstate, false); } } Signature feature_exists_sig = "feature-exists($feature)"; BUILT_IN(feature_exists) { sass::string s = unquote(ARG("$feature", String_Constant)->value()); static const auto *const features = new std::unordered_set { "global-variable-shadowing", "extend-selector-pseudoclass", "at-error", "units-level-3", "custom-property" }; return SASS_MEMORY_NEW(Boolean, pstate, features->find(s) != features->end()); } Signature call_sig = "call($function, $args...)"; BUILT_IN(call) { sass::string function; Function* ff = Cast(env["$function"]); String_Constant* ss = Cast(env["$function"]); if (ss) { function = Util::normalize_underscores(unquote(ss->value())); std::cerr << "DEPRECATION WARNING: "; std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl; std::cerr << "in Sass 4.0. Use call(get-function(" + quote(function) + ")) instead." << std::endl; std::cerr << std::endl; } else if (ff) { function = ff->name(); } List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); // sass::string full_name(name + "[f]"); // Definition* def = d_env.has(full_name) ? Cast((d_env)[full_name]) : 0; // Parameters* params = def ? def->parameters() : 0; // size_t param_size = params ? params->length() : 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { ExpressionObj expr = arglist->value_at_index(i); // if (params && params->has_rest_parameter()) { // Parameter_Obj p = param_size > i ? (*params)[i] : 0; // List* list = Cast(expr); // if (list && p && !p->is_rest_parameter()) expr = (*list)[0]; // } if (arglist->is_arglist()) { ExpressionObj obj = arglist->at(i); Argument_Obj arg = (Argument*) obj.ptr(); // XXX args->append(SASS_MEMORY_NEW(Argument, pstate, expr, arg ? arg->name() : "", arg ? arg->is_rest_argument() : false, arg ? arg->is_keyword_argument() : false)); } else { args->append(SASS_MEMORY_NEW(Argument, pstate, expr)); } } Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, function, args); Expand expand(ctx, &d_env, &selector_stack, &original_stack); func->via_call(true); // calc invoke is allowed if (ff) func->func(ff); return Cast(func->perform(&expand.eval)); } //////////////////// // BOOLEAN FUNCTIONS //////////////////// Signature not_sig = "not($value)"; BUILT_IN(sass_not) { return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false()); } Signature if_sig = "if($condition, $if-true, $if-false)"; BUILT_IN(sass_if) { Expand expand(ctx, &d_env, &selector_stack, &original_stack); ExpressionObj cond = ARG("$condition", Expression)->perform(&expand.eval); bool is_true = !cond->is_false(); ExpressionObj res = ARG(is_true ? "$if-true" : "$if-false", Expression); ValueObj qwe = Cast(res->perform(&expand.eval)); // res = res->perform(&expand.eval.val_eval); qwe->set_delayed(false); // clone? return qwe.detach(); } ////////////////////////// // MISCELLANEOUS FUNCTIONS ////////////////////////// Signature inspect_sig = "inspect($value)"; BUILT_IN(inspect) { Expression* v = ARG("$value", Expression); if (v->concrete_type() == Expression::NULL_VAL) { return SASS_MEMORY_NEW(String_Constant, pstate, "null"); } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) { return SASS_MEMORY_NEW(String_Constant, pstate, "false"); } else if (v->concrete_type() == Expression::STRING) { String_Constant *s = Cast(v); if (s->quote_mark()) { return SASS_MEMORY_NEW(String_Constant, pstate, quote(s->value(), s->quote_mark())); } else { return s; } } else { // ToDo: fix to_sass for nested parentheses Sass_Output_Style old_style; old_style = ctx.c_options.output_style; ctx.c_options.output_style = TO_SASS; Emitter emitter(ctx.c_options); Inspect i(emitter); i.in_declaration = false; v->perform(&i); ctx.c_options.output_style = old_style; return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer()); } } Signature content_exists_sig = "content-exists()"; BUILT_IN(content_exists) { if (!d_env.has_global("is_in_mixin")) { error("Cannot call content-exists() except within a mixin.", pstate, traces); } return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]")); } Signature get_function_sig = "get-function($name, $css: false)"; BUILT_IN(get_function) { String_Constant* ss = Cast(env["$name"]); if (!ss) { error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces); } sass::string name = Util::normalize_underscores(unquote(ss->value())); sass::string full_name = name + "[f]"; Boolean_Obj css = ARG("$css", Boolean); if (!css->is_false()) { Definition* def = SASS_MEMORY_NEW(Definition, pstate, name, SASS_MEMORY_NEW(Parameters, pstate), SASS_MEMORY_NEW(Block, pstate, 0, false), Definition::FUNCTION); return SASS_MEMORY_NEW(Function, pstate, def, true); } if (!d_env.has_global(full_name)) { error("Function not found: " + name, pstate, traces); } Definition* def = Cast(d_env[full_name]); return SASS_MEMORY_NEW(Function, pstate, def, false); } } } golibsass-1.0.0/libsass_src/src/fn_miscs.hpp000066400000000000000000000016221405214413600211070ustar00rootroot00000000000000#ifndef SASS_FN_MISCS_H #define SASS_FN_MISCS_H #include "fn_utils.hpp" namespace Sass { namespace Functions { extern Signature type_of_sig; extern Signature variable_exists_sig; extern Signature global_variable_exists_sig; extern Signature function_exists_sig; extern Signature mixin_exists_sig; extern Signature feature_exists_sig; extern Signature call_sig; extern Signature not_sig; extern Signature if_sig; extern Signature set_nth_sig; extern Signature content_exists_sig; extern Signature get_function_sig; BUILT_IN(type_of); BUILT_IN(variable_exists); BUILT_IN(global_variable_exists); BUILT_IN(function_exists); BUILT_IN(mixin_exists); BUILT_IN(feature_exists); BUILT_IN(call); BUILT_IN(sass_not); BUILT_IN(sass_if); BUILT_IN(set_nth); BUILT_IN(content_exists); BUILT_IN(get_function); } } #endif golibsass-1.0.0/libsass_src/src/fn_numbers.cpp000066400000000000000000000162701405214413600214440ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include #include #include #include #include #include "ast.hpp" #include "units.hpp" #include "fn_utils.hpp" #include "fn_numbers.hpp" #ifdef __MINGW32__ #include "windows.h" #include "wincrypt.h" #endif namespace Sass { namespace Functions { #ifdef __MINGW32__ uint64_t GetSeed() { HCRYPTPROV hp = 0; BYTE rb[8]; CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); CryptGenRandom(hp, sizeof(rb), rb); CryptReleaseContext(hp, 0); uint64_t seed; memcpy(&seed, &rb[0], sizeof(seed)); return seed; } #else uint64_t GetSeed() { // Init universe entropy uint64_t rnd = 42; // Try to get random number from system try { std::random_device rd; rnd = rd(); } // On certain system this can throw since either // underlying hardware or software can be buggy. // https://github.com/sass/libsass/issues/3151 catch (std::exception&) { } // Don't trust anyone to be random, so we // add a little entropy of our own. rnd ^= std::time(NULL) ^ std::clock() ^ std::hash() (std::this_thread::get_id()); // Return entropy return rnd; } #endif // note: the performance of many implementations of // random_device degrades sharply once the entropy pool // is exhausted. For practical use, random_device is // generally only used to seed a PRNG such as mt19937. static std::mt19937 rand(static_cast(GetSeed())); /////////////////// // NUMBER FUNCTIONS /////////////////// Signature percentage_sig = "percentage($number)"; BUILT_IN(percentage) { Number_Obj n = ARGN("$number"); if (!n->is_unitless()) error("argument $number of `" + sass::string(sig) + "` must be unitless", pstate, traces); return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%"); } Signature round_sig = "round($number)"; BUILT_IN(round) { Number_Obj r = ARGN("$number"); r->value(Sass::round(r->value(), ctx.c_options.precision)); r->pstate(pstate); return r.detach(); } Signature ceil_sig = "ceil($number)"; BUILT_IN(ceil) { Number_Obj r = ARGN("$number"); r->value(std::ceil(r->value())); r->pstate(pstate); return r.detach(); } Signature floor_sig = "floor($number)"; BUILT_IN(floor) { Number_Obj r = ARGN("$number"); r->value(std::floor(r->value())); r->pstate(pstate); return r.detach(); } Signature abs_sig = "abs($number)"; BUILT_IN(abs) { Number_Obj r = ARGN("$number"); r->value(std::abs(r->value())); r->pstate(pstate); return r.detach(); } Signature min_sig = "min($numbers...)"; BUILT_IN(min) { List* arglist = ARG("$numbers", List); Number_Obj least; size_t L = arglist->length(); if (L == 0) { error("At least one argument must be passed.", pstate, traces); } for (size_t i = 0; i < L; ++i) { ExpressionObj val = arglist->value_at_index(i); Number_Obj xi = Cast(val); if (!xi) { error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces); } if (least) { if (*xi < *least) least = xi; } else least = xi; } return least.detach(); } Signature max_sig = "max($numbers...)"; BUILT_IN(max) { List* arglist = ARG("$numbers", List); Number_Obj greatest; size_t L = arglist->length(); if (L == 0) { error("At least one argument must be passed.", pstate, traces); } for (size_t i = 0; i < L; ++i) { ExpressionObj val = arglist->value_at_index(i); Number_Obj xi = Cast(val); if (!xi) { error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces); } if (greatest) { if (*greatest < *xi) greatest = xi; } else greatest = xi; } return greatest.detach(); } Signature random_sig = "random($limit:false)"; BUILT_IN(random) { AST_Node_Obj arg = env["$limit"]; Value* v = Cast(arg); Number* l = Cast(arg); Boolean* b = Cast(arg); if (l) { double lv = l->value(); if (lv < 1) { sass::ostream err; err << "$limit " << lv << " must be greater than or equal to 1 for `random'"; error(err.str(), pstate, traces); } bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON; if (!eq_int) { sass::ostream err; err << "Expected $limit to be an integer but got " << lv << " for `random'"; error(err.str(), pstate, traces); } std::uniform_real_distribution<> distributor(1, lv + 1); uint_fast32_t distributed = static_cast(distributor(rand)); return SASS_MEMORY_NEW(Number, pstate, (double)distributed); } else if (b) { std::uniform_real_distribution<> distributor(0, 1); double distributed = static_cast(distributor(rand)); return SASS_MEMORY_NEW(Number, pstate, distributed); } else if (v) { traces.push_back(Backtrace(pstate)); throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v); } else { traces.push_back(Backtrace(pstate)); throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number"); } } Signature unique_id_sig = "unique-id()"; BUILT_IN(unique_id) { sass::ostream ss; std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8 uint_fast32_t distributed = static_cast(distributor(rand)); ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed; return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str()); } Signature unit_sig = "unit($number)"; BUILT_IN(unit) { Number_Obj arg = ARGN("$number"); sass::string str(quote(arg->unit(), '"')); return SASS_MEMORY_NEW(String_Quoted, pstate, str); } Signature unitless_sig = "unitless($number)"; BUILT_IN(unitless) { Number_Obj arg = ARGN("$number"); bool unitless = arg->is_unitless(); return SASS_MEMORY_NEW(Boolean, pstate, unitless); } Signature comparable_sig = "comparable($number1, $number2)"; BUILT_IN(comparable) { Number_Obj n1 = ARGN("$number1"); Number_Obj n2 = ARGN("$number2"); if (n1->is_unitless() || n2->is_unitless()) { return SASS_MEMORY_NEW(Boolean, pstate, true); } // normalize into main units n1->normalize(); n2->normalize(); Units &lhs_unit = *n1, &rhs_unit = *n2; bool is_comparable = (lhs_unit == rhs_unit); return SASS_MEMORY_NEW(Boolean, pstate, is_comparable); } } } golibsass-1.0.0/libsass_src/src/fn_numbers.hpp000066400000000000000000000017641405214413600214530ustar00rootroot00000000000000#ifndef SASS_FN_NUMBERS_H #define SASS_FN_NUMBERS_H #include "fn_utils.hpp" namespace Sass { namespace Functions { // return a number object (copied since we want to have reduced units) #define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy extern Signature percentage_sig; extern Signature round_sig; extern Signature ceil_sig; extern Signature floor_sig; extern Signature abs_sig; extern Signature min_sig; extern Signature max_sig; extern Signature inspect_sig; extern Signature random_sig; extern Signature unique_id_sig; extern Signature unit_sig; extern Signature unitless_sig; extern Signature comparable_sig; BUILT_IN(percentage); BUILT_IN(round); BUILT_IN(ceil); BUILT_IN(floor); BUILT_IN(abs); BUILT_IN(min); BUILT_IN(max); BUILT_IN(inspect); BUILT_IN(random); BUILT_IN(unique_id); BUILT_IN(unit); BUILT_IN(unitless); BUILT_IN(comparable); } } #endifgolibsass-1.0.0/libsass_src/src/fn_selectors.cpp000066400000000000000000000162131405214413600217710ustar00rootroot00000000000000#include #include "parser.hpp" #include "extender.hpp" #include "listize.hpp" #include "fn_utils.hpp" #include "fn_selectors.hpp" namespace Sass { namespace Functions { Signature selector_nest_sig = "selector-nest($selectors...)"; BUILT_IN(selector_nest) { List* arglist = ARG("$selectors", List); // Not enough parameters if (arglist->length() == 0) { error( "$selectors: At least one selector must be passed for `selector-nest'", pstate, traces); } // Parse args into vector of selectors SelectorStack parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) { ExpressionObj exp = Cast(arglist->value_at_index(i)); if (exp->concrete_type() == Expression::NULL_VAL) { error( "$selectors: null is not a valid selector: it must be a string,\n" "a list of strings, or a list of lists of strings for 'selector-nest'", pstate, traces); } if (String_Constant_Obj str = Cast(exp)) { str->quote_mark(0); } sass::string exp_src = exp->to_string(ctx.c_options); ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); SelectorListObj sel = Parser::parse_selector(source, ctx, traces); parsedSelectors.push_back(sel); } // Nothing to do if( parsedSelectors.empty() ) { return SASS_MEMORY_NEW(Null, pstate); } // Set the first element as the `result`, keep // appending to as we go down the parsedSelector vector. SelectorStack::iterator itr = parsedSelectors.begin(); SelectorListObj& result = *itr; ++itr; for(;itr != parsedSelectors.end(); ++itr) { SelectorListObj& child = *itr; original_stack.push_back(result); SelectorListObj rv = child->resolve_parent_refs(original_stack, traces); result->elements(rv->elements()); original_stack.pop_back(); } return Cast(Listize::perform(result)); } Signature selector_append_sig = "selector-append($selectors...)"; BUILT_IN(selector_append) { List* arglist = ARG("$selectors", List); // Not enough parameters if (arglist->empty()) { error( "$selectors: At least one selector must be " "passed for `selector-append'", pstate, traces); } // Parse args into vector of selectors SelectorStack parsedSelectors; parsedSelectors.push_back({}); for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression* exp = Cast(arglist->value_at_index(i)); if (exp->concrete_type() == Expression::NULL_VAL) { error( "$selectors: null is not a valid selector: it must be a string,\n" "a list of strings, or a list of lists of strings for 'selector-append'", pstate, traces); } if (String_Constant* str = Cast(exp)) { str->quote_mark(0); } sass::string exp_src = exp->to_string(); ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); SelectorListObj sel = Parser::parse_selector(source, ctx, traces, true); for (auto& complex : sel->elements()) { if (complex->empty()) { complex->append(SASS_MEMORY_NEW(CompoundSelector, "[append]")); } if (CompoundSelector* comp = Cast(complex->first())) { comp->hasRealParent(true); complex->chroots(true); } } if (parsedSelectors.size() > 1) { if (!sel->has_real_parent_ref()) { auto parent = parsedSelectors.back(); for (auto& complex : parent->elements()) { if (CompoundSelector* comp = Cast(complex->first())) { comp->hasRealParent(false); } } error("Can't append \"" + sel->to_string() + "\" to \"" + parent->to_string() + "\" for `selector-append'", pstate, traces); } // Build the resolved stack from the left. It's cheaper to directly // calculate and update each resolved selcted from the left, than to // recursively calculate them from the right side, as we would need // to go through the whole stack depth to build the final results. // E.g. 'a', 'b', 'x, y' => 'a' => 'a b' => 'a b x, a b y' // vs 'a', 'b', 'x, y' => 'x' => 'b x' => 'a b x', 'y' ... parsedSelectors.push_back(sel->resolve_parent_refs(parsedSelectors, traces, true)); } else { parsedSelectors.push_back(sel); } } // Nothing to do if( parsedSelectors.empty() ) { return SASS_MEMORY_NEW(Null, pstate); } return Cast(Listize::perform(parsedSelectors.back())); } Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; BUILT_IN(selector_unify) { SelectorListObj selector1 = ARGSELS("$selector1"); SelectorListObj selector2 = ARGSELS("$selector2"); SelectorListObj result = selector1->unifyWith(selector2); return Cast(Listize::perform(result)); } Signature simple_selectors_sig = "simple-selectors($selector)"; BUILT_IN(simple_selectors) { CompoundSelectorObj sel = ARGSEL("$selector"); List* l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) { const SimpleSelectorObj& ss = sel->get(i); sass::string ss_string = ss->to_string() ; l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string)); } return l; } Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; BUILT_IN(selector_extend) { SelectorListObj selector = ARGSELS("$selector"); SelectorListObj target = ARGSELS("$extendee"); SelectorListObj source = ARGSELS("$extender"); SelectorListObj result = Extender::extend(selector, source, target, traces); return Cast(Listize::perform(result)); } Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; BUILT_IN(selector_replace) { SelectorListObj selector = ARGSELS("$selector"); SelectorListObj target = ARGSELS("$original"); SelectorListObj source = ARGSELS("$replacement"); SelectorListObj result = Extender::replace(selector, source, target, traces); return Cast(Listize::perform(result)); } Signature selector_parse_sig = "selector-parse($selector)"; BUILT_IN(selector_parse) { SelectorListObj selector = ARGSELS("$selector"); return Cast(Listize::perform(selector)); } Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) { SelectorListObj sel_sup = ARGSELS("$super"); SelectorListObj sel_sub = ARGSELS("$sub"); bool result = sel_sup->isSuperselectorOf(sel_sub); return SASS_MEMORY_NEW(Boolean, pstate, result); } } } golibsass-1.0.0/libsass_src/src/fn_selectors.hpp000066400000000000000000000015671405214413600220040ustar00rootroot00000000000000#ifndef SASS_FN_SELECTORS_H #define SASS_FN_SELECTORS_H #include "fn_utils.hpp" namespace Sass { namespace Functions { #define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, traces, ctx) #define ARGSELS(argname) get_arg_sels(argname, env, sig, pstate, traces, ctx) BUILT_IN(selector_nest); BUILT_IN(selector_append); BUILT_IN(selector_extend); BUILT_IN(selector_replace); BUILT_IN(selector_unify); BUILT_IN(is_superselector); BUILT_IN(simple_selectors); BUILT_IN(selector_parse); extern Signature selector_nest_sig; extern Signature selector_append_sig; extern Signature selector_extend_sig; extern Signature selector_replace_sig; extern Signature selector_unify_sig; extern Signature is_superselector_sig; extern Signature simple_selectors_sig; extern Signature selector_parse_sig; } } #endif golibsass-1.0.0/libsass_src/src/fn_strings.cpp000066400000000000000000000203351405214413600214570ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "utf8.h" #include "ast.hpp" #include "fn_utils.hpp" #include "fn_strings.hpp" #include "util_string.hpp" namespace Sass { namespace Functions { void handle_utf8_error (const SourceSpan& pstate, Backtraces traces) { try { throw; } catch (utf8::invalid_code_point&) { sass::string msg("utf8::invalid_code_point"); error(msg, pstate, traces); } catch (utf8::not_enough_room&) { sass::string msg("utf8::not_enough_room"); error(msg, pstate, traces); } catch (utf8::invalid_utf8&) { sass::string msg("utf8::invalid_utf8"); error(msg, pstate, traces); } catch (...) { throw; } } /////////////////// // STRING FUNCTIONS /////////////////// Signature unquote_sig = "unquote($string)"; BUILT_IN(sass_unquote) { AST_Node_Obj arg = env["$string"]; if (String_Quoted* string_quoted = Cast(arg)) { String_Constant* result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value()); // remember if the string was quoted (color tokens) result->is_delayed(true); // delay colors return result; } else if (String_Constant* str = Cast(arg)) { return str; } else if (Value* ex = Cast(arg)) { Sass_Output_Style oldstyle = ctx.c_options.output_style; ctx.c_options.output_style = SASS_STYLE_NESTED; sass::string val(arg->to_string(ctx.c_options)); val = Cast(arg) ? "null" : val; ctx.c_options.output_style = oldstyle; deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate); return ex; } throw std::runtime_error("Invalid Data Type for unquote"); } Signature quote_sig = "quote($string)"; BUILT_IN(sass_quote) { const String_Constant* s = ARG("$string", String_Constant); String_Quoted *result = SASS_MEMORY_NEW( String_Quoted, pstate, s->value(), /*q=*/'\0', /*keep_utf8_escapes=*/false, /*skip_unquoting=*/true); result->quote_mark('*'); return result; } Signature str_length_sig = "str-length($string)"; BUILT_IN(str_length) { size_t len = sass::string::npos; try { String_Constant* s = ARG("$string", String_Constant); len = UTF_8::code_point_count(s->value(), 0, s->value().size()); } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, traces); } // return something even if we had an error (-1) return SASS_MEMORY_NEW(Number, pstate, (double)len); } Signature str_insert_sig = "str-insert($string, $insert, $index)"; BUILT_IN(str_insert) { sass::string str; try { String_Constant* s = ARG("$string", String_Constant); str = s->value(); String_Constant* i = ARG("$insert", String_Constant); sass::string ins = i->value(); double index = ARGVAL("$index"); if (index != (int)index) { sass::ostream strm; strm << "$index: "; strm << std::to_string(index); strm << " is not an int"; error(strm.str(), pstate, traces); } size_t len = UTF_8::code_point_count(str, 0, str.size()); if (index > 0 && index <= len) { // positive and within string length str.insert(UTF_8::offset_at_position(str, static_cast(index) - 1), ins); } else if (index > len) { // positive and past string length str += ins; } else if (index == 0) { str = ins + str; } else if (std::abs(index) <= len) { // negative and within string length index += len + 1; str.insert(UTF_8::offset_at_position(str, static_cast(index)), ins); } else { // negative and past string length str = ins + str; } if (String_Quoted* ss = Cast(s)) { if (ss->quote_mark()) str = quote(str); } } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, traces); } return SASS_MEMORY_NEW(String_Quoted, pstate, str); } Signature str_index_sig = "str-index($string, $substring)"; BUILT_IN(str_index) { size_t index = sass::string::npos; try { String_Constant* s = ARG("$string", String_Constant); String_Constant* t = ARG("$substring", String_Constant); sass::string str = s->value(); sass::string substr = t->value(); size_t c_index = str.find(substr); if(c_index == sass::string::npos) { return SASS_MEMORY_NEW(Null, pstate); } index = UTF_8::code_point_count(str, 0, c_index) + 1; } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, traces); } // return something even if we had an error (-1) return SASS_MEMORY_NEW(Number, pstate, (double)index); } Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)"; BUILT_IN(str_slice) { sass::string newstr; try { String_Constant* s = ARG("$string", String_Constant); double start_at = ARGVAL("$start-at"); double end_at = ARGVAL("$end-at"); if (start_at != (int)start_at) { sass::ostream strm; strm << "$start-at: "; strm << std::to_string(start_at); strm << " is not an int"; error(strm.str(), pstate, traces); } String_Quoted* ss = Cast(s); sass::string str(s->value()); size_t size = utf8::distance(str.begin(), str.end()); if (!Cast(env["$end-at"])) { end_at = -1; } if (end_at != (int)end_at) { sass::ostream strm; strm << "$end-at: "; strm << std::to_string(end_at); strm << " is not an int"; error(strm.str(), pstate, traces); } if (end_at == 0 || (end_at + size) < 0) { if (ss && ss->quote_mark()) newstr = quote(""); return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); } if (end_at < 0) { end_at += size + 1; if (end_at == 0) end_at = 1; } if (end_at > size) { end_at = (double)size; } if (start_at < 0) { start_at += size + 1; if (start_at <= 0) start_at = 1; } else if (start_at == 0) { ++ start_at; } if (start_at <= end_at) { sass::string::iterator start = str.begin(); utf8::advance(start, start_at - 1, str.end()); sass::string::iterator end = start; utf8::advance(end, end_at - start_at + 1, str.end()); newstr = sass::string(start, end); } if (ss) { if(ss->quote_mark()) newstr = quote(newstr); } } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, traces); } return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); } Signature to_upper_case_sig = "to-upper-case($string)"; BUILT_IN(to_upper_case) { String_Constant* s = ARG("$string", String_Constant); sass::string str = s->value(); Util::ascii_str_toupper(&str); if (String_Quoted* ss = Cast(s)) { String_Quoted* cpy = SASS_MEMORY_COPY(ss); cpy->value(str); return cpy; } else { return SASS_MEMORY_NEW(String_Quoted, pstate, str); } } Signature to_lower_case_sig = "to-lower-case($string)"; BUILT_IN(to_lower_case) { String_Constant* s = ARG("$string", String_Constant); sass::string str = s->value(); Util::ascii_str_tolower(&str); if (String_Quoted* ss = Cast(s)) { String_Quoted* cpy = SASS_MEMORY_COPY(ss); cpy->value(str); return cpy; } else { return SASS_MEMORY_NEW(String_Quoted, pstate, str); } } } } golibsass-1.0.0/libsass_src/src/fn_strings.hpp000066400000000000000000000012731405214413600214640ustar00rootroot00000000000000#ifndef SASS_FN_STRINGS_H #define SASS_FN_STRINGS_H #include "fn_utils.hpp" namespace Sass { namespace Functions { extern Signature unquote_sig; extern Signature quote_sig; extern Signature str_length_sig; extern Signature str_insert_sig; extern Signature str_index_sig; extern Signature str_slice_sig; extern Signature to_upper_case_sig; extern Signature to_lower_case_sig; extern Signature length_sig; BUILT_IN(sass_unquote); BUILT_IN(sass_quote); BUILT_IN(str_length); BUILT_IN(str_insert); BUILT_IN(str_index); BUILT_IN(str_slice); BUILT_IN(to_upper_case); BUILT_IN(to_lower_case); BUILT_IN(length); } } #endif golibsass-1.0.0/libsass_src/src/fn_utils.cpp000066400000000000000000000137731405214413600211360ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "parser.hpp" #include "fn_utils.hpp" #include "util_string.hpp" namespace Sass { Definition* make_native_function(Signature sig, Native_Function func, Context& ctx) { SourceFile* source = SASS_MEMORY_NEW(SourceFile, "[built-in function]", sig, std::string::npos); Parser sig_parser(source, ctx, ctx.traces); sig_parser.lex(); sass::string name(Util::normalize_underscores(sig_parser.lexed)); Parameters_Obj params = sig_parser.parse_parameters(); return SASS_MEMORY_NEW(Definition, SourceSpan(source), sig, name, params, func, false); } Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx) { using namespace Prelexer; const char* sig = sass_function_get_signature(c_func); SourceFile* source = SASS_MEMORY_NEW(SourceFile, "[c function]", sig, std::string::npos); Parser sig_parser(source, ctx, ctx.traces); // allow to overload generic callback plus @warn, @error and @debug with custom functions sig_parser.lex < alternatives < identifier, exactly <'*'>, exactly < Constants::warn_kwd >, exactly < Constants::error_kwd >, exactly < Constants::debug_kwd > > >(); sass::string name(Util::normalize_underscores(sig_parser.lexed)); Parameters_Obj params = sig_parser.parse_parameters(); return SASS_MEMORY_NEW(Definition, SourceSpan(source), sig, name, params, c_func); } namespace Functions { sass::string function_name(Signature sig) { sass::string str(sig); return str.substr(0, str.find('(')); } Map* get_arg_m(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces) { AST_Node* value = env[argname]; if (Map* map = Cast(value)) return map; List* list = Cast(value); if (list && list->length() == 0) { return SASS_MEMORY_NEW(Map, pstate, 0); } return get_arg(argname, env, sig, pstate, traces); } double get_arg_r(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, double lo, double hi) { Number* val = get_arg(argname, env, sig, pstate, traces); Number tmpnr(val); tmpnr.reduce(); double v = tmpnr.value(); if (!(lo <= v && v <= hi)) { sass::ostream msg; msg << "argument `" << argname << "` of `" << sig << "` must be between "; msg << lo << " and " << hi; error(msg.str(), pstate, traces); } return v; } Number* get_arg_n(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces) { Number* val = get_arg(argname, env, sig, pstate, traces); val = SASS_MEMORY_COPY(val); val->reduce(); return val; } double get_arg_val(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces) { Number* val = get_arg(argname, env, sig, pstate, traces); Number tmpnr(val); tmpnr.reduce(); return tmpnr.value(); } double color_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces) { Number* val = get_arg(argname, env, sig, pstate, traces); Number tmpnr(val); tmpnr.reduce(); if (tmpnr.unit() == "%") { return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0); } else { return std::min(std::max(tmpnr.value(), 0.0), 255.0); } } double alpha_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces) { Number* val = get_arg(argname, env, sig, pstate, traces); Number tmpnr(val); tmpnr.reduce(); if (tmpnr.unit() == "%") { return std::min(std::max(tmpnr.value(), 0.0), 100.0); } else { return std::min(std::max(tmpnr.value(), 0.0), 1.0); } } SelectorListObj get_arg_sels(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx) { ExpressionObj exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { sass::ostream msg; msg << argname << ": null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; error(msg.str(), exp->pstate(), traces); } if (String_Constant* str = Cast(exp)) { str->quote_mark(0); } sass::string exp_src = exp->to_string(ctx.c_options); ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); return Parser::parse_selector(source, ctx, traces, false); } CompoundSelectorObj get_arg_sel(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx) { ExpressionObj exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { sass::ostream msg; msg << argname << ": null is not a string for `" << function_name(sig) << "'"; error(msg.str(), exp->pstate(), traces); } if (String_Constant* str = Cast(exp)) { str->quote_mark(0); } sass::string exp_src = exp->to_string(ctx.c_options); ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); SelectorListObj sel_list = Parser::parse_selector(source, ctx, traces, false); if (sel_list->length() == 0) return {}; return sel_list->first()->first(); } } } golibsass-1.0.0/libsass_src/src/fn_utils.hpp000066400000000000000000000047711405214413600211410ustar00rootroot00000000000000#ifndef SASS_FN_UTILS_H #define SASS_FN_UTILS_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "units.hpp" #include "backtrace.hpp" #include "environment.hpp" #include "ast_fwd_decl.hpp" #include "error_handling.hpp" namespace Sass { #define FN_PROTOTYPE \ Env& env, \ Env& d_env, \ Context& ctx, \ Signature sig, \ SourceSpan pstate, \ Backtraces& traces, \ SelectorStack selector_stack, \ SelectorStack original_stack \ typedef const char* Signature; typedef PreValue* (*Native_Function)(FN_PROTOTYPE); #define BUILT_IN(name) PreValue* name(FN_PROTOTYPE) #define ARG(argname, argtype) get_arg(argname, env, sig, pstate, traces) // special function for weird hsla percent (10px == 10% == 10 != 0.1) #define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double Definition* make_native_function(Signature, Native_Function, Context& ctx); Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx); namespace Functions { template T* get_arg(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces) { T* val = Cast(env[argname]); if (!val) { error("argument `" + argname + "` of `" + sig + "` must be a " + T::type_name(), pstate, traces); } return val; } Map* get_arg_m(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // maps only Number* get_arg_n(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // numbers only double alpha_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // colors only double color_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // colors only double get_arg_r(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, double lo, double hi); // colors only double get_arg_val(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // shared SelectorListObj get_arg_sels(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx); // selectors only CompoundSelectorObj get_arg_sel(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx); // selectors only } } #endif golibsass-1.0.0/libsass_src/src/inspect.cpp000066400000000000000000000723741405214413600207620ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include #include #include "ast.hpp" #include "inspect.hpp" #include "context.hpp" #include "listize.hpp" #include "color_maps.hpp" #include "utf8/checked.h" namespace Sass { Inspect::Inspect(const Emitter& emi) : Emitter(emi) { } Inspect::~Inspect() { } // statements void Inspect::operator()(Block* block) { if (!block->is_root()) { add_open_mapping(block); append_scope_opener(); } if (output_style() == NESTED) indentation += block->tabs(); for (size_t i = 0, L = block->length(); i < L; ++i) { (*block)[i]->perform(this); } if (output_style() == NESTED) indentation -= block->tabs(); if (!block->is_root()) { append_scope_closer(); add_close_mapping(block); } } void Inspect::operator()(StyleRule* ruleset) { if (ruleset->selector()) { ruleset->selector()->perform(this); } if (ruleset->block()) { ruleset->block()->perform(this); } } void Inspect::operator()(Keyframe_Rule* rule) { if (rule->name()) rule->name()->perform(this); if (rule->block()) rule->block()->perform(this); } void Inspect::operator()(Bubble* bubble) { append_indentation(); append_token("::BUBBLE", bubble); append_scope_opener(); bubble->node()->perform(this); append_scope_closer(); } void Inspect::operator()(MediaRule* rule) { append_indentation(); append_token("@media", rule); append_mandatory_space(); if (rule->block()) { rule->block()->perform(this); } } void Inspect::operator()(CssMediaRule* rule) { if (output_style() == NESTED) indentation += rule->tabs(); append_indentation(); append_token("@media", rule); append_mandatory_space(); in_media_block = true; bool joinIt = false; for (auto query : rule->elements()) { if (joinIt) { append_comma_separator(); append_optional_space(); } operator()(query); joinIt = true; } if (rule->block()) { rule->block()->perform(this); } in_media_block = false; if (output_style() == NESTED) indentation -= rule->tabs(); } void Inspect::operator()(CssMediaQuery* query) { bool joinIt = false; if (!query->modifier().empty()) { append_string(query->modifier()); append_mandatory_space(); } if (!query->type().empty()) { append_string(query->type()); joinIt = true; } for (auto feature : query->features()) { if (joinIt) { append_mandatory_space(); append_string("and"); append_mandatory_space(); } append_string(feature); joinIt = true; } } void Inspect::operator()(SupportsRule* feature_block) { append_indentation(); append_token("@supports", feature_block); append_mandatory_space(); feature_block->condition()->perform(this); feature_block->block()->perform(this); } void Inspect::operator()(AtRootRule* at_root_block) { append_indentation(); append_token("@at-root ", at_root_block); append_mandatory_space(); if(at_root_block->expression()) at_root_block->expression()->perform(this); if(at_root_block->block()) at_root_block->block()->perform(this); } void Inspect::operator()(AtRule* at_rule) { append_indentation(); append_token(at_rule->keyword(), at_rule); if (at_rule->selector()) { append_mandatory_space(); bool was_wrapped = in_wrapped; in_wrapped = true; at_rule->selector()->perform(this); in_wrapped = was_wrapped; } if (at_rule->value()) { append_mandatory_space(); at_rule->value()->perform(this); } if (at_rule->block()) { at_rule->block()->perform(this); } else { append_delimiter(); } } void Inspect::operator()(Declaration* dec) { if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration; in_declaration = true; LOCAL_FLAG(in_custom_property, dec->is_custom_property()); if (output_style() == NESTED) indentation += dec->tabs(); append_indentation(); if (dec->property()) dec->property()->perform(this); append_colon_separator(); if (dec->value()->concrete_type() == Expression::SELECTOR) { ExpressionObj ls = Listize::perform(dec->value()); ls->perform(this); } else { dec->value()->perform(this); } if (dec->is_important()) { append_optional_space(); append_string("!important"); } append_delimiter(); if (output_style() == NESTED) indentation -= dec->tabs(); in_declaration = was_decl; } void Inspect::operator()(Assignment* assn) { append_token(assn->variable(), assn); append_colon_separator(); assn->value()->perform(this); if (assn->is_default()) { append_optional_space(); append_string("!default"); } append_delimiter(); } void Inspect::operator()(Import* import) { if (!import->urls().empty()) { append_token("@import", import); append_mandatory_space(); import->urls().front()->perform(this); if (import->urls().size() == 1) { if (import->import_queries()) { append_mandatory_space(); import->import_queries()->perform(this); } } append_delimiter(); for (size_t i = 1, S = import->urls().size(); i < S; ++i) { append_mandatory_linefeed(); append_token("@import", import); append_mandatory_space(); import->urls()[i]->perform(this); if (import->urls().size() - 1 == i) { if (import->import_queries()) { append_mandatory_space(); import->import_queries()->perform(this); } } append_delimiter(); } } } void Inspect::operator()(Import_Stub* import) { append_indentation(); append_token("@import", import); append_mandatory_space(); append_string(import->imp_path()); append_delimiter(); } void Inspect::operator()(WarningRule* warning) { append_indentation(); append_token("@warn", warning); append_mandatory_space(); warning->message()->perform(this); append_delimiter(); } void Inspect::operator()(ErrorRule* error) { append_indentation(); append_token("@error", error); append_mandatory_space(); error->message()->perform(this); append_delimiter(); } void Inspect::operator()(DebugRule* debug) { append_indentation(); append_token("@debug", debug); append_mandatory_space(); debug->value()->perform(this); append_delimiter(); } void Inspect::operator()(Comment* comment) { in_comment = true; comment->text()->perform(this); in_comment = false; } void Inspect::operator()(If* cond) { append_indentation(); append_token("@if", cond); append_mandatory_space(); cond->predicate()->perform(this); cond->block()->perform(this); if (cond->alternative()) { append_optional_linefeed(); append_indentation(); append_string("else"); cond->alternative()->perform(this); } } void Inspect::operator()(ForRule* loop) { append_indentation(); append_token("@for", loop); append_mandatory_space(); append_string(loop->variable()); append_string(" from "); loop->lower_bound()->perform(this); append_string(loop->is_inclusive() ? " through " : " to "); loop->upper_bound()->perform(this); loop->block()->perform(this); } void Inspect::operator()(EachRule* loop) { append_indentation(); append_token("@each", loop); append_mandatory_space(); append_string(loop->variables()[0]); for (size_t i = 1, L = loop->variables().size(); i < L; ++i) { append_comma_separator(); append_string(loop->variables()[i]); } append_string(" in "); loop->list()->perform(this); loop->block()->perform(this); } void Inspect::operator()(WhileRule* loop) { append_indentation(); append_token("@while", loop); append_mandatory_space(); loop->predicate()->perform(this); loop->block()->perform(this); } void Inspect::operator()(Return* ret) { append_indentation(); append_token("@return", ret); append_mandatory_space(); ret->value()->perform(this); append_delimiter(); } void Inspect::operator()(ExtendRule* extend) { append_indentation(); append_token("@extend", extend); append_mandatory_space(); extend->selector()->perform(this); append_delimiter(); } void Inspect::operator()(Definition* def) { append_indentation(); if (def->type() == Definition::MIXIN) { append_token("@mixin", def); append_mandatory_space(); } else { append_token("@function", def); append_mandatory_space(); } append_string(def->name()); def->parameters()->perform(this); def->block()->perform(this); } void Inspect::operator()(Mixin_Call* call) { append_indentation(); append_token("@include", call); append_mandatory_space(); append_string(call->name()); if (call->arguments()) { call->arguments()->perform(this); } if (call->block()) { append_optional_space(); call->block()->perform(this); } if (!call->block()) append_delimiter(); } void Inspect::operator()(Content* content) { append_indentation(); append_token("@content", content); append_delimiter(); } void Inspect::operator()(Map* map) { if (output_style() == TO_SASS && map->empty()) { append_string("()"); return; } if (map->empty()) return; if (map->is_invisible()) return; bool items_output = false; append_string("("); for (auto key : map->keys()) { if (items_output) append_comma_separator(); key->perform(this); append_colon_separator(); LOCAL_FLAG(in_space_array, true); LOCAL_FLAG(in_comma_array, true); map->at(key)->perform(this); items_output = true; } append_string(")"); } sass::string Inspect::lbracket(List* list) { return list->is_bracketed() ? "[" : "("; } sass::string Inspect::rbracket(List* list) { return list->is_bracketed() ? "]" : ")"; } void Inspect::operator()(List* list) { if (list->empty() && (output_style() == TO_SASS || list->is_bracketed())) { append_string(lbracket(list)); append_string(rbracket(list)); return; } sass::string sep(list->separator() == SASS_SPACE ? " " : ","); if ((output_style() != COMPRESSED) && sep == ",") sep += " "; else if (in_media_block && sep != " ") sep += " "; // verified if (list->empty()) return; bool items_output = false; bool was_space_array = in_space_array; bool was_comma_array = in_comma_array; // if the list is bracketed, always include the left bracket if (list->is_bracketed()) { append_string(lbracket(list)); } // probably ruby sass equivalent of element_needs_parens else if (output_style() == TO_SASS && list->length() == 1 && !list->from_selector() && !Cast(list->at(0)) && !Cast(list->at(0)) ) { append_string(lbracket(list)); } else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { append_string(lbracket(list)); } if (list->separator() == SASS_SPACE) in_space_array = true; else if (list->separator() == SASS_COMMA) in_comma_array = true; for (size_t i = 0, L = list->size(); i < L; ++i) { if (list->separator() == SASS_HASH) { sep[0] = i % 2 ? ':' : ','; } ExpressionObj list_item = list->at(i); if (output_style() != TO_SASS) { if (list_item->is_invisible()) { // this fixes an issue with "" in a list if (!Cast(list_item)) { continue; } } } if (items_output) { append_string(sep); } if (items_output && sep != " ") append_optional_space(); list_item->perform(this); items_output = true; } in_comma_array = was_comma_array; in_space_array = was_space_array; // if the list is bracketed, always include the right bracket if (list->is_bracketed()) { if (list->separator() == SASS_COMMA && list->size() == 1) { append_string(","); } append_string(rbracket(list)); } // probably ruby sass equivalent of element_needs_parens else if (output_style() == TO_SASS && list->length() == 1 && !list->from_selector() && !Cast(list->at(0)) && !Cast(list->at(0)) ) { append_string(","); append_string(rbracket(list)); } else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { append_string(rbracket(list)); } } void Inspect::operator()(Binary_Expression* expr) { expr->left()->perform(this); if ( in_media_block || (output_style() == INSPECT) || ( expr->op().ws_before && (!expr->is_interpolant()) && (expr->is_left_interpolant() || expr->is_right_interpolant()) )) append_string(" "); switch (expr->optype()) { case Sass_OP::AND: append_string("&&"); break; case Sass_OP::OR: append_string("||"); break; case Sass_OP::EQ: append_string("=="); break; case Sass_OP::NEQ: append_string("!="); break; case Sass_OP::GT: append_string(">"); break; case Sass_OP::GTE: append_string(">="); break; case Sass_OP::LT: append_string("<"); break; case Sass_OP::LTE: append_string("<="); break; case Sass_OP::ADD: append_string("+"); break; case Sass_OP::SUB: append_string("-"); break; case Sass_OP::MUL: append_string("*"); break; case Sass_OP::DIV: append_string("/"); break; case Sass_OP::MOD: append_string("%"); break; default: break; // shouldn't get here } if ( in_media_block || (output_style() == INSPECT) || ( expr->op().ws_after && (!expr->is_interpolant()) && (expr->is_left_interpolant() || expr->is_right_interpolant()) )) append_string(" "); expr->right()->perform(this); } void Inspect::operator()(Unary_Expression* expr) { if (expr->optype() == Unary_Expression::PLUS) append_string("+"); else if (expr->optype() == Unary_Expression::SLASH) append_string("/"); else append_string("-"); expr->operand()->perform(this); } void Inspect::operator()(Function_Call* call) { append_token(call->name(), call); call->arguments()->perform(this); } void Inspect::operator()(Variable* var) { append_token(var->name(), var); } void Inspect::operator()(Number* n) { // reduce units n->reduce(); sass::ostream ss; ss.precision(opt.precision); ss << std::fixed << n->value(); sass::string res = ss.str(); size_t s = res.length(); // delete trailing zeros for(s = s - 1; s > 0; --s) { if(res[s] == '0') { res.erase(s, 1); } else break; } // delete trailing decimal separator if(res[s] == '.') res.erase(s, 1); // some final cosmetics if (res == "0.0") res = "0"; else if (res == "") res = "0"; else if (res == "-0") res = "0"; else if (res == "-0.0") res = "0"; else if (opt.output_style == COMPRESSED) { if (n->zero()) { // check if handling negative nr size_t off = res[0] == '-' ? 1 : 0; // remove leading zero from floating point in compressed mode if (res[off] == '0' && res[off+1] == '.') res.erase(off, 1); } } // add unit now res += n->unit(); if (opt.output_style == TO_CSS && !n->is_valid_css_unit()) { // traces.push_back(Backtrace(nr->pstate())); throw Exception::InvalidValue({}, *n); } // output the final token append_token(res, n); } // helper function for serializing colors template static double cap_channel(double c) { if (c > range) return range; else if (c < 0) return 0; else return c; } void Inspect::operator()(Color_RGBA* c) { // output the final token sass::ostream ss; // original color name // maybe an unknown token sass::string name = c->disp(); // resolved color sass::string res_name = name; double r = Sass::round(cap_channel<0xff>(c->r()), opt.precision); double g = Sass::round(cap_channel<0xff>(c->g()), opt.precision); double b = Sass::round(cap_channel<0xff>(c->b()), opt.precision); double a = cap_channel<1> (c->a()); // get color from given name (if one was given at all) if (name != "" && name_to_color(name)) { const Color_RGBA* n = name_to_color(name); r = Sass::round(cap_channel<0xff>(n->r()), opt.precision); g = Sass::round(cap_channel<0xff>(n->g()), opt.precision); b = Sass::round(cap_channel<0xff>(n->b()), opt.precision); a = cap_channel<1> (n->a()); } // otherwise get the possible resolved color name else { double numval = r * 0x10000 + g * 0x100 + b; if (color_to_name(numval)) res_name = color_to_name(numval); } sass::ostream hexlet; // dart sass compressed all colors in regular css always // ruby sass and libsass does it only when not delayed // since color math is going to be removed, this can go too bool compressed = opt.output_style == COMPRESSED; hexlet << '#' << std::setw(1) << std::setfill('0'); // create a short color hexlet if there is any need for it if (compressed && is_color_doublet(r, g, b) && a == 1) { hexlet << std::hex << std::setw(1) << (static_cast(r) >> 4); hexlet << std::hex << std::setw(1) << (static_cast(g) >> 4); hexlet << std::hex << std::setw(1) << (static_cast(b) >> 4); } else { hexlet << std::hex << std::setw(2) << static_cast(r); hexlet << std::hex << std::setw(2) << static_cast(g); hexlet << std::hex << std::setw(2) << static_cast(b); } if (compressed && !c->is_delayed()) name = ""; if (opt.output_style == INSPECT && a >= 1) { append_token(hexlet.str(), c); return; } // retain the originally specified color definition if unchanged if (name != "") { ss << name; } else if (a >= 1) { if (res_name != "") { if (compressed && hexlet.str().size() < res_name.size()) { ss << hexlet.str(); } else { ss << res_name; } } else { ss << hexlet.str(); } } else { ss << "rgba("; ss << static_cast(r) << ","; if (!compressed) ss << " "; ss << static_cast(g) << ","; if (!compressed) ss << " "; ss << static_cast(b) << ","; if (!compressed) ss << " "; ss << a << ')'; } append_token(ss.str(), c); } void Inspect::operator()(Color_HSLA* c) { Color_RGBA_Obj rgba = c->toRGBA(); operator()(rgba); } void Inspect::operator()(Boolean* b) { // output the final token append_token(b->value() ? "true" : "false", b); } void Inspect::operator()(String_Schema* ss) { // Evaluation should turn these into String_Constants, // so this method is only for inspection purposes. for (size_t i = 0, L = ss->length(); i < L; ++i) { if ((*ss)[i]->is_interpolant()) append_string("#{"); (*ss)[i]->perform(this); if ((*ss)[i]->is_interpolant()) append_string("}"); } } void Inspect::operator()(String_Constant* s) { append_token(s->value(), s); } void Inspect::operator()(String_Quoted* s) { if (const char q = s->quote_mark()) { append_token(quote(s->value(), q), s); } else { append_token(s->value(), s); } } void Inspect::operator()(Custom_Error* e) { append_token(e->message(), e); } void Inspect::operator()(Custom_Warning* w) { append_token(w->message(), w); } void Inspect::operator()(SupportsOperation* so) { if (so->needs_parens(so->left())) append_string("("); so->left()->perform(this); if (so->needs_parens(so->left())) append_string(")"); if (so->operand() == SupportsOperation::AND) { append_mandatory_space(); append_token("and", so); append_mandatory_space(); } else if (so->operand() == SupportsOperation::OR) { append_mandatory_space(); append_token("or", so); append_mandatory_space(); } if (so->needs_parens(so->right())) append_string("("); so->right()->perform(this); if (so->needs_parens(so->right())) append_string(")"); } void Inspect::operator()(SupportsNegation* sn) { append_token("not", sn); append_mandatory_space(); if (sn->needs_parens(sn->condition())) append_string("("); sn->condition()->perform(this); if (sn->needs_parens(sn->condition())) append_string(")"); } void Inspect::operator()(SupportsDeclaration* sd) { append_string("("); sd->feature()->perform(this); append_string(": "); sd->value()->perform(this); append_string(")"); } void Inspect::operator()(Supports_Interpolation* sd) { sd->value()->perform(this); } void Inspect::operator()(Media_Query* mq) { size_t i = 0; if (mq->media_type()) { if (mq->is_negated()) append_string("not "); else if (mq->is_restricted()) append_string("only "); mq->media_type()->perform(this); } else { (*mq)[i++]->perform(this); } for (size_t L = mq->length(); i < L; ++i) { append_string(" and "); (*mq)[i]->perform(this); } } void Inspect::operator()(Media_Query_Expression* mqe) { if (mqe->is_interpolated()) { mqe->feature()->perform(this); } else { append_string("("); mqe->feature()->perform(this); if (mqe->value()) { append_string(": "); // verified mqe->value()->perform(this); } append_string(")"); } } void Inspect::operator()(At_Root_Query* ae) { if (ae->feature()) { append_string("("); ae->feature()->perform(this); if (ae->value()) { append_colon_separator(); ae->value()->perform(this); } append_string(")"); } } void Inspect::operator()(Function* f) { append_token("get-function", f); append_string("("); append_string(quote(f->name())); append_string(")"); } void Inspect::operator()(Null* n) { // output the final token append_token("null", n); } // parameters and arguments void Inspect::operator()(Parameter* p) { append_token(p->name(), p); if (p->default_value()) { append_colon_separator(); p->default_value()->perform(this); } else if (p->is_rest_parameter()) { append_string("..."); } } void Inspect::operator()(Parameters* p) { append_string("("); if (!p->empty()) { (*p)[0]->perform(this); for (size_t i = 1, L = p->length(); i < L; ++i) { append_comma_separator(); (*p)[i]->perform(this); } } append_string(")"); } void Inspect::operator()(Argument* a) { if (!a->name().empty()) { append_token(a->name(), a); append_colon_separator(); } if (!a->value()) return; // Special case: argument nulls can be ignored if (a->value()->concrete_type() == Expression::NULL_VAL) { return; } if (a->value()->concrete_type() == Expression::STRING) { String_Constant* s = Cast(a->value()); if (s) s->perform(this); } else { a->value()->perform(this); } if (a->is_rest_argument()) { append_string("..."); } } void Inspect::operator()(Arguments* a) { append_string("("); if (!a->empty()) { (*a)[0]->perform(this); for (size_t i = 1, L = a->length(); i < L; ++i) { append_string(", "); // verified // Sass Bug? append_comma_separator(); (*a)[i]->perform(this); } } append_string(")"); } void Inspect::operator()(Selector_Schema* s) { s->contents()->perform(this); } void Inspect::operator()(Parent_Reference* p) { append_string("&"); } void Inspect::operator()(PlaceholderSelector* s) { append_token(s->name(), s); } void Inspect::operator()(TypeSelector* s) { append_token(s->ns_name(), s); } void Inspect::operator()(ClassSelector* s) { append_token(s->ns_name(), s); } void Inspect::operator()(IDSelector* s) { append_token(s->ns_name(), s); } void Inspect::operator()(AttributeSelector* s) { append_string("["); add_open_mapping(s); append_token(s->ns_name(), s); if (!s->matcher().empty()) { append_string(s->matcher()); if (s->value() && *s->value()) { s->value()->perform(this); } } add_close_mapping(s); if (s->modifier() != 0) { append_mandatory_space(); append_char(s->modifier()); } append_string("]"); } void Inspect::operator()(PseudoSelector* s) { if (s->name() != "") { append_string(":"); if (s->isSyntacticElement()) { append_string(":"); } append_token(s->ns_name(), s); if (s->selector() || s->argument()) { bool was = in_wrapped; in_wrapped = true; append_string("("); if (s->argument()) { s->argument()->perform(this); } if (s->selector() && s->argument()) { append_mandatory_space(); } bool was_comma_array = in_comma_array; in_comma_array = false; if (s->selector()) { s->selector()->perform(this); } in_comma_array = was_comma_array; append_string(")"); in_wrapped = was; } } } void Inspect::operator()(SelectorList* g) { if (g->empty()) { if (output_style() == TO_SASS) { append_token("()", g); } return; } bool was_comma_array = in_comma_array; // probably ruby sass equivalent of element_needs_parens if (output_style() == TO_SASS && g->length() == 1 && (!Cast((*g)[0]) && !Cast((*g)[0]))) { append_string("("); } else if (!in_declaration && in_comma_array) { append_string("("); } if (in_declaration) in_comma_array = true; for (size_t i = 0, L = g->length(); i < L; ++i) { if (!in_wrapped && i == 0) append_indentation(); if ((*g)[i] == nullptr) continue; if (g->at(i)->length() == 0) continue; schedule_mapping(g->at(i)->last()); // add_open_mapping((*g)[i]->last()); (*g)[i]->perform(this); // add_close_mapping((*g)[i]->last()); if (i < L - 1) { scheduled_space = 0; append_comma_separator(); } } in_comma_array = was_comma_array; // probably ruby sass equivalent of element_needs_parens if (output_style() == TO_SASS && g->length() == 1 && (!Cast((*g)[0]) && !Cast((*g)[0]))) { append_string(",)"); } else if (!in_declaration && in_comma_array) { append_string(")"); } } void Inspect::operator()(ComplexSelector* sel) { if (sel->hasPreLineFeed()) { append_optional_linefeed(); if (!in_wrapped && output_style() == NESTED) { append_indentation(); } } const SelectorComponent* prev = nullptr; for (auto& item : sel->elements()) { if (prev != nullptr) { if (item->getCombinator() || prev->getCombinator()) { append_optional_space(); } else { append_mandatory_space(); } } item->perform(this); prev = item.ptr(); } } void Inspect::operator()(SelectorComponent* sel) { // You should probably never call this method directly // But in case anyone does, we will do the up-casting if (auto comp = Cast(sel)) operator()(comp); if (auto comb = Cast(sel)) operator()(comb); } void Inspect::operator()(CompoundSelector* sel) { if (sel->hasRealParent()) { append_string("&"); } for (auto& item : sel->elements()) { item->perform(this); } // Add the post line break (from ruby sass) // Dart sass uses another logic for newlines if (sel->hasPostLineBreak()) { if (output_style() != COMPACT) { append_optional_linefeed(); } } } void Inspect::operator()(SelectorCombinator* sel) { append_optional_space(); switch (sel->combinator()) { case SelectorCombinator::Combinator::CHILD: append_string(">"); break; case SelectorCombinator::Combinator::GENERAL: append_string("~"); break; case SelectorCombinator::Combinator::ADJACENT: append_string("+"); break; } append_optional_space(); // Add the post line break (from ruby sass) // Dart sass uses another logic for newlines if (sel->hasPostLineBreak()) { if (output_style() != COMPACT) { // append_optional_linefeed(); } } } } golibsass-1.0.0/libsass_src/src/inspect.hpp000066400000000000000000000071061405214413600207560ustar00rootroot00000000000000#ifndef SASS_INSPECT_H #define SASS_INSPECT_H #include "position.hpp" #include "operation.hpp" #include "emitter.hpp" namespace Sass { class Context; class Inspect : public Operation_CRTP, public Emitter { protected: // import all the class-specific methods and override as desired using Operation_CRTP::operator(); public: Inspect(const Emitter& emi); virtual ~Inspect(); // statements virtual void operator()(Block*); virtual void operator()(StyleRule*); virtual void operator()(Bubble*); virtual void operator()(SupportsRule*); virtual void operator()(AtRootRule*); virtual void operator()(AtRule*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Declaration*); virtual void operator()(Assignment*); virtual void operator()(Import*); virtual void operator()(Import_Stub*); virtual void operator()(WarningRule*); virtual void operator()(ErrorRule*); virtual void operator()(DebugRule*); virtual void operator()(Comment*); virtual void operator()(If*); virtual void operator()(ForRule*); virtual void operator()(EachRule*); virtual void operator()(WhileRule*); virtual void operator()(Return*); virtual void operator()(ExtendRule*); virtual void operator()(Definition*); virtual void operator()(Mixin_Call*); virtual void operator()(Content*); // expressions virtual void operator()(Map*); virtual void operator()(Function*); virtual void operator()(List*); virtual void operator()(Binary_Expression*); virtual void operator()(Unary_Expression*); virtual void operator()(Function_Call*); // virtual void operator()(Custom_Warning*); // virtual void operator()(Custom_Error*); virtual void operator()(Variable*); virtual void operator()(Number*); virtual void operator()(Color_RGBA*); virtual void operator()(Color_HSLA*); virtual void operator()(Boolean*); virtual void operator()(String_Schema*); virtual void operator()(String_Constant*); virtual void operator()(String_Quoted*); virtual void operator()(Custom_Error*); virtual void operator()(Custom_Warning*); virtual void operator()(SupportsOperation*); virtual void operator()(SupportsNegation*); virtual void operator()(SupportsDeclaration*); virtual void operator()(Supports_Interpolation*); virtual void operator()(MediaRule*); virtual void operator()(CssMediaRule*); virtual void operator()(CssMediaQuery*); virtual void operator()(Media_Query*); virtual void operator()(Media_Query_Expression*); virtual void operator()(At_Root_Query*); virtual void operator()(Null*); virtual void operator()(Parent_Reference* p); // parameters and arguments virtual void operator()(Parameter*); virtual void operator()(Parameters*); virtual void operator()(Argument*); virtual void operator()(Arguments*); // selectors virtual void operator()(Selector_Schema*); virtual void operator()(PlaceholderSelector*); virtual void operator()(TypeSelector*); virtual void operator()(ClassSelector*); virtual void operator()(IDSelector*); virtual void operator()(AttributeSelector*); virtual void operator()(PseudoSelector*); virtual void operator()(SelectorComponent*); virtual void operator()(SelectorCombinator*); virtual void operator()(CompoundSelector*); virtual void operator()(ComplexSelector*); virtual void operator()(SelectorList*); virtual sass::string lbracket(List*); virtual sass::string rbracket(List*); }; } #endif golibsass-1.0.0/libsass_src/src/json.cpp000066400000000000000000000773251405214413600202670ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE #endif #include "json.hpp" // include utf8 library used by libsass // ToDo: replace internal json utf8 code #include "utf8.h" #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1900 #include #ifdef snprintf #undef snprintf #endif extern "C" int snprintf(char *, size_t, const char *, ...); #endif #define out_of_memory() do { \ fprintf(stderr, "Out of memory.\n"); \ exit(EXIT_FAILURE); \ } while (0) /* Sadly, strdup is not portable. */ static char *json_strdup(const char *str) { char *ret = (char*) malloc(strlen(str) + 1); if (ret == NULL) out_of_memory(); strcpy(ret, str); return ret; } /* String buffer */ typedef struct { char *cur; char *end; char *start; } SB; static void sb_init(SB *sb) { sb->start = (char*) malloc(17); if (sb->start == NULL) out_of_memory(); sb->cur = sb->start; sb->end = sb->start + 16; } /* sb and need may be evaluated multiple times. */ #define sb_need(sb, need) do { \ if ((sb)->end - (sb)->cur < (need)) \ sb_grow(sb, need); \ } while (0) static void sb_grow(SB *sb, int need) { size_t length = sb->cur - sb->start; size_t alloc = sb->end - sb->start; do { alloc *= 2; } while (alloc < length + need); sb->start = (char*) realloc(sb->start, alloc + 1); if (sb->start == NULL) out_of_memory(); sb->cur = sb->start + length; sb->end = sb->start + alloc; } static void sb_put(SB *sb, const char *bytes, int count) { sb_need(sb, count); memcpy(sb->cur, bytes, count); sb->cur += count; } #define sb_putc(sb, c) do { \ if ((sb)->cur >= (sb)->end) \ sb_grow(sb, 1); \ *(sb)->cur++ = (c); \ } while (0) static void sb_puts(SB *sb, const char *str) { sb_put(sb, str, (int)strlen(str)); } static char *sb_finish(SB *sb) { *sb->cur = 0; assert(sb->start <= sb->cur && strlen(sb->start) == (size_t)(sb->cur - sb->start)); return sb->start; } static void sb_free(SB *sb) { free(sb->start); } /* * Unicode helper functions * * These are taken from the ccan/charset module and customized a bit. * Putting them here means the compiler can (choose to) inline them, * and it keeps ccan/json from having a dependency. * * We use uint32_t Type for Unicode codepoints. * We need our own because wchar_t might be 16 bits. */ /* * Validate a single UTF-8 character starting at @s. * The string must be null-terminated. * * If it's valid, return its length (1 thru 4). * If it's invalid or clipped, return 0. * * This function implements the syntax given in RFC3629, which is * the same as that given in The Unicode Standard, Version 6.0. * * It has the following properties: * * * All codepoints U+0000..U+10FFFF may be encoded, * except for U+D800..U+DFFF, which are reserved * for UTF-16 surrogate pair encoding. * * UTF-8 byte sequences longer than 4 bytes are not permitted, * as they exceed the range of Unicode. * * The sixty-six Unicode "non-characters" are permitted * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). */ static int utf8_validate_cz(const char *s) { unsigned char c = *s++; if (c <= 0x7F) { /* 00..7F */ return 1; } else if (c <= 0xC1) { /* 80..C1 */ /* Disallow overlong 2-byte sequence. */ return 0; } else if (c <= 0xDF) { /* C2..DF */ /* Make sure subsequent byte is in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 2; } else if (c <= 0xEF) { /* E0..EF */ /* Disallow overlong 3-byte sequence. */ if (c == 0xE0 && (unsigned char)*s < 0xA0) return 0; /* Disallow U+D800..U+DFFF. */ if (c == 0xED && (unsigned char)*s > 0x9F) return 0; /* Make sure subsequent bytes are in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 3; } else if (c <= 0xF4) { /* F0..F4 */ /* Disallow overlong 4-byte sequence. */ if (c == 0xF0 && (unsigned char)*s < 0x90) return 0; /* Disallow codepoints beyond U+10FFFF. */ if (c == 0xF4 && (unsigned char)*s > 0x8F) return 0; /* Make sure subsequent bytes are in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 4; } else { /* F5..FF */ return 0; } } /* Validate a null-terminated UTF-8 string. */ static bool utf8_validate(const char *s) { int len; for (; *s != 0; s += len) { len = utf8_validate_cz(s); if (len == 0) return false; } return true; } /* * Read a single UTF-8 character starting at @s, * returning the length, in bytes, of the character read. * * This function assumes input is valid UTF-8, * and that there are enough characters in front of @s. */ static int utf8_read_char(const char *s, uint32_t *out) { const unsigned char *c = (const unsigned char*) s; assert(utf8_validate_cz(s)); if (c[0] <= 0x7F) { /* 00..7F */ *out = c[0]; return 1; } else if (c[0] <= 0xDF) { /* C2..DF (unless input is invalid) */ *out = ((uint32_t)c[0] & 0x1F) << 6 | ((uint32_t)c[1] & 0x3F); return 2; } else if (c[0] <= 0xEF) { /* E0..EF */ *out = ((uint32_t)c[0] & 0xF) << 12 | ((uint32_t)c[1] & 0x3F) << 6 | ((uint32_t)c[2] & 0x3F); return 3; } else { /* F0..F4 (unless input is invalid) */ *out = ((uint32_t)c[0] & 0x7) << 18 | ((uint32_t)c[1] & 0x3F) << 12 | ((uint32_t)c[2] & 0x3F) << 6 | ((uint32_t)c[3] & 0x3F); return 4; } } /* * Write a single UTF-8 character to @s, * returning the length, in bytes, of the character written. * * @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF. * * This function will write up to 4 bytes to @out. */ static int utf8_write_char(uint32_t unicode, char *out) { unsigned char *o = (unsigned char*) out; assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF)); if (unicode <= 0x7F) { /* U+0000..U+007F */ *o++ = unicode; return 1; } else if (unicode <= 0x7FF) { /* U+0080..U+07FF */ *o++ = 0xC0 | unicode >> 6; *o++ = 0x80 | (unicode & 0x3F); return 2; } else if (unicode <= 0xFFFF) { /* U+0800..U+FFFF */ *o++ = 0xE0 | unicode >> 12; *o++ = 0x80 | (unicode >> 6 & 0x3F); *o++ = 0x80 | (unicode & 0x3F); return 3; } else { /* U+10000..U+10FFFF */ *o++ = 0xF0 | unicode >> 18; *o++ = 0x80 | (unicode >> 12 & 0x3F); *o++ = 0x80 | (unicode >> 6 & 0x3F); *o++ = 0x80 | (unicode & 0x3F); return 4; } } /* * Compute the Unicode codepoint of a UTF-16 surrogate pair. * * @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF. * If they aren't, this function returns false. */ static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uint32_t *unicode) { if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) { *unicode = 0x10000 + ((((uint32_t)uc & 0x3FF) << 10) | (lc & 0x3FF)); return true; } else { return false; } } /* * Construct a UTF-16 surrogate pair given a Unicode codepoint. * * @unicode must be U+10000..U+10FFFF. */ static void to_surrogate_pair(uint32_t unicode, uint16_t *uc, uint16_t *lc) { uint32_t n; assert(unicode >= 0x10000 && unicode <= 0x10FFFF); n = unicode - 0x10000; *uc = ((n >> 10) & 0x3FF) | 0xD800; *lc = (n & 0x3FF) | 0xDC00; } static bool is_space (const char *c); static bool is_digit (const char *c); static bool parse_value (const char **sp, JsonNode **out); static bool parse_string (const char **sp, char **out); static bool parse_number (const char **sp, double *out); static bool parse_array (const char **sp, JsonNode **out); static bool parse_object (const char **sp, JsonNode **out); static bool parse_hex16 (const char **sp, uint16_t *out); static bool expect_literal (const char **sp, const char *str); static void skip_space (const char **sp); static void emit_value (SB *out, const JsonNode *node); static void emit_value_indented (SB *out, const JsonNode *node, const char *space, int indent_level); static void emit_string (SB *out, const char *str); static void emit_number (SB *out, double num); static void emit_array (SB *out, const JsonNode *array); static void emit_array_indented (SB *out, const JsonNode *array, const char *space, int indent_level); static void emit_object (SB *out, const JsonNode *object); static void emit_object_indented (SB *out, const JsonNode *object, const char *space, int indent_level); static int write_hex16(char *out, uint16_t val); static JsonNode *mknode(JsonTag tag); static void append_node(JsonNode *parent, JsonNode *child); static void prepend_node(JsonNode *parent, JsonNode *child); static void append_member(JsonNode *object, char *key, JsonNode *value); /* Assertion-friendly validity checks */ static bool tag_is_valid(unsigned int tag); static bool number_is_valid(const char *num); JsonNode *json_decode(const char *json) { const char *s = json; JsonNode *ret; skip_space(&s); if (!parse_value(&s, &ret)) return NULL; skip_space(&s); if (*s != 0) { json_delete(ret); return NULL; } return ret; } char *json_encode(const JsonNode *node) { return json_stringify(node, NULL); } char *json_encode_string(const char *str) { SB sb; sb_init(&sb); try { emit_string(&sb, str); } catch (std::exception&) { sb_free(&sb); throw; } return sb_finish(&sb); } char *json_stringify(const JsonNode *node, const char *space) { SB sb; sb_init(&sb); try { if (space != NULL) emit_value_indented(&sb, node, space, 0); else emit_value(&sb, node); } catch (std::exception&) { sb_free(&sb); throw; } return sb_finish(&sb); } void json_delete(JsonNode *node) { if (node != NULL) { json_remove_from_parent(node); switch (node->tag) { case JSON_STRING: free(node->string_); break; case JSON_ARRAY: case JSON_OBJECT: { JsonNode *child, *next; for (child = node->children.head; child != NULL; child = next) { next = child->next; json_delete(child); } break; } default:; } free(node); } } bool json_validate(const char *json) { const char *s = json; skip_space(&s); if (!parse_value(&s, NULL)) return false; skip_space(&s); if (*s != 0) return false; return true; } JsonNode *json_find_element(JsonNode *array, int index) { JsonNode *element; int i = 0; if (array == NULL || array->tag != JSON_ARRAY) return NULL; json_foreach(element, array) { if (i == index) return element; i++; } return NULL; } JsonNode *json_find_member(JsonNode *object, const char *name) { JsonNode *member; if (object == NULL || object->tag != JSON_OBJECT) return NULL; json_foreach(member, object) if (strcmp(member->key, name) == 0) return member; return NULL; } JsonNode *json_first_child(const JsonNode *node) { if (node != NULL && (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT)) return node->children.head; return NULL; } static JsonNode *mknode(JsonTag tag) { JsonNode *ret = (JsonNode*) calloc(1, sizeof(JsonNode)); if (ret == NULL) out_of_memory(); ret->tag = tag; return ret; } JsonNode *json_mknull(void) { return mknode(JSON_NULL); } JsonNode *json_mkbool(bool b) { JsonNode *ret = mknode(JSON_BOOL); ret->bool_ = b; return ret; } static JsonNode *mkstring(char *s) { JsonNode *ret = mknode(JSON_STRING); ret->string_ = s; return ret; } JsonNode *json_mkstring(const char *s) { return mkstring(json_strdup(s)); } JsonNode *json_mknumber(double n) { JsonNode *node = mknode(JSON_NUMBER); node->number_ = n; return node; } JsonNode *json_mkarray(void) { return mknode(JSON_ARRAY); } JsonNode *json_mkobject(void) { return mknode(JSON_OBJECT); } static void append_node(JsonNode *parent, JsonNode *child) { if (child != NULL && parent != NULL) { child->parent = parent; child->prev = parent->children.tail; child->next = NULL; if (parent->children.tail != NULL) parent->children.tail->next = child; else parent->children.head = child; parent->children.tail = child; } } static void prepend_node(JsonNode *parent, JsonNode *child) { if (child != NULL && parent != NULL) { child->parent = parent; child->prev = NULL; child->next = parent->children.head; if (parent->children.head != NULL) parent->children.head->prev = child; else parent->children.tail = child; parent->children.head = child; } } static void append_member(JsonNode *object, char *key, JsonNode *value) { if (value != NULL && object != NULL) { value->key = key; append_node(object, value); } } void json_append_element(JsonNode *array, JsonNode *element) { if (array != NULL && element !=NULL) { assert(array->tag == JSON_ARRAY); assert(element->parent == NULL); append_node(array, element); } } void json_prepend_element(JsonNode *array, JsonNode *element) { assert(array->tag == JSON_ARRAY); assert(element->parent == NULL); prepend_node(array, element); } void json_append_member(JsonNode *object, const char *key, JsonNode *value) { if (object != NULL && key != NULL && value != NULL) { assert(object->tag == JSON_OBJECT); assert(value->parent == NULL); append_member(object, json_strdup(key), value); } } void json_prepend_member(JsonNode *object, const char *key, JsonNode *value) { if (object != NULL && key != NULL && value != NULL) { assert(object->tag == JSON_OBJECT); assert(value->parent == NULL); value->key = json_strdup(key); prepend_node(object, value); } } void json_remove_from_parent(JsonNode *node) { if (node != NULL) { JsonNode *parent = node->parent; if (parent != NULL) { if (node->prev != NULL) node->prev->next = node->next; else parent->children.head = node->next; if (node->next != NULL) node->next->prev = node->prev; else parent->children.tail = node->prev; free(node->key); node->parent = NULL; node->prev = node->next = NULL; node->key = NULL; } } } static bool parse_value(const char **sp, JsonNode **out) { const char *s = *sp; switch (*s) { case 'n': if (expect_literal(&s, "null")) { if (out) *out = json_mknull(); *sp = s; return true; } return false; case 'f': if (expect_literal(&s, "false")) { if (out) *out = json_mkbool(false); *sp = s; return true; } return false; case 't': if (expect_literal(&s, "true")) { if (out) *out = json_mkbool(true); *sp = s; return true; } return false; case '"': { char *str = NULL; if (parse_string(&s, out ? &str : NULL)) { if (out) *out = mkstring(str); *sp = s; return true; } return false; } case '[': if (parse_array(&s, out)) { *sp = s; return true; } return false; case '{': if (parse_object(&s, out)) { *sp = s; return true; } return false; default: { double num; if (parse_number(&s, out ? &num : NULL)) { if (out) *out = json_mknumber(num); *sp = s; return true; } return false; } } } static bool parse_array(const char **sp, JsonNode **out) { const char *s = *sp; JsonNode *ret = out ? json_mkarray() : NULL; JsonNode *element = NULL; if (*s++ != '[') goto failure; skip_space(&s); if (*s == ']') { s++; goto success; } for (;;) { if (!parse_value(&s, out ? &element : NULL)) goto failure; skip_space(&s); if (out) json_append_element(ret, element); if (*s == ']') { s++; goto success; } if (*s++ != ',') goto failure; skip_space(&s); } success: *sp = s; if (out) *out = ret; return true; failure: json_delete(ret); return false; } static bool parse_object(const char **sp, JsonNode **out) { const char *s = *sp; JsonNode *ret = out ? json_mkobject() : NULL; char *key = NULL; JsonNode *value = NULL; if (*s++ != '{') goto failure; skip_space(&s); if (*s == '}') { s++; goto success; } for (;;) { if (!parse_string(&s, out ? &key : NULL)) goto failure; skip_space(&s); if (*s++ != ':') goto failure_free_key; skip_space(&s); if (!parse_value(&s, out ? &value : NULL)) goto failure_free_key; skip_space(&s); if (out) append_member(ret, key, value); if (*s == '}') { s++; goto success; } if (*s++ != ',') goto failure; skip_space(&s); } success: *sp = s; if (out) *out = ret; return true; failure_free_key: if (out) free(key); failure: json_delete(ret); return false; } bool parse_string(const char **sp, char **out) { const char *s = *sp; SB sb = { 0, 0, 0 }; char throwaway_buffer[4]; /* enough space for a UTF-8 character */ char *b; if (*s++ != '"') return false; if (out) { sb_init(&sb); sb_need(&sb, 4); b = sb.cur; } else { b = throwaway_buffer; } while (*s != '"') { unsigned char c = *s++; /* Parse next character, and write it to b. */ if (c == '\\') { c = *s++; switch (c) { case '"': case '\\': case '/': *b++ = c; break; case 'b': *b++ = '\b'; break; case 'f': *b++ = '\f'; break; case 'n': *b++ = '\n'; break; case 'r': *b++ = '\r'; break; case 't': *b++ = '\t'; break; case 'u': { uint16_t uc, lc; uint32_t unicode; if (!parse_hex16(&s, &uc)) goto failed; if (uc >= 0xD800 && uc <= 0xDFFF) { /* Handle UTF-16 surrogate pair. */ if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc)) goto failed; /* Incomplete surrogate pair. */ if (!from_surrogate_pair(uc, lc, &unicode)) goto failed; /* Invalid surrogate pair. */ } else if (uc == 0) { /* Disallow "\u0000". */ goto failed; } else { unicode = uc; } b += utf8_write_char(unicode, b); break; } default: /* Invalid escape */ goto failed; } } else if (c <= 0x1F) { /* Control characters are not allowed in string literals. */ goto failed; } else { /* Validate and echo a UTF-8 character. */ int len; s--; len = utf8_validate_cz(s); if (len == 0) goto failed; /* Invalid UTF-8 character. */ while (len--) *b++ = *s++; } /* * Update sb to know about the new bytes, * and set up b to write another character. */ if (out) { sb.cur = b; sb_need(&sb, 4); b = sb.cur; } else { b = throwaway_buffer; } } s++; if (out) *out = sb_finish(&sb); *sp = s; return true; failed: if (out) sb_free(&sb); return false; } bool is_space(const char *c) { return ((*c) == '\t' || (*c) == '\n' || (*c) == '\r' || (*c) == ' '); } bool is_digit(const char *c){ return ((*c) >= '0' && (*c) <= '9'); } /* * The JSON spec says that a number shall follow this precise pattern * (spaces and quotes added for readability): * '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)? * * However, some JSON parsers are more liberal. For instance, PHP accepts * '.5' and '1.'. JSON.parse accepts '+3'. * * This function takes the strict approach. */ bool parse_number(const char **sp, double *out) { const char *s = *sp; /* '-'? */ if (*s == '-') s++; /* (0 | [1-9][0-9]*) */ if (*s == '0') { s++; } else { if (!is_digit(s)) return false; do { s++; } while (is_digit(s)); } /* ('.' [0-9]+)? */ if (*s == '.') { s++; if (!is_digit(s)) return false; do { s++; } while (is_digit(s)); } /* ([Ee] [+-]? [0-9]+)? */ if (*s == 'E' || *s == 'e') { s++; if (*s == '+' || *s == '-') s++; if (!is_digit(s)) return false; do { s++; } while (is_digit(s)); } if (out) *out = strtod(*sp, NULL); *sp = s; return true; } static void skip_space(const char **sp) { const char *s = *sp; while (is_space(s)) s++; *sp = s; } static void emit_value(SB *out, const JsonNode *node) { assert(tag_is_valid(node->tag)); switch (node->tag) { case JSON_NULL: sb_puts(out, "null"); break; case JSON_BOOL: sb_puts(out, node->bool_ ? "true" : "false"); break; case JSON_STRING: emit_string(out, node->string_); break; case JSON_NUMBER: emit_number(out, node->number_); break; case JSON_ARRAY: emit_array(out, node); break; case JSON_OBJECT: emit_object(out, node); break; default: assert(false); } } void emit_value_indented(SB *out, const JsonNode *node, const char *space, int indent_level) { assert(tag_is_valid(node->tag)); switch (node->tag) { case JSON_NULL: sb_puts(out, "null"); break; case JSON_BOOL: sb_puts(out, node->bool_ ? "true" : "false"); break; case JSON_STRING: emit_string(out, node->string_); break; case JSON_NUMBER: emit_number(out, node->number_); break; case JSON_ARRAY: emit_array_indented(out, node, space, indent_level); break; case JSON_OBJECT: emit_object_indented(out, node, space, indent_level); break; default: assert(false); } } static void emit_array(SB *out, const JsonNode *array) { const JsonNode *element; sb_putc(out, '['); json_foreach(element, array) { emit_value(out, element); if (element->next != NULL) sb_putc(out, ','); } sb_putc(out, ']'); } static void emit_array_indented(SB *out, const JsonNode *array, const char *space, int indent_level) { const JsonNode *element = array->children.head; int i; if (element == NULL) { sb_puts(out, "[]"); return; } sb_puts(out, "[\n"); while (element != NULL) { for (i = 0; i < indent_level + 1; i++) sb_puts(out, space); emit_value_indented(out, element, space, indent_level + 1); element = element->next; sb_puts(out, element != NULL ? ",\n" : "\n"); } for (i = 0; i < indent_level; i++) sb_puts(out, space); sb_putc(out, ']'); } static void emit_object(SB *out, const JsonNode *object) { const JsonNode *member; sb_putc(out, '{'); json_foreach(member, object) { emit_string(out, member->key); sb_putc(out, ':'); emit_value(out, member); if (member->next != NULL) sb_putc(out, ','); } sb_putc(out, '}'); } static void emit_object_indented(SB *out, const JsonNode *object, const char *space, int indent_level) { const JsonNode *member = object->children.head; int i; if (member == NULL) { sb_puts(out, "{}"); return; } sb_puts(out, "{\n"); while (member != NULL) { for (i = 0; i < indent_level + 1; i++) sb_puts(out, space); emit_string(out, member->key); sb_puts(out, ": "); emit_value_indented(out, member, space, indent_level + 1); member = member->next; sb_puts(out, member != NULL ? ",\n" : "\n"); } for (i = 0; i < indent_level; i++) sb_puts(out, space); sb_putc(out, '}'); } void emit_string(SB *out, const char *str) { bool escape_unicode = false; const char *s = str; char *b; // make assertion catchable #ifndef NDEBUG if (!utf8_validate(str)) { throw utf8::invalid_utf8(0); } #endif assert(utf8_validate(str)); /* * 14 bytes is enough space to write up to two * \uXXXX escapes and two quotation marks. */ sb_need(out, 14); b = out->cur; *b++ = '"'; while (*s != 0) { unsigned char c = *s++; /* Encode the next character, and write it to b. */ switch (c) { case '"': *b++ = '\\'; *b++ = '"'; break; case '\\': *b++ = '\\'; *b++ = '\\'; break; case '\b': *b++ = '\\'; *b++ = 'b'; break; case '\f': *b++ = '\\'; *b++ = 'f'; break; case '\n': *b++ = '\\'; *b++ = 'n'; break; case '\r': *b++ = '\\'; *b++ = 'r'; break; case '\t': *b++ = '\\'; *b++ = 't'; break; default: { int len; s--; len = utf8_validate_cz(s); if (len == 0) { /* * Handle invalid UTF-8 character gracefully in production * by writing a replacement character (U+FFFD) * and skipping a single byte. * * This should never happen when assertions are enabled * due to the assertion at the beginning of this function. */ assert(false); if (escape_unicode) { strcpy(b, "\\uFFFD"); b += 6; } else { *b++ = 0xEFu; *b++ = 0xBFu; *b++ = 0xBDu; } s++; } else if (c < 0x1F || (c >= 0x80 && escape_unicode)) { /* Encode using \u.... */ uint32_t unicode; s += utf8_read_char(s, &unicode); if (unicode <= 0xFFFF) { *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, unicode); } else { /* Produce a surrogate pair. */ uint16_t uc, lc; assert(unicode <= 0x10FFFF); to_surrogate_pair(unicode, &uc, &lc); *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, uc); *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, lc); } } else { /* Write the character directly. */ while (len--) *b++ = *s++; } break; } } /* * Update *out to know about the new bytes, * and set up b to write another encoded character. */ out->cur = b; sb_need(out, 14); b = out->cur; } *b++ = '"'; out->cur = b; } static void emit_number(SB *out, double num) { /* * This isn't exactly how JavaScript renders numbers, * but it should produce valid JSON for reasonable numbers * preserve precision well enough, and avoid some oddities * like 0.3 -> 0.299999999999999988898 . */ char buf[64]; sprintf(buf, "%.16g", num); if (number_is_valid(buf)) sb_puts(out, buf); else sb_puts(out, "null"); } static bool tag_is_valid(unsigned int tag) { return (/* tag >= JSON_NULL && */ tag <= JSON_OBJECT); } static bool number_is_valid(const char *num) { return (parse_number(&num, NULL) && *num == '\0'); } static bool expect_literal(const char **sp, const char *str) { const char *s = *sp; while (*str != '\0') if (*s++ != *str++) return false; *sp = s; return true; } /* * Parses exactly 4 hex characters (capital or lowercase). * Fails if any input chars are not [0-9A-Fa-f]. */ static bool parse_hex16(const char **sp, uint16_t *out) { const char *s = *sp; uint16_t ret = 0; uint16_t i; uint16_t tmp; char c; for (i = 0; i < 4; i++) { c = *s++; if (c >= '0' && c <= '9') tmp = c - '0'; else if (c >= 'A' && c <= 'F') tmp = c - 'A' + 10; else if (c >= 'a' && c <= 'f') tmp = c - 'a' + 10; else return false; ret <<= 4; ret += tmp; } if (out) *out = ret; *sp = s; return true; } /* * Encodes a 16-bit number into hexadecimal, * writing exactly 4 hex chars. */ static int write_hex16(char *out, uint16_t val) { const char *hex = "0123456789ABCDEF"; *out++ = hex[(val >> 12) & 0xF]; *out++ = hex[(val >> 8) & 0xF]; *out++ = hex[(val >> 4) & 0xF]; *out++ = hex[ val & 0xF]; return 4; } bool json_check(const JsonNode *node, char errmsg[256]) { #define problem(...) do { \ if (errmsg != NULL) \ snprintf(errmsg, 256, __VA_ARGS__); \ return false; \ } while (0) if (node->key != NULL && !utf8_validate(node->key)) problem("key contains invalid UTF-8"); if (!tag_is_valid(node->tag)) problem("tag is invalid (%u)", node->tag); if (node->tag == JSON_BOOL) { if (node->bool_ != false && node->bool_ != true) problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true); } else if (node->tag == JSON_STRING) { if (node->string_ == NULL) problem("string_ is NULL"); if (!utf8_validate(node->string_)) problem("string_ contains invalid UTF-8"); } else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) { JsonNode *head = node->children.head; JsonNode *tail = node->children.tail; if (head == NULL || tail == NULL) { if (head != NULL) problem("tail is NULL, but head is not"); if (tail != NULL) problem("head is NULL, but tail is not"); } else { JsonNode *child; JsonNode *last = NULL; if (head->prev != NULL) problem("First child's prev pointer is not NULL"); for (child = head; child != NULL; last = child, child = child->next) { if (child == node) problem("node is its own child"); if (child->next == child) problem("child->next == child (cycle)"); if (child->next == head) problem("child->next == head (cycle)"); if (child->parent != node) problem("child does not point back to parent"); if (child->next != NULL && child->next->prev != child) problem("child->next does not point back to child"); if (node->tag == JSON_ARRAY && child->key != NULL) problem("Array element's key is not NULL"); if (node->tag == JSON_OBJECT && child->key == NULL) problem("Object member's key is NULL"); if (!json_check(child, errmsg)) return false; } if (last != tail) problem("tail does not match pointer found by starting at head and following next links"); } } return true; #undef problem } golibsass-1.0.0/libsass_src/src/json.hpp000066400000000000000000000067061405214413600202670ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CCAN_JSON_H #define CCAN_JSON_H #include #include typedef enum { JSON_NULL, JSON_BOOL, JSON_STRING, JSON_NUMBER, JSON_ARRAY, JSON_OBJECT, } JsonTag; typedef struct JsonNode JsonNode; struct JsonNode { /* only if parent is an object or array (NULL otherwise) */ JsonNode *parent; JsonNode *prev, *next; /* only if parent is an object (NULL otherwise) */ char *key; /* Must be valid UTF-8. */ JsonTag tag; union { /* JSON_BOOL */ bool bool_; /* JSON_STRING */ char *string_; /* Must be valid UTF-8. */ /* JSON_NUMBER */ double number_; /* JSON_ARRAY */ /* JSON_OBJECT */ struct { JsonNode *head, *tail; } children; }; }; /*** Encoding, decoding, and validation ***/ JsonNode *json_decode (const char *json); char *json_encode (const JsonNode *node); char *json_encode_string (const char *str); char *json_stringify (const JsonNode *node, const char *space); void json_delete (JsonNode *node); bool json_validate (const char *json); /*** Lookup and traversal ***/ JsonNode *json_find_element (JsonNode *array, int index); JsonNode *json_find_member (JsonNode *object, const char *key); JsonNode *json_first_child (const JsonNode *node); #define json_foreach(i, object_or_array) \ for ((i) = json_first_child(object_or_array); \ (i) != NULL; \ (i) = (i)->next) /*** Construction and manipulation ***/ JsonNode *json_mknull(void); JsonNode *json_mkbool(bool b); JsonNode *json_mkstring(const char *s); JsonNode *json_mknumber(double n); JsonNode *json_mkarray(void); JsonNode *json_mkobject(void); void json_append_element(JsonNode *array, JsonNode *element); void json_prepend_element(JsonNode *array, JsonNode *element); void json_append_member(JsonNode *object, const char *key, JsonNode *value); void json_prepend_member(JsonNode *object, const char *key, JsonNode *value); void json_remove_from_parent(JsonNode *node); /*** Debugging ***/ /* * Look for structure and encoding problems in a JsonNode or its descendents. * * If a problem is detected, return false, writing a description of the problem * to errmsg (unless errmsg is NULL). */ bool json_check(const JsonNode *node, char errmsg[256]); #endif golibsass-1.0.0/libsass_src/src/kwd_arg_macros.hpp000066400000000000000000000010021405214413600222600ustar00rootroot00000000000000#ifndef SASS_KWD_ARG_MACROS_H #define SASS_KWD_ARG_MACROS_H // Example usage: // KWD_ARG_SET(Args) { // KWD_ARG(Args, string, foo); // KWD_ARG(Args, int, bar); // ... // }; // // ... and later ... // // something(Args().foo("hey").bar(3)); #define KWD_ARG_SET(set_name) class set_name #define KWD_ARG(set_name, type, name) \ private: \ type name##_; \ public: \ set_name& name(type name##__) { \ name##_ = name##__; \ return *this; \ } \ type name() { return name##_; } \ private: #endif golibsass-1.0.0/libsass_src/src/lexer.cpp000066400000000000000000000114371405214413600204250ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "lexer.hpp" #include "constants.hpp" #include "util_string.hpp" namespace Sass { using namespace Constants; namespace Prelexer { //#################################### // BASIC CHARACTER MATCHERS //#################################### // Match standard control chars const char* kwd_at(const char* src) { return exactly<'@'>(src); } const char* kwd_dot(const char* src) { return exactly<'.'>(src); } const char* kwd_comma(const char* src) { return exactly<','>(src); }; const char* kwd_colon(const char* src) { return exactly<':'>(src); }; const char* kwd_star(const char* src) { return exactly<'*'>(src); }; const char* kwd_plus(const char* src) { return exactly<'+'>(src); }; const char* kwd_minus(const char* src) { return exactly<'-'>(src); }; const char* kwd_slash(const char* src) { return exactly<'/'>(src); }; bool is_number(char chr) { return Util::ascii_isdigit(static_cast(chr)) || chr == '-' || chr == '+'; } // check if char is within a reduced ascii range // valid in a uri (copied from Ruby Sass) bool is_uri_character(char chr) { unsigned int cmp = unsigned(chr); return (cmp > 41 && cmp < 127) || cmp == ':' || cmp == '/' || cmp == '|'; } // check if char is within a reduced ascii range // valid for escaping (copied from Ruby Sass) bool is_escapable_character(char chr) { unsigned int cmp = unsigned(chr); return cmp > 31 && cmp < 127; } // Match word character (look ahead) bool is_character(char chr) { // valid alpha, numeric or unicode char (plus hyphen) return Util::ascii_isalnum(static_cast(chr)) || !Util::ascii_isascii(static_cast(chr)) || chr == '-'; } //#################################### // BASIC CLASS MATCHERS //#################################### // create matchers that advance the position const char* space(const char* src) { return Util::ascii_isspace(static_cast(*src)) ? src + 1 : nullptr; } const char* alpha(const char* src) { return Util::ascii_isalpha(static_cast(*src)) ? src + 1 : nullptr; } const char* nonascii(const char* src) { return Util::ascii_isascii(static_cast(*src)) ? nullptr : src + 1; } const char* digit(const char* src) { return Util::ascii_isdigit(static_cast(*src)) ? src + 1 : nullptr; } const char* xdigit(const char* src) { return Util::ascii_isxdigit(static_cast(*src)) ? src + 1 : nullptr; } const char* alnum(const char* src) { return Util::ascii_isalnum(static_cast(*src)) ? src + 1 : nullptr; } const char* hyphen(const char* src) { return *src == '-' ? src + 1 : 0; } const char* uri_character(const char* src) { return is_uri_character(*src) ? src + 1 : 0; } const char* escapable_character(const char* src) { return is_escapable_character(*src) ? src + 1 : 0; } // Match multiple ctype characters. const char* spaces(const char* src) { return one_plus(src); } const char* digits(const char* src) { return one_plus(src); } const char* hyphens(const char* src) { return one_plus(src); } // Whitespace handling. const char* no_spaces(const char* src) { return negate< space >(src); } const char* optional_spaces(const char* src) { return zero_plus< space >(src); } // Match any single character. const char* any_char(const char* src) { return *src ? src + 1 : src; } // Match word boundary (zero-width lookahead). const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; } // Match linefeed /(?:\n|\r\n?|\f)/ const char* re_linebreak(const char* src) { // end of file or unix linefeed return here if (*src == 0) return src; // end of file or unix linefeed return here if (*src == '\n' || *src == '\f') return src + 1; // a carriage return may optionally be followed by a linefeed if (*src == '\r') return *(src + 1) == '\n' ? src + 2 : src + 1; // no linefeed return 0; } // Assert string boundaries (/\Z|\z|\A/) // This is a zero-width positive lookahead const char* end_of_line(const char* src) { // end of file or unix linefeed return here return *src == 0 || *src == '\n' || *src == '\r' || *src == '\f' ? src : 0; } // Assert end_of_file boundary (/\z/) // This is a zero-width positive lookahead const char* end_of_file(const char* src) { // end of file or unix linefeed return here return *src == 0 ? src : 0; } } } golibsass-1.0.0/libsass_src/src/lexer.hpp000066400000000000000000000214571405214413600204350ustar00rootroot00000000000000#ifndef SASS_LEXER_H #define SASS_LEXER_H #include namespace Sass { namespace Prelexer { //#################################### // BASIC CHARACTER MATCHERS //#################################### // Match standard control chars const char* kwd_at(const char* src); const char* kwd_dot(const char* src); const char* kwd_comma(const char* src); const char* kwd_colon(const char* src); const char* kwd_star(const char* src); const char* kwd_plus(const char* src); const char* kwd_minus(const char* src); const char* kwd_slash(const char* src); //#################################### // BASIC CLASS MATCHERS //#################################### // Matches ASCII digits, +, and -. bool is_number(char src); bool is_uri_character(char src); bool escapable_character(char src); // Match a single ctype predicate. const char* space(const char* src); const char* alpha(const char* src); const char* digit(const char* src); const char* xdigit(const char* src); const char* alnum(const char* src); const char* hyphen(const char* src); const char* nonascii(const char* src); const char* uri_character(const char* src); const char* escapable_character(const char* src); // Match multiple ctype characters. const char* spaces(const char* src); const char* digits(const char* src); const char* hyphens(const char* src); // Whitespace handling. const char* no_spaces(const char* src); const char* optional_spaces(const char* src); // Match any single character (/./). const char* any_char(const char* src); // Assert word boundary (/\b/) // Is a zero-width positive lookaheads const char* word_boundary(const char* src); // Match a single linebreak (/(?:\n|\r\n?)/). const char* re_linebreak(const char* src); // Assert string boundaries (/\Z|\z|\A/) // There are zero-width positive lookaheads const char* end_of_line(const char* src); // Assert end_of_file boundary (/\z/) const char* end_of_file(const char* src); // const char* start_of_string(const char* src); // Type definition for prelexer functions typedef const char* (*prelexer)(const char*); //#################################### // BASIC "REGEX" CONSTRUCTORS //#################################### // Match a single character literal. // Regex equivalent: /(?:x)/ template const char* exactly(const char* src) { return *src == chr ? src + 1 : 0; } // Match the full string literal. // Regex equivalent: /(?:literal)/ template const char* exactly(const char* src) { if (str == NULL) return 0; const char* pre = str; if (src == NULL) return 0; // there is a small chance that the search string // is longer than the rest of the string to look at while (*pre && *src == *pre) { ++src, ++pre; } // did the matcher finish? return *pre == 0 ? src : 0; } // Match a single character literal. // Regex equivalent: /(?:x)/i // only define lower case alpha chars template const char* insensitive(const char* src) { return *src == chr || *src+32 == chr ? src + 1 : 0; } // Match the full string literal. // Regex equivalent: /(?:literal)/i // only define lower case alpha chars template const char* insensitive(const char* src) { if (str == NULL) return 0; const char* pre = str; if (src == NULL) return 0; // there is a small chance that the search string // is longer than the rest of the string to look at while (*pre && (*src == *pre || *src+32 == *pre)) { ++src, ++pre; } // did the matcher finish? return *pre == 0 ? src : 0; } // Match for members of char class. // Regex equivalent: /[axy]/ template const char* class_char(const char* src) { const char* cc = char_class; while (*cc && *src != *cc) ++cc; return *cc ? src + 1 : 0; } // Match for members of char class. // Regex equivalent: /[axy]+/ template const char* class_chars(const char* src) { const char* p = src; while (class_char(p)) ++p; return p == src ? 0 : p; } // Match for members of char class. // Regex equivalent: /[^axy]/ template const char* neg_class_char(const char* src) { if (*src == 0) return 0; const char* cc = neg_char_class; while (*cc && *src != *cc) ++cc; return *cc ? 0 : src + 1; } // Match for members of char class. // Regex equivalent: /[^axy]+/ template const char* neg_class_chars(const char* src) { const char* p = src; while (neg_class_char(p)) ++p; return p == src ? 0 : p; } // Match all except the supplied one. // Regex equivalent: /[^x]/ template const char* any_char_but(const char* src) { return (*src && *src != chr) ? src + 1 : 0; } // Succeeds if the matcher fails. // Aka. zero-width negative lookahead. // Regex equivalent: /(?!literal)/ template const char* negate(const char* src) { return mx(src) ? 0 : src; } // Succeeds if the matcher succeeds. // Aka. zero-width positive lookahead. // Regex equivalent: /(?=literal)/ // just hangs around until we need it template const char* lookahead(const char* src) { return mx(src) ? src : 0; } // Tries supplied matchers in order. // Succeeds if one of them succeeds. // Regex equivalent: /(?:FOO|BAR)/ template const char* alternatives(const char* src) { const char* rslt; if ((rslt = mx(src))) return rslt; return 0; } template const char* alternatives(const char* src) { const char* rslt; if ((rslt = mx1(src))) return rslt; return alternatives(src); } // Tries supplied matchers in order. // Succeeds if all of them succeeds. // Regex equivalent: /(?:FOO)(?:BAR)/ template const char* sequence(const char* src) { const char* rslt = src; if (!(rslt = mx1(rslt))) return 0; return rslt; } template const char* sequence(const char* src) { const char* rslt = src; if (!(rslt = mx1(rslt))) return 0; return sequence(rslt); } // Match a pattern or not. Always succeeds. // Regex equivalent: /(?:literal)?/ template const char* optional(const char* src) { const char* p = mx(src); return p ? p : src; } // Match zero or more of the patterns. // Regex equivalent: /(?:literal)*/ template const char* zero_plus(const char* src) { const char* p = mx(src); while (p) src = p, p = mx(src); return src; } // Match one or more of the patterns. // Regex equivalent: /(?:literal)+/ template const char* one_plus(const char* src) { const char* p = mx(src); if (!p) return 0; while (p) src = p, p = mx(src); return src; } // Match mx non-greedy until delimiter. // Other prelexers are greedy by default. // Regex equivalent: /(?:$mx)*?(?=$delim)\b/ template const char* non_greedy(const char* src) { while (!delim(src)) { const char* p = mx(src); if (p == src) return 0; if (p == 0) return 0; src = p; } return src; } //#################################### // ADVANCED "REGEX" CONSTRUCTORS //#################################### // Match with word boundary rule. // Regex equivalent: /(?:$mx)\b/i template const char* keyword(const char* src) { return sequence < insensitive < str >, word_boundary >(src); } // Match with word boundary rule. // Regex equivalent: /(?:$mx)\b/ template const char* word(const char* src) { return sequence < exactly < str >, word_boundary >(src); } template const char* loosely(const char* src) { return sequence < optional_spaces, exactly < chr > >(src); } template const char* loosely(const char* src) { return sequence < optional_spaces, exactly < str > >(src); } } } #endif golibsass-1.0.0/libsass_src/src/listize.cpp000066400000000000000000000034331405214413600207660ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include "listize.hpp" #include "context.hpp" #include "backtrace.hpp" #include "error_handling.hpp" namespace Sass { Listize::Listize() { } Expression* Listize::perform(AST_Node* node) { Listize listize; return node->perform(&listize); } Expression* Listize::operator()(SelectorList* sel) { List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); l->from_selector(true); for (size_t i = 0, L = sel->length(); i < L; ++i) { if (!sel->at(i)) continue; l->append(sel->at(i)->perform(this)); } if (l->length()) return l.detach(); return SASS_MEMORY_NEW(Null, l->pstate()); } Expression* Listize::operator()(CompoundSelector* sel) { sass::string str; for (size_t i = 0, L = sel->length(); i < L; ++i) { Expression* e = (*sel)[i]->perform(this); if (e) str += e->to_string(); } return SASS_MEMORY_NEW(String_Quoted, sel->pstate(), str); } Expression* Listize::operator()(ComplexSelector* sel) { List_Obj l = SASS_MEMORY_NEW(List, sel->pstate()); // ToDo: investigate what this does // Note: seems reated to parent ref l->from_selector(true); for (auto component : sel->elements()) { if (CompoundSelectorObj compound = Cast(component)) { if (!compound->empty()) { ExpressionObj hh = compound->perform(this); if (hh) l->append(hh); } } else if (component) { l->append(SASS_MEMORY_NEW(String_Quoted, component->pstate(), component->to_string())); } } if (l->length() == 0) return 0; return l.detach(); } } golibsass-1.0.0/libsass_src/src/listize.hpp000066400000000000000000000012631405214413600207720ustar00rootroot00000000000000#ifndef SASS_LISTIZE_H #define SASS_LISTIZE_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast_fwd_decl.hpp" #include "operation.hpp" namespace Sass { struct Backtrace; class Listize : public Operation_CRTP { public: static Expression* perform(AST_Node* node); public: Listize(); ~Listize() { } Expression* operator()(SelectorList*); Expression* operator()(ComplexSelector*); Expression* operator()(CompoundSelector*); // generic fallback template Expression* fallback(U x) { return Cast(x); } }; } #endif golibsass-1.0.0/libsass_src/src/mapping.hpp000066400000000000000000000006021405214413600207360ustar00rootroot00000000000000#ifndef SASS_MAPPING_H #define SASS_MAPPING_H #include "position.hpp" #include "backtrace.hpp" namespace Sass { struct Mapping { Position original_position; Position generated_position; Mapping(const Position& original_position, const Position& generated_position) : original_position(original_position), generated_position(generated_position) { } }; } #endif golibsass-1.0.0/libsass_src/src/memory.hpp000066400000000000000000000003501405214413600206130ustar00rootroot00000000000000#ifndef SASS_MEMORY_H #define SASS_MEMORY_H #include "settings.hpp" // Include memory headers #include "memory/config.hpp" #include "memory/allocator.hpp" #include "memory/shared_ptr.hpp" #include "memory/memory_pool.hpp" #endif golibsass-1.0.0/libsass_src/src/memory/000077500000000000000000000000001405214413600201045ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/src/memory/allocator.cpp000066400000000000000000000020411405214413600225650ustar00rootroot00000000000000#include "../sass.hpp" #include "allocator.hpp" #include "memory_pool.hpp" #if defined (_MSC_VER) // Visual studio #define thread_local __declspec( thread ) #elif defined (__GCC__) // GCC #define thread_local __thread #endif namespace Sass { #ifdef SASS_CUSTOM_ALLOCATOR // Only use PODs for thread_local // Objects get unpredictable init order static thread_local MemoryPool* pool; static thread_local size_t allocations; void* allocateMem(size_t size) { if (pool == nullptr) { pool = new MemoryPool(); } allocations++; return pool->allocate(size); } void deallocateMem(void* ptr, size_t size) { // It seems thread_local variable might be discharged!? // But the destructors of e.g. static strings is still // called, although their memory was discharged too. // Fine with me as long as address sanitizer is happy. if (pool == nullptr || allocations == 0) { return; } pool->deallocate(ptr); if (--allocations == 0) { delete pool; pool = nullptr; } } #endif } golibsass-1.0.0/libsass_src/src/memory/allocator.hpp000066400000000000000000000062401405214413600225770ustar00rootroot00000000000000#ifndef SASS_ALLOCATOR_H #define SASS_ALLOCATOR_H #include "config.hpp" #include "../settings.hpp" #include "../MurmurHash2.hpp" #include #include #include #include #include namespace Sass { #ifndef SASS_CUSTOM_ALLOCATOR template using Allocator = std::allocator; #else void* allocateMem(size_t size); void deallocateMem(void* ptr, size_t size = 1); template class Allocator { public: // Allocator traits typedef T type; typedef type value_type; typedef value_type* pointer; typedef value_type const* const_pointer; typedef value_type& reference; typedef value_type const& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; template struct rebind { typedef Allocator other; }; // Constructor Allocator(void) {} // Copy Constructor template Allocator(Allocator const&) {} // allocate but don't initialize count of elements of type T pointer allocate(size_type count, const_pointer /* hint */ = 0) { return (pointer)(Sass::allocateMem(count * sizeof(T))); } // deallocate storage ptr of deleted elements void deallocate(pointer ptr, size_type count) { Sass::deallocateMem(ptr, count); } // return maximum number of elements that can be allocated size_type max_size() const throw() { return std::numeric_limits::max() / sizeof(T); } // Address of object type* address(type& obj) const { return &obj; } type const* address(type const& obj) const { return &obj; } // Construct object void construct(type* ptr, type const& ref) const { // In-place copy construct new(ptr) type(ref); } // Destroy object void destroy(type* ptr) const { // Call destructor ptr->~type(); } }; template bool operator==(Allocator const& left, Allocator const& right) { return true; } template bool operator!=(Allocator const& left, Allocator const& right) { return !(left == right); } #endif namespace sass { template using vector = std::vector>; using string = std::basic_string, Sass::Allocator>; using sstream = std::basic_stringstream, Sass::Allocator>; using ostream = std::basic_ostringstream, Sass::Allocator>; using istream = std::basic_istringstream, Sass::Allocator>; } } #ifdef SASS_CUSTOM_ALLOCATOR namespace std { // Only GCC seems to need this specialization!? template <> struct hash { public: inline size_t operator()( const Sass::sass::string& name) const { return MurmurHash2( (void*)name.c_str(), (int)name.size(), 0x73617373); } }; } #endif #endif golibsass-1.0.0/libsass_src/src/memory/config.hpp000066400000000000000000000013041405214413600220600ustar00rootroot00000000000000#ifndef SASS_MEMORY_CONFIG_H #define SASS_MEMORY_CONFIG_H // Define memory alignment requirements #define SASS_MEM_ALIGN sizeof(unsigned int) // Minimal alignment for memory fragments. Must be a multiple // of `SASS_MEM_ALIGN` and should not be too big (maybe 1 or 2) #define SassAllocatorHeadSize sizeof(unsigned int) // The number of bytes we use for our book-keeping before every // memory fragment. Needed to know to which bucket we belongs on // deallocations, or if it should go directly to the `free` call. #define SassAllocatorBookSize sizeof(unsigned int) // Bytes reserve for book-keeping on the arenas // Currently unused and for later optimization #define SassAllocatorArenaHeadSize 0 #endifgolibsass-1.0.0/libsass_src/src/memory/memory_pool.hpp000066400000000000000000000127721405214413600231670ustar00rootroot00000000000000#ifndef SASS_MEMORY_POOL_H #define SASS_MEMORY_POOL_H #include #include #include #include #include namespace Sass { // SIMPLE MEMORY-POOL ALLOCATOR WITH FREE-LIST ON TOP // This is a memory pool allocator with a free list on top. // We only allocate memory arenas from the system in specific // sizes (`SassAllocatorArenaSize`). Users claim memory slices // of certain sizes from the pool. If the allocation is too big // to fit into our buckets, we use regular malloc/free instead. // When the systems starts, we allocate the first arena and then // start to give out addresses to memory slices. During that // we steadily increase `offset` until the current arena is full. // Once that happens we allocate a new arena and continue. // https://en.wikipedia.org/wiki/Memory_pool // Fragments that get deallocated are not really freed, we put // them on our free-list. For every bucket we have a pointer to // the first item for reuse. That item itself holds a pointer to // the previously free item (regular free-list implementation). // https://en.wikipedia.org/wiki/Free_list // On allocation calls we first check if there is any suitable // item on the free-list. If there is we pop it from the stack // and return it to the caller. Otherwise we have to take out // a new slice from the current `arena` and increase `offset`. // Note that this is not thread safe. This is on purpose as we // want to use the memory pool in a thread local usage. In order // to get this thread safe you need to only allocate one pool // per thread. This can be achieved by using thread local PODs. // Simply create a pool on the first allocation and dispose // it once all allocations have been returned. E.g. by using: // static thread_local size_t allocations; // static thread_local MemoryPool* pool; class MemoryPool { // Current arena we fill up char* arena; // Position into the arena size_t offset = std::string::npos; // A list of full arenas std::vector arenas; // One pointer for every bucket (zero init) #ifdef _MSC_VER #pragma warning (suppress:4351) #endif void* freeList[SassAllocatorBuckets]{}; // Increase the address until it sits on a // memory aligned address (maybe use `aligned`). inline static size_t alignMemAddr(size_t addr) { return (addr + SASS_MEM_ALIGN - 1) & ~(SASS_MEM_ALIGN - 1); } public: // Default ctor MemoryPool() : // Wait for first allocation arena(nullptr), // Set to maximum value in order to // make an allocation on the first run offset(std::string::npos) { } // Destructor ~MemoryPool() { // Delete full arenas for (auto area : arenas) { free(area); } // Delete current arena free(arena); } // Allocate a slice of the memory pool void* allocate(size_t size) { // Increase size so its memory is aligned size = alignMemAddr( // Make sure we have enough space for us to // create the pointer to the free list later std::max(sizeof(void*), size) // and the size needed for our book-keeping + SassAllocatorBookSize); // Size must be multiple of alignment // So we can derive bucket index from it size_t bucket = size / SASS_MEM_ALIGN; // Everything bigger is allocated via malloc // Malloc is optimized for exactly this case if (bucket >= SassAllocatorBuckets) { char* buffer = (char*)malloc(size); if (buffer == nullptr) { throw std::bad_alloc(); } // Mark it for deallocation via free ((unsigned int*)buffer)[0] = UINT_MAX; // Return pointer after our book-keeping space return (void*)(buffer + SassAllocatorBookSize); } // Use custom allocator else { // Get item from free list void*& free = freeList[bucket]; // Do we have a free item? if (free != nullptr) { // Copy pointer to return void* ptr = free; // Update free list pointer free = ((void**)ptr)[0]; // Return popped item return ptr; } } // Make sure we have enough space in the arena if (!arena || offset > SassAllocatorArenaSize - size) { if (arena) arenas.emplace_back(arena); arena = (char*)malloc(SassAllocatorArenaSize); if (arena == nullptr) throw std::bad_alloc(); offset = SassAllocatorArenaHeadSize; } // Get pointer into the arena char* buffer = arena + offset; // Consume object size offset += size; // Set the bucket index for this slice ((unsigned int*)buffer)[0] = (unsigned int)bucket; // Return pointer after our book-keeping space return (void*)(buffer + SassAllocatorBookSize); } // EO allocate void deallocate(void* ptr) { // Rewind buffer from pointer char* buffer = (char*)ptr - SassAllocatorBookSize; // Get the bucket index stored in the header unsigned int bucket = ((unsigned int*)buffer)[0]; // Check allocation method if (bucket != UINT_MAX) { // Let memory point to previous item in free list ((void**)ptr)[0] = freeList[bucket]; // Free list now points to our memory freeList[bucket] = (void*)ptr; } else { // Release memory free(buffer); } } // EO deallocate }; } #endif golibsass-1.0.0/libsass_src/src/memory/shared_ptr.cpp000066400000000000000000000013771405214413600227530ustar00rootroot00000000000000#include "../sass.hpp" #include #include #include "shared_ptr.hpp" #include "../ast_fwd_decl.hpp" #ifdef DEBUG_SHARED_PTR #include "../debugger.hpp" #endif namespace Sass { #ifdef DEBUG_SHARED_PTR void SharedObj::dumpMemLeaks() { if (!all.empty()) { std::cerr << "###################################\n"; std::cerr << "# REPORTING MISSING DEALLOCATIONS #\n"; std::cerr << "###################################\n"; for (SharedObj* var : all) { if (AST_Node* ast = dynamic_cast(var)) { debug_ast(ast); } else { std::cerr << "LEAKED " << var << "\n"; } } } } sass::vector SharedObj::all; #endif bool SharedObj::taint = false; } golibsass-1.0.0/libsass_src/src/memory/shared_ptr.hpp000066400000000000000000000224371405214413600227600ustar00rootroot00000000000000#ifndef SASS_MEMORY_SHARED_PTR_H #define SASS_MEMORY_SHARED_PTR_H #include "sass/base.h" #include "../sass.hpp" #include "allocator.hpp" #include #include #include #include #include // https://lokiastari.com/blog/2014/12/30/c-plus-plus-by-example-smart-pointer/index.html // https://lokiastari.com/blog/2015/01/15/c-plus-plus-by-example-smart-pointer-part-ii/index.html // https://lokiastari.com/blog/2015/01/23/c-plus-plus-by-example-smart-pointer-part-iii/index.html namespace Sass { // Forward declaration class SharedPtr; /////////////////////////////////////////////////////////////////////////////// // Use macros for the allocation task, since overloading operator `new` // has been proven to be flaky under certain compilers (see comment below). /////////////////////////////////////////////////////////////////////////////// #ifdef DEBUG_SHARED_PTR #define SASS_MEMORY_NEW(Class, ...) \ ((Class*)(new Class(__VA_ARGS__))->trace(__FILE__, __LINE__)) \ #define SASS_MEMORY_COPY(obj) \ ((obj)->copy(__FILE__, __LINE__)) \ #define SASS_MEMORY_CLONE(obj) \ ((obj)->clone(__FILE__, __LINE__)) \ #else #define SASS_MEMORY_NEW(Class, ...) \ new Class(__VA_ARGS__) \ #define SASS_MEMORY_COPY(obj) \ ((obj)->copy()) \ #define SASS_MEMORY_CLONE(obj) \ ((obj)->clone()) \ #endif // SharedObj is the base class for all objects that can be stored as a shared object // It adds the reference counter and other values directly to the objects // This gives a slight overhead when directly used as a stack object, but has some // advantages for our code. It is safe to create two shared pointers from the same // objects, as the "control block" is directly attached to it. This would lead // to undefined behavior with std::shared_ptr. This also avoids the need to // allocate additional control blocks and/or the need to dereference two // pointers on each operation. This can be optimized in `std::shared_ptr` // too by using `std::make_shared` (where the control block and the actual // object are allocated in one continuous memory block via one single call). class SharedObj { public: SharedObj() : refcount(0), detached(false) { #ifdef DEBUG_SHARED_PTR if (taint) all.push_back(this); #endif } virtual ~SharedObj() { #ifdef DEBUG_SHARED_PTR for (size_t i = 0; i < all.size(); i++) { if (all[i] == this) { all.erase(all.begin() + i); break; } } #endif } #ifdef DEBUG_SHARED_PTR static void dumpMemLeaks(); SharedObj* trace(sass::string file, size_t line) { this->file = file; this->line = line; return this; } sass::string getDbgFile() { return file; } size_t getDbgLine() { return line; } void setDbg(bool dbg) { this->dbg = dbg; } size_t getRefCount() const { return refcount; } #endif static void setTaint(bool val) { taint = val; } #ifdef SASS_CUSTOM_ALLOCATOR inline void* operator new(size_t nbytes) { return allocateMem(nbytes); } inline void operator delete(void* ptr) { return deallocateMem(ptr); } #endif virtual sass::string to_string() const = 0; protected: friend class SharedPtr; friend class Memory_Manager; size_t refcount; bool detached; static bool taint; #ifdef DEBUG_SHARED_PTR sass::string file; size_t line; bool dbg = false; static sass::vector all; #endif }; // SharedPtr is a intermediate (template-less) base class for SharedImpl. // ToDo: there should be a way to include this in SharedImpl and to get // ToDo: rid of all the static_cast that are now needed in SharedImpl. class SharedPtr { public: SharedPtr() : node(nullptr) {} SharedPtr(SharedObj* ptr) : node(ptr) { incRefCount(); } SharedPtr(const SharedPtr& obj) : SharedPtr(obj.node) {} ~SharedPtr() { decRefCount(); } SharedPtr& operator=(SharedObj* other_node) { if (node != other_node) { decRefCount(); node = other_node; incRefCount(); } else if (node != nullptr) { node->detached = false; } return *this; } SharedPtr& operator=(const SharedPtr& obj) { return *this = obj.node; } // Prevents all SharedPtrs from freeing this node until it is assigned to another SharedPtr. SharedObj* detach() { if (node != nullptr) node->detached = true; #ifdef DEBUG_SHARED_PTR if (node->dbg) { std::cerr << "DETACHING NODE\n"; } #endif return node; } SharedObj* obj() const { return node; } SharedObj* operator->() const { return node; } bool isNull() const { return node == nullptr; } operator bool() const { return node != nullptr; } protected: SharedObj* node; void decRefCount() { if (node == nullptr) return; --node->refcount; #ifdef DEBUG_SHARED_PTR if (node->dbg) std::cerr << "- " << node << " X " << node->refcount << " (" << this << ") " << "\n"; #endif if (node->refcount == 0 && !node->detached) { #ifdef DEBUG_SHARED_PTR if (node->dbg) std::cerr << "DELETE NODE " << node << "\n"; #endif delete node; } else if (node->refcount == 0) { #ifdef DEBUG_SHARED_PTR if (node->dbg) std::cerr << "NODE EVAEDED DELETE " << node << "\n"; #endif } } void incRefCount() { if (node == nullptr) return; node->detached = false; ++node->refcount; #ifdef DEBUG_SHARED_PTR if (node->dbg) std::cerr << "+ " << node << " X " << node->refcount << " (" << this << ") " << "\n"; #endif } }; template class SharedImpl : private SharedPtr { public: SharedImpl() : SharedPtr(nullptr) {} template SharedImpl(U* node) : SharedPtr(static_cast(node)) {} template SharedImpl(const SharedImpl& impl) : SharedImpl(impl.ptr()) {} template SharedImpl& operator=(U *rhs) { return static_cast&>( SharedPtr::operator=(static_cast(rhs))); } template SharedImpl& operator=(const SharedImpl& rhs) { return static_cast&>( SharedPtr::operator=(static_cast&>(rhs))); } operator sass::string() const { if (node) return node->to_string(); return "null"; } using SharedPtr::isNull; using SharedPtr::operator bool; operator T*() const { return static_cast(this->obj()); } operator T&() const { return *static_cast(this->obj()); } T& operator* () const { return *static_cast(this->obj()); }; T* operator-> () const { return static_cast(this->obj()); }; T* ptr () const { return static_cast(this->obj()); }; T* detach() { return static_cast(SharedPtr::detach()); } }; // Comparison operators, based on: // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp template bool operator==(const SharedImpl& x, const SharedImpl& y) { return x.ptr() == y.ptr(); } template bool operator!=(const SharedImpl& x, const SharedImpl& y) { return x.ptr() != y.ptr(); } template bool operator<(const SharedImpl& x, const SharedImpl& y) { using CT = typename std::common_type::type; return std::less()(x.get(), y.get()); } template bool operator<=(const SharedImpl& x, const SharedImpl& y) { return !(y < x); } template bool operator>(const SharedImpl& x, const SharedImpl& y) { return y < x; } template bool operator>=(const SharedImpl& x, const SharedImpl& y) { return !(x < y); } template bool operator==(const SharedImpl& x, std::nullptr_t) noexcept { return x.isNull(); } template bool operator==(std::nullptr_t, const SharedImpl& x) noexcept { return x.isNull(); } template bool operator!=(const SharedImpl& x, std::nullptr_t) noexcept { return !x.isNull(); } template bool operator!=(std::nullptr_t, const SharedImpl& x) noexcept { return !x.isNull(); } template bool operator<(const SharedImpl& x, std::nullptr_t) { return std::less()(x.get(), nullptr); } template bool operator<(std::nullptr_t, const SharedImpl& y) { return std::less()(nullptr, y.get()); } template bool operator<=(const SharedImpl& x, std::nullptr_t) { return !(nullptr < x); } template bool operator<=(std::nullptr_t, const SharedImpl& y) { return !(y < nullptr); } template bool operator>(const SharedImpl& x, std::nullptr_t) { return nullptr < x; } template bool operator>(std::nullptr_t, const SharedImpl& y) { return y < nullptr; } template bool operator>=(const SharedImpl& x, std::nullptr_t) { return !(x < nullptr); } template bool operator>=(std::nullptr_t, const SharedImpl& y) { return !(nullptr < y); } } // namespace Sass #endif golibsass-1.0.0/libsass_src/src/operation.hpp000066400000000000000000000322401405214413600213060ustar00rootroot00000000000000#ifndef SASS_OPERATION_H #define SASS_OPERATION_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" // base classes to implement curiously recurring template pattern (CRTP) // https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern #include #include #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp" namespace Sass { #define ATTACH_ABSTRACT_CRTP_PERFORM_METHODS()\ virtual void perform(Operation* op) = 0; \ virtual Value* perform(Operation* op) = 0; \ virtual sass::string perform(Operation* op) = 0; \ virtual AST_Node* perform(Operation* op) = 0; \ virtual Selector* perform(Operation* op) = 0; \ virtual Statement* perform(Operation* op) = 0; \ virtual Expression* perform(Operation* op) = 0; \ virtual union Sass_Value* perform(Operation* op) = 0; \ virtual SupportsCondition* perform(Operation* op) = 0; \ // you must add operators to every class // ensures `this` of actual instance type // we therefore call the specific operator // they are virtual so most specific is used #define ATTACH_CRTP_PERFORM_METHODS()\ virtual void perform(Operation* op) override { return (*op)(this); } \ virtual Value* perform(Operation* op) override { return (*op)(this); } \ virtual sass::string perform(Operation* op) override { return (*op)(this); } \ virtual AST_Node* perform(Operation* op) override { return (*op)(this); } \ virtual Selector* perform(Operation* op) override { return (*op)(this); } \ virtual Statement* perform(Operation* op) override { return (*op)(this); } \ virtual Expression* perform(Operation* op) override { return (*op)(this); } \ virtual union Sass_Value* perform(Operation* op) override { return (*op)(this); } \ virtual SupportsCondition* perform(Operation* op) override { return (*op)(this); } \ template class Operation { public: virtual T operator()(AST_Node* x) = 0; // statements virtual T operator()(Block* x) = 0; virtual T operator()(StyleRule* x) = 0; virtual T operator()(Bubble* x) = 0; virtual T operator()(Trace* x) = 0; virtual T operator()(SupportsRule* x) = 0; virtual T operator()(MediaRule* x) = 0; virtual T operator()(CssMediaRule* x) = 0; virtual T operator()(CssMediaQuery* x) = 0; virtual T operator()(AtRootRule* x) = 0; virtual T operator()(AtRule* x) = 0; virtual T operator()(Keyframe_Rule* x) = 0; virtual T operator()(Declaration* x) = 0; virtual T operator()(Assignment* x) = 0; virtual T operator()(Import* x) = 0; virtual T operator()(Import_Stub* x) = 0; virtual T operator()(WarningRule* x) = 0; virtual T operator()(ErrorRule* x) = 0; virtual T operator()(DebugRule* x) = 0; virtual T operator()(Comment* x) = 0; virtual T operator()(If* x) = 0; virtual T operator()(ForRule* x) = 0; virtual T operator()(EachRule* x) = 0; virtual T operator()(WhileRule* x) = 0; virtual T operator()(Return* x) = 0; virtual T operator()(Content* x) = 0; virtual T operator()(ExtendRule* x) = 0; virtual T operator()(Definition* x) = 0; virtual T operator()(Mixin_Call* x) = 0; // expressions virtual T operator()(Null* x) = 0; virtual T operator()(List* x) = 0; virtual T operator()(Map* x) = 0; virtual T operator()(Function* x) = 0; virtual T operator()(Binary_Expression* x) = 0; virtual T operator()(Unary_Expression* x) = 0; virtual T operator()(Function_Call* x) = 0; virtual T operator()(Custom_Warning* x) = 0; virtual T operator()(Custom_Error* x) = 0; virtual T operator()(Variable* x) = 0; virtual T operator()(Number* x) = 0; virtual T operator()(Color* x) = 0; virtual T operator()(Color_RGBA* x) = 0; virtual T operator()(Color_HSLA* x) = 0; virtual T operator()(Boolean* x) = 0; virtual T operator()(String_Schema* x) = 0; virtual T operator()(String_Quoted* x) = 0; virtual T operator()(String_Constant* x) = 0; virtual T operator()(SupportsCondition* x) = 0; virtual T operator()(SupportsOperation* x) = 0; virtual T operator()(SupportsNegation* x) = 0; virtual T operator()(SupportsDeclaration* x) = 0; virtual T operator()(Supports_Interpolation* x) = 0; virtual T operator()(Media_Query* x) = 0; virtual T operator()(Media_Query_Expression* x) = 0; virtual T operator()(At_Root_Query* x) = 0; virtual T operator()(Parent_Reference* x) = 0; // parameters and arguments virtual T operator()(Parameter* x) = 0; virtual T operator()(Parameters* x) = 0; virtual T operator()(Argument* x) = 0; virtual T operator()(Arguments* x) = 0; // selectors virtual T operator()(Selector_Schema* x) = 0; virtual T operator()(PlaceholderSelector* x) = 0; virtual T operator()(TypeSelector* x) = 0; virtual T operator()(ClassSelector* x) = 0; virtual T operator()(IDSelector* x) = 0; virtual T operator()(AttributeSelector* x) = 0; virtual T operator()(PseudoSelector* x) = 0; virtual T operator()(SelectorComponent* x) = 0; virtual T operator()(SelectorCombinator* x) = 0; virtual T operator()(CompoundSelector* x) = 0; virtual T operator()(ComplexSelector* x) = 0; virtual T operator()(SelectorList* x) = 0; }; // example: Operation_CRTP // T is the base return type of all visitors // D is the class derived visitor class // normally you want to implement all operators template class Operation_CRTP : public Operation { public: T operator()(AST_Node* x) { return static_cast(this)->fallback(x); } // statements T operator()(Block* x) { return static_cast(this)->fallback(x); } T operator()(StyleRule* x) { return static_cast(this)->fallback(x); } T operator()(Bubble* x) { return static_cast(this)->fallback(x); } T operator()(Trace* x) { return static_cast(this)->fallback(x); } T operator()(SupportsRule* x) { return static_cast(this)->fallback(x); } T operator()(MediaRule* x) { return static_cast(this)->fallback(x); } T operator()(CssMediaRule* x) { return static_cast(this)->fallback(x); } T operator()(CssMediaQuery* x) { return static_cast(this)->fallback(x); } T operator()(AtRootRule* x) { return static_cast(this)->fallback(x); } T operator()(AtRule* x) { return static_cast(this)->fallback(x); } T operator()(Keyframe_Rule* x) { return static_cast(this)->fallback(x); } T operator()(Declaration* x) { return static_cast(this)->fallback(x); } T operator()(Assignment* x) { return static_cast(this)->fallback(x); } T operator()(Import* x) { return static_cast(this)->fallback(x); } T operator()(Import_Stub* x) { return static_cast(this)->fallback(x); } T operator()(WarningRule* x) { return static_cast(this)->fallback(x); } T operator()(ErrorRule* x) { return static_cast(this)->fallback(x); } T operator()(DebugRule* x) { return static_cast(this)->fallback(x); } T operator()(Comment* x) { return static_cast(this)->fallback(x); } T operator()(If* x) { return static_cast(this)->fallback(x); } T operator()(ForRule* x) { return static_cast(this)->fallback(x); } T operator()(EachRule* x) { return static_cast(this)->fallback(x); } T operator()(WhileRule* x) { return static_cast(this)->fallback(x); } T operator()(Return* x) { return static_cast(this)->fallback(x); } T operator()(Content* x) { return static_cast(this)->fallback(x); } T operator()(ExtendRule* x) { return static_cast(this)->fallback(x); } T operator()(Definition* x) { return static_cast(this)->fallback(x); } T operator()(Mixin_Call* x) { return static_cast(this)->fallback(x); } // expressions T operator()(Null* x) { return static_cast(this)->fallback(x); } T operator()(List* x) { return static_cast(this)->fallback(x); } T operator()(Map* x) { return static_cast(this)->fallback(x); } T operator()(Function* x) { return static_cast(this)->fallback(x); } T operator()(Binary_Expression* x) { return static_cast(this)->fallback(x); } T operator()(Unary_Expression* x) { return static_cast(this)->fallback(x); } T operator()(Function_Call* x) { return static_cast(this)->fallback(x); } T operator()(Custom_Warning* x) { return static_cast(this)->fallback(x); } T operator()(Custom_Error* x) { return static_cast(this)->fallback(x); } T operator()(Variable* x) { return static_cast(this)->fallback(x); } T operator()(Number* x) { return static_cast(this)->fallback(x); } T operator()(Color* x) { return static_cast(this)->fallback(x); } T operator()(Color_RGBA* x) { return static_cast(this)->fallback(x); } T operator()(Color_HSLA* x) { return static_cast(this)->fallback(x); } T operator()(Boolean* x) { return static_cast(this)->fallback(x); } T operator()(String_Schema* x) { return static_cast(this)->fallback(x); } T operator()(String_Constant* x) { return static_cast(this)->fallback(x); } T operator()(String_Quoted* x) { return static_cast(this)->fallback(x); } T operator()(SupportsCondition* x) { return static_cast(this)->fallback(x); } T operator()(SupportsOperation* x) { return static_cast(this)->fallback(x); } T operator()(SupportsNegation* x) { return static_cast(this)->fallback(x); } T operator()(SupportsDeclaration* x) { return static_cast(this)->fallback(x); } T operator()(Supports_Interpolation* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query_Expression* x) { return static_cast(this)->fallback(x); } T operator()(At_Root_Query* x) { return static_cast(this)->fallback(x); } T operator()(Parent_Reference* x) { return static_cast(this)->fallback(x); } // parameters and arguments T operator()(Parameter* x) { return static_cast(this)->fallback(x); } T operator()(Parameters* x) { return static_cast(this)->fallback(x); } T operator()(Argument* x) { return static_cast(this)->fallback(x); } T operator()(Arguments* x) { return static_cast(this)->fallback(x); } // selectors T operator()(Selector_Schema* x) { return static_cast(this)->fallback(x); } T operator()(PlaceholderSelector* x) { return static_cast(this)->fallback(x); } T operator()(TypeSelector* x) { return static_cast(this)->fallback(x); } T operator()(ClassSelector* x) { return static_cast(this)->fallback(x); } T operator()(IDSelector* x) { return static_cast(this)->fallback(x); } T operator()(AttributeSelector* x) { return static_cast(this)->fallback(x); } T operator()(PseudoSelector* x) { return static_cast(this)->fallback(x); } T operator()(SelectorComponent* x) { return static_cast(this)->fallback(x); } T operator()(SelectorCombinator* x) { return static_cast(this)->fallback(x); } T operator()(CompoundSelector* x) { return static_cast(this)->fallback(x); } T operator()(ComplexSelector* x) { return static_cast(this)->fallback(x); } T operator()(SelectorList* x) { return static_cast(this)->fallback(x); } // fallback with specific type U // will be called if not overloaded template inline T fallback(U x) { throw std::runtime_error( std::string(typeid(*this).name()) + ": CRTP not implemented for " + typeid(x).name()); } }; } #endif golibsass-1.0.0/libsass_src/src/operators.cpp000066400000000000000000000240561405214413600213250ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include "operators.hpp" namespace Sass { namespace Operators { inline double add(double x, double y) { return x + y; } inline double sub(double x, double y) { return x - y; } inline double mul(double x, double y) { return x * y; } inline double div(double x, double y) { return x / y; } // x/0 checked by caller inline double mod(double x, double y) { // x/0 checked by caller if ((x > 0 && y < 0) || (x < 0 && y > 0)) { double ret = std::fmod(x, y); return ret ? ret + y : ret; } else { return std::fmod(x, y); } } typedef double (*bop)(double, double); bop ops[Sass_OP::NUM_OPS] = { 0, 0, // and, or 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte add, sub, mul, div, mod }; /* static function, has no pstate or traces */ bool eq(ExpressionObj lhs, ExpressionObj rhs) { // operation is undefined if one is not a number if (!lhs || !rhs) throw Exception::UndefinedOperation(lhs, rhs, Sass_OP::EQ); // use compare operator from ast node return *lhs == *rhs; } /* static function, throws OperationError, has no pstate or traces */ bool cmp(ExpressionObj lhs, ExpressionObj rhs, const Sass_OP op) { // can only compare numbers!? Number_Obj l = Cast(lhs); Number_Obj r = Cast(rhs); // operation is undefined if one is not a number if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op); // use compare operator from ast node return *l < *r; } /* static functions, throws OperationError, has no pstate or traces */ bool lt(ExpressionObj lhs, ExpressionObj rhs) { return cmp(lhs, rhs, Sass_OP::LT); } bool neq(ExpressionObj lhs, ExpressionObj rhs) { return eq(lhs, rhs) == false; } bool gt(ExpressionObj lhs, ExpressionObj rhs) { return !cmp(lhs, rhs, Sass_OP::GT) && neq(lhs, rhs); } bool lte(ExpressionObj lhs, ExpressionObj rhs) { return cmp(lhs, rhs, Sass_OP::LTE) || eq(lhs, rhs); } bool gte(ExpressionObj lhs, ExpressionObj rhs) { return !cmp(lhs, rhs, Sass_OP::GTE) || eq(lhs, rhs); } /* colour math deprecation warning */ void op_color_deprecation(enum Sass_OP op, sass::string lsh, sass::string rhs, const SourceSpan& pstate) { deprecated( "The operation `" + lsh + " " + sass_op_to_name(op) + " " + rhs + "` is deprecated and will be an error in future versions.", "Consider using Sass's color functions instead.\n" "https://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions", /*with_column=*/false, pstate); } /* static function, throws OperationError, has no traces but optional pstate for returned value */ Value* op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed) { enum Sass_OP op = operand.operand; String_Quoted* lqstr = Cast(&lhs); String_Quoted* rqstr = Cast(&rhs); sass::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt)); sass::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt)); if (Cast(&lhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op); if (Cast(&rhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op); sass::string sep; switch (op) { case Sass_OP::ADD: sep = ""; break; case Sass_OP::SUB: sep = "-"; break; case Sass_OP::DIV: sep = "/"; break; case Sass_OP::EQ: sep = "=="; break; case Sass_OP::NEQ: sep = "!="; break; case Sass_OP::LT: sep = "<"; break; case Sass_OP::GT: sep = ">"; break; case Sass_OP::LTE: sep = "<="; break; case Sass_OP::GTE: sep = ">="; break; default: throw Exception::UndefinedOperation(&lhs, &rhs, op); break; } if (op == Sass_OP::ADD) { // create string that might be quoted on output (but do not unquote what we pass) return SASS_MEMORY_NEW(String_Quoted, pstate, lstr + rstr, 0, false, true); } // add whitespace around operator // but only if result is not delayed if (sep != "" && delayed == false) { if (operand.ws_before) sep = " " + sep; if (operand.ws_after) sep = sep + " "; } if (op == Sass_OP::SUB || op == Sass_OP::DIV) { if (lqstr && lqstr->quote_mark()) lstr = quote(lstr); if (rqstr && rqstr->quote_mark()) rstr = quote(rstr); } return SASS_MEMORY_NEW(String_Constant, pstate, lstr + sep + rstr); } /* ToDo: allow to operate also with hsla colors */ /* static function, throws OperationError, has no traces but optional pstate for returned value */ Value* op_colors(enum Sass_OP op, const Color_RGBA& lhs, const Color_RGBA& rhs, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed) { if (lhs.a() != rhs.a()) { throw Exception::AlphaChannelsNotEqual(&lhs, &rhs, op); } if ((op == Sass_OP::DIV || op == Sass_OP::MOD) && (!rhs.r() || !rhs.g() || !rhs.b())) { throw Exception::ZeroDivisionError(lhs, rhs); } op_color_deprecation(op, lhs.to_string(), rhs.to_string(), pstate); return SASS_MEMORY_NEW(Color_RGBA, pstate, ops[op](lhs.r(), rhs.r()), ops[op](lhs.g(), rhs.g()), ops[op](lhs.b(), rhs.b()), lhs.a()); } /* static function, throws OperationError, has no traces but optional pstate for returned value */ Value* op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed) { double lval = lhs.value(); double rval = rhs.value(); if (op == Sass_OP::MOD && rval == 0) { return SASS_MEMORY_NEW(String_Quoted, pstate, "NaN"); } if (op == Sass_OP::DIV && rval == 0) { sass::string result(lval ? "Infinity" : "NaN"); return SASS_MEMORY_NEW(String_Quoted, pstate, result); } size_t l_n_units = lhs.numerators.size(); size_t l_d_units = lhs.numerators.size(); size_t r_n_units = rhs.denominators.size(); size_t r_d_units = rhs.denominators.size(); // optimize out the most common and simplest case if (l_n_units == r_n_units && l_d_units == r_d_units) { if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) { if (lhs.numerators == rhs.numerators) { if (lhs.denominators == rhs.denominators) { Number* v = SASS_MEMORY_COPY(&lhs); v->value(ops[op](lval, rval)); return v; } } } } Number_Obj v = SASS_MEMORY_COPY(&lhs); if (lhs.is_unitless() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) { v->numerators = rhs.numerators; v->denominators = rhs.denominators; } if (op == Sass_OP::MUL) { v->value(ops[op](lval, rval)); v->numerators.insert(v->numerators.end(), rhs.numerators.begin(), rhs.numerators.end() ); v->denominators.insert(v->denominators.end(), rhs.denominators.begin(), rhs.denominators.end() ); v->reduce(); } else if (op == Sass_OP::DIV) { v->value(ops[op](lval, rval)); v->numerators.insert(v->numerators.end(), rhs.denominators.begin(), rhs.denominators.end() ); v->denominators.insert(v->denominators.end(), rhs.numerators.begin(), rhs.numerators.end() ); v->reduce(); } else { Number ln(lhs), rn(rhs); ln.reduce(); rn.reduce(); double f(rn.convert_factor(ln)); v->value(ops[op](lval, rn.value() * f)); } v->pstate(pstate); return v.detach(); } /* static function, throws OperationError, has no traces but optional pstate for returned value */ Value* op_number_color(enum Sass_OP op, const Number& lhs, const Color_RGBA& rhs, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed) { double lval = lhs.value(); switch (op) { case Sass_OP::ADD: case Sass_OP::MUL: { op_color_deprecation(op, lhs.to_string(), rhs.to_string(opt), pstate); return SASS_MEMORY_NEW(Color_RGBA, pstate, ops[op](lval, rhs.r()), ops[op](lval, rhs.g()), ops[op](lval, rhs.b()), rhs.a()); } case Sass_OP::SUB: case Sass_OP::DIV: { sass::string color(rhs.to_string(opt)); op_color_deprecation(op, lhs.to_string(), color, pstate); return SASS_MEMORY_NEW(String_Quoted, pstate, lhs.to_string(opt) + sass_op_separator(op) + color); } default: break; } throw Exception::UndefinedOperation(&lhs, &rhs, op); } /* static function, throws OperationError, has no traces but optional pstate for returned value */ Value* op_color_number(enum Sass_OP op, const Color_RGBA& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed) { double rval = rhs.value(); if ((op == Sass_OP::DIV || op == Sass_OP::MOD) && rval == 0) { // comparison of Fixnum with Float failed? throw Exception::ZeroDivisionError(lhs, rhs); } op_color_deprecation(op, lhs.to_string(), rhs.to_string(), pstate); return SASS_MEMORY_NEW(Color_RGBA, pstate, ops[op](lhs.r(), rval), ops[op](lhs.g(), rval), ops[op](lhs.b(), rval), lhs.a()); } } } golibsass-1.0.0/libsass_src/src/operators.hpp000066400000000000000000000024541405214413600213300ustar00rootroot00000000000000#ifndef SASS_OPERATORS_H #define SASS_OPERATORS_H #include "values.hpp" #include "sass/values.h" namespace Sass { namespace Operators { // equality operator using AST Node operator== bool eq(ExpressionObj, ExpressionObj); bool neq(ExpressionObj, ExpressionObj); // specific operators based on cmp and eq bool lt(ExpressionObj, ExpressionObj); bool gt(ExpressionObj, ExpressionObj); bool lte(ExpressionObj, ExpressionObj); bool gte(ExpressionObj, ExpressionObj); // arithmetic for all the combinations that matter Value* op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed = false); Value* op_colors(enum Sass_OP, const Color_RGBA&, const Color_RGBA&, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed = false); Value* op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed = false); Value* op_number_color(enum Sass_OP, const Number&, const Color_RGBA&, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed = false); Value* op_color_number(enum Sass_OP, const Color_RGBA&, const Number&, struct Sass_Inspect_Options opt, const SourceSpan& pstate, bool delayed = false); }; } #endif golibsass-1.0.0/libsass_src/src/ordered_map.hpp000066400000000000000000000064201405214413600215700ustar00rootroot00000000000000#ifndef SASS_ORDERED_MAP_H #define SASS_ORDERED_MAP_H namespace Sass { // ########################################################################## // Very simple and limited container for insert ordered hash map. // Piggy-back implementation on std::unordered_map and sass::vector // ########################################################################## template< class Key, class T, class Hash = std::hash, class KeyEqual = std::equal_to, class Allocator = std::allocator> > class ordered_map { private: using map_type = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>; using map_iterator = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>::iterator; using map_const_iterator = typename std::unordered_map< Key, T, Hash, KeyEqual, Allocator>::const_iterator; // The main unordered map map_type _map; // Keep insertion order sass::vector _keys; sass::vector _values; const KeyEqual _keyEqual; public: ordered_map() : _keyEqual(KeyEqual()) { } ordered_map& operator= (const ordered_map& other) { _map = other._map; _keys = other._keys; _values = other._values; return *this; } std::pair front() { return std::make_pair( _keys.front(), _values.front() ); } bool empty() const { return _keys.empty(); } void insert(const Key& key, const T& val) { if (!hasKey(key)) { _values.push_back(val); _keys.push_back(key); } _map[key] = val; } bool hasKey(const Key& key) const { return _map.find(key) != _map.end(); } bool erase(const Key& key) { _map.erase(key); // find the position in the array for (size_t i = 0; i < _keys.size(); i += 1) { if (_keyEqual(key, _keys[i])) { _keys.erase(_keys.begin() + i); _values.erase(_values.begin() + i); return true; } } return false; } const sass::vector& keys() const { return _keys; } const sass::vector& values() const { return _values; } const T& get(const Key& key) { if (hasKey(key)) { return _map[key]; } throw std::runtime_error("Key does not exist"); } using iterator = typename sass::vector::iterator; using const_iterator = typename sass::vector::const_iterator; using reverse_iterator = typename sass::vector::reverse_iterator; using const_reverse_iterator = typename sass::vector::const_reverse_iterator; typename sass::vector::iterator end() { return _keys.end(); } typename sass::vector::iterator begin() { return _keys.begin(); } typename sass::vector::reverse_iterator rend() { return _keys.rend(); } typename sass::vector::reverse_iterator rbegin() { return _keys.rbegin(); } typename sass::vector::const_iterator end() const { return _keys.end(); } typename sass::vector::const_iterator begin() const { return _keys.begin(); } typename sass::vector::const_reverse_iterator rend() const { return _keys.rend(); } typename sass::vector::const_reverse_iterator rbegin() const { return _keys.rbegin(); } }; } #endif golibsass-1.0.0/libsass_src/src/output.cpp000066400000000000000000000203501405214413600206400ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "output.hpp" #include "util.hpp" namespace Sass { Output::Output(Sass_Output_Options& opt) : Inspect(Emitter(opt)), charset(""), top_nodes(0) {} Output::~Output() { } void Output::fallback_impl(AST_Node* n) { return n->perform(this); } void Output::operator()(Number* n) { // check for a valid unit here // includes result for reporting if (!n->is_valid_css_unit()) { // should be handle in check_expression throw Exception::InvalidValue({}, *n); } // use values to_string facility sass::string res = n->to_string(opt); // output the final token append_token(res, n); } void Output::operator()(Import* imp) { top_nodes.push_back(imp); } void Output::operator()(Map* m) { // should be handle in check_expression throw Exception::InvalidValue({}, *m); } OutputBuffer Output::get_buffer(void) { Emitter emitter(opt); Inspect inspect(emitter); size_t size_nodes = top_nodes.size(); for (size_t i = 0; i < size_nodes; i++) { top_nodes[i]->perform(&inspect); inspect.append_mandatory_linefeed(); } // flush scheduled outputs // maybe omit semicolon if possible inspect.finalize(wbuf.buffer.size() == 0); // prepend buffer on top prepend_output(inspect.output()); // make sure we end with a linefeed if (!ends_with(wbuf.buffer, opt.linefeed)) { // if the output is not completely empty if (!wbuf.buffer.empty()) append_string(opt.linefeed); } // search for unicode char for(const char& chr : wbuf.buffer) { // skip all ascii chars // static cast to unsigned to handle `char` being signed / unsigned if (static_cast(chr) < 128) continue; // declare the charset if (output_style() != COMPRESSED) charset = "@charset \"UTF-8\";" + sass::string(opt.linefeed); else charset = "\xEF\xBB\xBF"; // abort search break; } // add charset as first line, before comments and imports if (!charset.empty()) prepend_string(charset); return wbuf; } void Output::operator()(Comment* c) { // if (indentation && txt == "/**/") return; bool important = c->is_important(); if (output_style() != COMPRESSED || important) { if (buffer().size() == 0) { top_nodes.push_back(c); } else { in_comment = true; append_indentation(); c->text()->perform(this); in_comment = false; if (indentation == 0) { append_mandatory_linefeed(); } else { append_optional_linefeed(); } } } } void Output::operator()(StyleRule* r) { Block_Obj b = r->block(); SelectorListObj s = r->selector(); if (!s || s->empty()) return; // Filter out rulesets that aren't printable (process its children though) if (!Util::isPrintable(r, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { const Statement_Obj& stm = b->get(i); if (Cast(stm)) { if (!Cast(stm)) { stm->perform(this); } } } return; } if (output_style() == NESTED) { indentation += r->tabs(); } if (opt.source_comments) { sass::ostream ss; append_indentation(); sass::string path(File::abs2rel(r->pstate().getPath())); ss << "/* line " << r->pstate().getLine() << ", " << path << " */"; append_string(ss.str()); append_optional_linefeed(); } scheduled_crutch = s; if (s) s->perform(this); append_scope_opener(b); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->get(i); bool bPrintExpression = true; // Check print conditions if (Declaration* dec = Cast(stm)) { if (const String_Constant* valConst = Cast(dec->value())) { const sass::string& val = valConst->value(); if (const String_Quoted* qstr = Cast(valConst)) { if (!qstr->quote_mark() && val.empty()) { bPrintExpression = false; } } } else if (List* list = Cast(dec->value())) { bool all_invisible = true; for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) { Expression* item = list->get(list_i); if (!item->is_invisible()) all_invisible = false; } if (all_invisible && !list->is_bracketed()) bPrintExpression = false; } } // Print if OK if (bPrintExpression) { stm->perform(this); } } if (output_style() == NESTED) indentation -= r->tabs(); append_scope_closer(b); } void Output::operator()(Keyframe_Rule* r) { Block_Obj b = r->block(); Selector_Obj v = r->name(); if (!v.isNull()) { v->perform(this); } if (!b) { append_colon_separator(); return; } append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->get(i); stm->perform(this); if (i < L - 1) append_special_linefeed(); } append_scope_closer(); } void Output::operator()(SupportsRule* f) { if (f->is_invisible()) return; SupportsConditionObj c = f->condition(); Block_Obj b = f->block(); // Filter out feature blocks that aren't printable (process its children though) if (!Util::isPrintable(f, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->get(i); if (Cast(stm)) { stm->perform(this); } } return; } if (output_style() == NESTED) indentation += f->tabs(); append_indentation(); append_token("@supports", f); append_mandatory_space(); c->perform(this); append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->get(i); stm->perform(this); if (i < L - 1) append_special_linefeed(); } if (output_style() == NESTED) indentation -= f->tabs(); append_scope_closer(); } void Output::operator()(CssMediaRule* rule) { // Avoid null pointer exception if (rule == nullptr) return; // Skip empty/invisible rule if (rule->isInvisible()) return; // Avoid null pointer exception if (rule->block() == nullptr) return; // Skip empty/invisible rule if (rule->block()->isInvisible()) return; // Skip if block is empty/invisible if (Util::isPrintable(rule, output_style())) { // Let inspect do its magic Inspect::operator()(rule); } } void Output::operator()(AtRule* a) { sass::string kwd = a->keyword(); Selector_Obj s = a->selector(); ExpressionObj v = a->value(); Block_Obj b = a->block(); append_indentation(); append_token(kwd, a); if (s) { append_mandatory_space(); in_wrapped = true; s->perform(this); in_wrapped = false; } if (v) { append_mandatory_space(); // ruby sass bug? should use options? append_token(v->to_string(/* opt */), v); } if (!b) { append_delimiter(); return; } if (b->is_invisible() || b->length() == 0) { append_optional_space(); return append_string("{}"); } append_scope_opener(); bool format = kwd != "@font-face";; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->get(i); if (stm) stm->perform(this); if (i < L - 1 && format) append_special_linefeed(); } append_scope_closer(); } void Output::operator()(String_Quoted* s) { if (s->quote_mark()) { append_token(quote(s->value(), s->quote_mark()), s); } else if (!in_comment) { append_token(string_to_output(s->value()), s); } else { append_token(s->value(), s); } } void Output::operator()(String_Constant* s) { sass::string value(s->value()); if (!in_comment && !in_custom_property) { append_token(string_to_output(value), s); } else { append_token(value, s); } } } golibsass-1.0.0/libsass_src/src/output.hpp000066400000000000000000000017131405214413600206470ustar00rootroot00000000000000#ifndef SASS_OUTPUT_H #define SASS_OUTPUT_H #include #include #include "util.hpp" #include "inspect.hpp" #include "operation.hpp" namespace Sass { class Context; class Output : public Inspect { protected: using Inspect::operator(); public: Output(Sass_Output_Options& opt); virtual ~Output(); protected: sass::string charset; sass::vector top_nodes; public: OutputBuffer get_buffer(void); virtual void operator()(Map*); virtual void operator()(StyleRule*); virtual void operator()(SupportsRule*); virtual void operator()(CssMediaRule*); virtual void operator()(AtRule*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Import*); virtual void operator()(Comment*); virtual void operator()(Number*); virtual void operator()(String_Quoted*); virtual void operator()(String_Constant*); void fallback_impl(AST_Node* n); }; } #endif golibsass-1.0.0/libsass_src/src/parser.cpp000066400000000000000000003102241405214413600205760ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "parser.hpp" #include "color_maps.hpp" #include "util_string.hpp" // Notes about delayed: some ast nodes can have delayed evaluation so // they can preserve their original semantics if needed. This is most // prominently exhibited by the division operation, since it is not // only a valid operation, but also a valid css statement (i.e. for // fonts, as in `16px/24px`). When parsing lists and expression we // unwrap single items from lists and other operations. A nested list // must not be delayed, only the items of the first level sometimes // are delayed (as with argument lists). To achieve this we need to // pass status to the list parser, so this can be set correctly. // Another case with delayed values are colors. In compressed mode // only processed values get compressed (other are left as written). namespace Sass { using namespace Constants; using namespace Prelexer; Parser::Parser(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent) : SourceSpan(source), ctx(ctx), source(source), begin(source->begin()), position(source->begin()), end(source->end()), before_token(0, 0), after_token(0, 0), pstate(source->getSourceSpan()), traces(traces), indentation(0), nestings(0), allow_parent(allow_parent) { Block_Obj root = SASS_MEMORY_NEW(Block, pstate); stack.push_back(Scope::Root); block_stack.push_back(root); root->is_root(true); } void Parser::advanceToNextToken() { lex < css_comments >(false); // advance to position pstate.position += pstate.offset; pstate.offset.column = 0; pstate.offset.line = 0; } SelectorListObj Parser::parse_selector(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent) { Parser p(source, ctx, traces, allow_parent); // ToDo: remap the source-map entries somehow return p.parseSelectorList(false); } bool Parser::peek_newline(const char* start) { return peek_linefeed(start ? start : position) && ! peek_css>(start); } /* main entry point to parse root block */ Block_Obj Parser::parse() { // consume unicode BOM read_bom(); // scan the input to find invalid utf8 sequences const char* it = utf8::find_invalid(position, end); // report invalid utf8 if (it != end) { pstate.position += Offset::init(position, it); traces.push_back(Backtrace(pstate)); throw Exception::InvalidSass(pstate, traces, "Invalid UTF-8 sequence"); } // create a block AST node to hold children Block_Obj root = SASS_MEMORY_NEW(Block, pstate, 0, true); // check seems a bit esoteric but works if (ctx.resources.size() == 1) { // apply headers only on very first include ctx.apply_custom_headers(root, getPath(), pstate); } // parse children nodes block_stack.push_back(root); parse_block_nodes(true); block_stack.pop_back(); // update final position root->update_pstate(pstate); if (position != end) { css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); } return root; } // convenience function for block parsing // will create a new block ad-hoc for you // this is the base block parsing function Block_Obj Parser::parse_css_block(bool is_root) { // parse comments before block // lex < optional_css_comments >(); // lex mandatory opener or error out if (!lex_css < exactly<'{'> >()) { css_error("Invalid CSS", " after ", ": expected \"{\", was "); } // create new block and push to the selector stack Block_Obj block = SASS_MEMORY_NEW(Block, pstate, 0, is_root); block_stack.push_back(block); if (!parse_block_nodes(is_root)) css_error("Invalid CSS", " after ", ": expected \"}\", was "); if (!lex_css < exactly<'}'> >()) { css_error("Invalid CSS", " after ", ": expected \"}\", was "); } // update for end position // this seems to be done somewhere else // but that fixed selector schema issue // block->update_pstate(pstate); // parse comments after block // lex < optional_css_comments >(); block_stack.pop_back(); return block; } // convenience function for block parsing // will create a new block ad-hoc for you // also updates the `in_at_root` flag Block_Obj Parser::parse_block(bool is_root) { return parse_css_block(is_root); } // the main block parsing function // parses stuff between `{` and `}` bool Parser::parse_block_nodes(bool is_root) { // loop until end of string while (position < end) { // we should be able to refactor this parse_block_comments(); lex < css_whitespace >(); if (lex < exactly<';'> >()) continue; if (peek < end_of_file >()) return true; if (peek < exactly<'}'> >()) return true; if (parse_block_node(is_root)) continue; parse_block_comments(); if (lex_css < exactly<';'> >()) continue; if (peek_css < end_of_file >()) return true; if (peek_css < exactly<'}'> >()) return true; // illegal sass return false; } // return success return true; } // parser for a single node in a block // semicolons must be lexed beforehand bool Parser::parse_block_node(bool is_root) { Block_Obj block = block_stack.back(); parse_block_comments(); // throw away white-space // includes line comments lex < css_whitespace >(); Lookahead lookahead_result; // also parse block comments // first parse everything that is allowed in functions if (lex < variable >(true)) { block->append(parse_assignment()); } else if (lex < kwd_err >(true)) { block->append(parse_error()); } else if (lex < kwd_dbg >(true)) { block->append(parse_debug()); } else if (lex < kwd_warn >(true)) { block->append(parse_warning()); } else if (lex < kwd_if_directive >(true)) { block->append(parse_if_directive()); } else if (lex < kwd_for_directive >(true)) { block->append(parse_for_directive()); } else if (lex < kwd_each_directive >(true)) { block->append(parse_each_directive()); } else if (lex < kwd_while_directive >(true)) { block->append(parse_while_directive()); } else if (lex < kwd_return_directive >(true)) { block->append(parse_return_directive()); } // parse imports to process later else if (lex < kwd_import >(true)) { Scope parent = stack.empty() ? Scope::Rules : stack.back(); if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) { if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20 error("Import directives may not be used within control directives or mixins."); } } // this puts the parsed doc into sheets // import stub will fetch this in expand Import_Obj imp = parse_import(); // if it is a url, we only add the statement if (!imp->urls().empty()) block->append(imp); // process all resources now (add Import_Stub nodes) for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { block->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i])); } } else if (lex < kwd_extend >(true)) { Lookahead lookahead = lookahead_for_include(position); if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was "); SelectorListObj target; if (!lookahead.has_interpolants) { LOCAL_FLAG(allow_parent, false); auto selector = parseSelectorList(true); auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector); extender->isOptional(selector && selector->is_optional()); block->append(extender); } else { LOCAL_FLAG(allow_parent, false); auto selector = parse_selector_schema(lookahead.found, true); auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector); // A schema is not optional yet, check once it is evaluated // extender->isOptional(selector && selector->is_optional()); block->append(extender); } } // selector may contain interpolations which need delayed evaluation else if ( !(lookahead_result = lookahead_for_selector(position)).error && !lookahead_result.is_custom_property ) { block->append(parse_ruleset(lookahead_result)); } // parse multiple specific keyword directives else if (lex < kwd_media >(true)) { block->append(parseMediaRule()); } else if (lex < kwd_at_root >(true)) { block->append(parse_at_root_block()); } else if (lex < kwd_include_directive >(true)) { block->append(parse_include_directive()); } else if (lex < kwd_content_directive >(true)) { block->append(parse_content_directive()); } else if (lex < kwd_supports_directive >(true)) { block->append(parse_supports_directive()); } else if (lex < kwd_mixin >(true)) { block->append(parse_definition(Definition::MIXIN)); } else if (lex < kwd_function >(true)) { block->append(parse_definition(Definition::FUNCTION)); } // ignore the @charset directive for now else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } else if (lex < exactly < else_kwd >>(true)) { error("Invalid CSS: @else must come after @if"); } // generic at keyword (keep last) else if (lex< at_keyword >(true)) { block->append(parse_directive()); } else if (is_root && stack.back() != Scope::AtRoot /* && block->is_root() */) { lex< css_whitespace >(); if (position >= end) return true; css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was "); } // parse a declaration else { // ToDo: how does it handle parse errors? // maybe we are expected to parse something? Declaration_Obj decl = parse_declaration(); decl->tabs(indentation); block->append(decl); // maybe we have a "sub-block" if (peek< exactly<'{'> >()) { if (decl->is_indented()) ++ indentation; // parse a propset that rides on the declaration's property stack.push_back(Scope::Properties); decl->block(parse_block()); stack.pop_back(); if (decl->is_indented()) -- indentation; } } // something matched return true; } // EO parse_block_nodes // parse imports inside the Import_Obj Parser::parse_import() { Import_Obj imp = SASS_MEMORY_NEW(Import, pstate); sass::vector> to_import; bool first = true; do { while (lex< block_comment >()); if (lex< quoted_string >()) { to_import.push_back(std::pair(sass::string(lexed), {})); } else if (lex< uri_prefix >()) { Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); Function_Call_Obj result = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), args); if (lex< quoted_string >()) { ExpressionObj quoted_url = parse_string(); args->append(SASS_MEMORY_NEW(Argument, quoted_url->pstate(), quoted_url)); } else if (String_Obj string_url = parse_url_function_argument()) { args->append(SASS_MEMORY_NEW(Argument, string_url->pstate(), string_url)); } else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) { ExpressionObj braced_url = parse_list(); // parse_interpolated_chunk(lexed); args->append(SASS_MEMORY_NEW(Argument, braced_url->pstate(), braced_url)); } else { error("malformed URL"); } if (!lex< exactly<')'> >()) error("URI is missing ')'"); to_import.push_back(std::pair("", result)); } else { if (first) error("@import directive requires a url or quoted path"); else error("expecting another url or quoted path in @import list"); } first = false; } while (lex_css< exactly<','> >()); if (!peek_css< alternatives< exactly<';'>, exactly<'}'>, end_of_file > >()) { List_Obj import_queries = parse_media_queries(); imp->import_queries(import_queries); } for(auto location : to_import) { if (location.second) { imp->urls().push_back(location.second); } // check if custom importers want to take over the handling else if (!ctx.call_importers(unquote(location.first), getPath(), pstate, imp)) { // nobody wants it, so we do our import ctx.import_url(imp, location.first, getPath()); } } return imp; } Definition_Obj Parser::parse_definition(Definition::Type which_type) { sass::string which_str(lexed); if (!lex< identifier >()) error("invalid name in " + which_str + " definition"); sass::string name(Util::normalize_underscores(lexed)); if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not")) { error("Invalid function name \"" + name + "\"."); } SourceSpan source_position_of_def = pstate; Parameters_Obj params = parse_parameters(); if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin); else stack.push_back(Scope::Function); Block_Obj body = parse_block(); stack.pop_back(); return SASS_MEMORY_NEW(Definition, source_position_of_def, name, params, body, which_type); } Parameters_Obj Parser::parse_parameters() { Parameters_Obj params = SASS_MEMORY_NEW(Parameters, pstate); if (lex_css< exactly<'('> >()) { // if there's anything there at all if (!peek_css< exactly<')'> >()) { do { if (peek< exactly<')'> >()) break; params->append(parse_parameter()); } while (lex_css< exactly<','> >()); } if (!lex_css< exactly<')'> >()) { css_error("Invalid CSS", " after ", ": expected \")\", was "); } } return params; } Parameter_Obj Parser::parse_parameter() { if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) { css_error("Invalid CSS", " after ", ": expected variable (e.g. $foo), was "); } while (lex< alternatives < spaces, block_comment > >()); lex < variable >(); sass::string name(Util::normalize_underscores(lexed)); SourceSpan pos = pstate; ExpressionObj val; bool is_rest = false; while (lex< alternatives < spaces, block_comment > >()); if (lex< exactly<':'> >()) { // there's a default value while (lex< block_comment >()); val = parse_space_list(); } else if (lex< exactly< ellipsis > >()) { is_rest = true; } return SASS_MEMORY_NEW(Parameter, pos, name, val, is_rest); } Arguments_Obj Parser::parse_arguments() { Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); if (lex_css< exactly<'('> >()) { // if there's anything there at all if (!peek_css< exactly<')'> >()) { do { if (peek< exactly<')'> >()) break; args->append(parse_argument()); } while (lex_css< exactly<','> >()); } if (!lex_css< exactly<')'> >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } } return args; } Argument_Obj Parser::parse_argument() { if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) { css_error("Invalid CSS", " after ", ": expected \")\", was "); } if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) { position += 2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } Argument_Obj arg; if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) { lex_css< variable >(); sass::string name(Util::normalize_underscores(lexed)); SourceSpan p = pstate; lex_css< exactly<':'> >(); ExpressionObj val = parse_space_list(); arg = SASS_MEMORY_NEW(Argument, p, val, name); } else { bool is_arglist = false; bool is_keyword = false; ExpressionObj val = parse_space_list(); List* l = Cast(val); if (lex_css< exactly< ellipsis > >()) { if (val->concrete_type() == Expression::MAP || ( (l != NULL && l->separator() == SASS_HASH) )) is_keyword = true; else is_arglist = true; } arg = SASS_MEMORY_NEW(Argument, pstate, val, "", is_arglist, is_keyword); } return arg; } Assignment_Obj Parser::parse_assignment() { sass::string name(Util::normalize_underscores(lexed)); SourceSpan var_source_position = pstate; if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement"); if (peek_css< alternatives < exactly<';'>, end_of_file > >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } ExpressionObj val; Lookahead lookahead = lookahead_for_value(position); if (lookahead.has_interpolants && lookahead.found) { val = parse_value_schema(lookahead.found); } else { val = parse_list(); } bool is_default = false; bool is_global = false; while (peek< alternatives < default_flag, global_flag > >()) { if (lex< default_flag >()) is_default = true; else if (lex< global_flag >()) is_global = true; } return SASS_MEMORY_NEW(Assignment, var_source_position, name, val, is_default, is_global); } // a ruleset connects a selector and a block StyleRuleObj Parser::parse_ruleset(Lookahead lookahead) { NESTING_GUARD(nestings); // inherit is_root from parent block Block_Obj parent = block_stack.back(); bool is_root = parent && parent->is_root(); // make sure to move up the the last position lex < optional_css_whitespace >(false, true); // create the connector object (add parts later) StyleRuleObj ruleset = SASS_MEMORY_NEW(StyleRule, pstate); // parse selector static or as schema to be evaluated later if (lookahead.parsable) { ruleset->selector(parseSelectorList(false)); } else { SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate); auto sc = parse_selector_schema(lookahead.position, false); ruleset->schema(sc); ruleset->selector(list); } // then parse the inner block stack.push_back(Scope::Rules); ruleset->block(parse_block()); stack.pop_back(); // update for end position ruleset->update_pstate(pstate); ruleset->block()->update_pstate(pstate); // need this info for coherence checks ruleset->is_root(is_root); // return AST Node return ruleset; } // parse a selector schema that will be evaluated in the eval stage // uses a string schema internally to do the actual schema handling // in the eval stage we will be re-parse it into an actual selector Selector_Schema_Obj Parser::parse_selector_schema(const char* end_of_selector, bool chroot) { NESTING_GUARD(nestings); // move up to the start lex< optional_spaces >(); const char* i = position; // selector schema re-uses string schema implementation String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate); // the selector schema is pretty much just a wrapper for the string schema Selector_Schema_Obj selector_schema = SASS_MEMORY_NEW(Selector_Schema, pstate, schema); selector_schema->connect_parent(chroot == false); // process until end while (i < end_of_selector) { // try to parse multiple interpolants if (const char* p = find_first_in_interval< exactly, block_comment >(i, end_of_selector)) { // accumulate the preceding segment if the position has advanced if (i < p) { sass::string parsed(i, p); String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed); pstate.position += Offset(parsed); str->update_pstate(pstate); schema->append(str); } // skip over all nested inner interpolations up to our own delimiter const char* j = skip_over_scopes< exactly, exactly >(p + 2, end_of_selector); // check if the interpolation never ends of only contains white-space (error out) if (!j || peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } // pass inner expression to the parser to resolve nested interpolations LocalOption partEnd(end, j); LocalOption partBeg(position, p + 2); ExpressionObj interpolant = parse_list(); // set status on the list expression interpolant->is_interpolant(true); // schema->has_interpolants(true); // add to the string schema schema->append(interpolant); // advance parser state pstate.position.add(p+2, j); // advance position i = j; } // no more interpolants have been found // add the last segment if there is one else { // make sure to add the last bits of the string up to the end (if any) if (i < end_of_selector) { sass::string parsed(i, end_of_selector); String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed); pstate.position += Offset(parsed); str->update_pstate(pstate); i = end_of_selector; schema->append(str); } // exit loop } } // EO until eos // update position position = i; // update for end position selector_schema->update_pstate(pstate); schema->update_pstate(pstate); after_token = before_token = pstate.position; // return parsed result return selector_schema.detach(); } // EO parse_selector_schema void Parser::parse_charset_directive() { lex < sequence < quoted_string, optional_spaces, exactly <';'> > >(); } // called after parsing `kwd_include_directive` Mixin_Call_Obj Parser::parse_include_directive() { // lex identifier into `lexed` var lex_identifier(); // may error out // normalize underscores to hyphens sass::string name(Util::normalize_underscores(lexed)); // create the initial mixin call object Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, Arguments_Obj{}); // parse mandatory arguments call->arguments(parse_arguments()); // parse using and optional block parameters bool has_parameters = lex< kwd_using >() != nullptr; if (has_parameters) { if (!peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \"(\", was "); } else { if (peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \";\", was "); } if (has_parameters) call->block_parameters(parse_parameters()); // parse optional block if (peek < exactly <'{'> >()) { call->block(parse_block()); } else if (has_parameters) { css_error("Invalid CSS", " after ", ": expected \"{\", was "); } // return ast node return call.detach(); } // EO parse_include_directive SimpleSelectorObj Parser::parse_simple_selector() { lex < css_comments >(false); if (lex< class_name >()) { return SASS_MEMORY_NEW(ClassSelector, pstate, lexed); } else if (lex< id_name >()) { return SASS_MEMORY_NEW(IDSelector, pstate, lexed); } else if (lex< alternatives < variable, number, static_reference_combinator > >()) { return SASS_MEMORY_NEW(TypeSelector, pstate, lexed); } else if (peek< pseudo_not >()) { return parse_negated_selector2(); } else if (peek< re_pseudo_selector >()) { return parse_pseudo_selector(); } else if (peek< exactly<':'> >()) { return parse_pseudo_selector(); } else if (lex < exactly<'['> >()) { return parse_attribute_selector(); } else if (lex< placeholder >()) { return SASS_MEMORY_NEW(PlaceholderSelector, pstate, lexed); } else { css_error("Invalid CSS", " after ", ": expected selector, was "); } // failed return {}; } PseudoSelectorObj Parser::parse_negated_selector2() { lex< pseudo_not >(); sass::string name(lexed); SourceSpan nsource_position = pstate; SelectorListObj negated = parseSelectorList(true); if (!lex< exactly<')'> >()) { error("negated selector is missing ')'"); } name.erase(name.size() - 1); PseudoSelector* sel = SASS_MEMORY_NEW(PseudoSelector, nsource_position, name.substr(1)); sel->selector(negated); return sel; } // Helper to clean binominal string bool BothAreSpaces(char lhs, char rhs) { return isspace(lhs) && isspace(rhs); } // a pseudo selector often starts with one or two colons // it can contain more selectors inside parentheses SimpleSelectorObj Parser::parse_pseudo_selector() { // Lex one or two colon characters if (lex()) { sass::string colons(lexed); // Check if it is a pseudo element bool element = colons.size() == 2; if (lex< sequence< // we keep the space within the name, strange enough // ToDo: refactor output to schedule the space for it // or do we really want to keep the real white-space? sequence< identifier, optional < block_comment >, exactly<'('> > > >()) { sass::string name(lexed); name.erase(name.size() - 1); SourceSpan p = pstate; // specially parse nth-child pseudo selectors if (lex_css < sequence < binomial, word_boundary >>()) { sass::string parsed(lexed); // always compacting binominals (as dart-sass) parsed.erase(std::unique(parsed.begin(), parsed.end(), BothAreSpaces), parsed.end()); String_Constant_Obj arg = SASS_MEMORY_NEW(String_Constant, pstate, parsed); PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element); if (lex < sequence < css_whitespace, insensitive < of_kwd >>>(false)) { pseudo->selector(parseSelectorList(true)); } pseudo->argument(arg); if (lex_css< exactly<')'> >()) { return pseudo; } } else { if (peek_css< exactly<')'>>() && Util::equalsLiteral("nth-", name.substr(0, 4))) { css_error("Invalid CSS", " after ", ": expected An+B expression, was "); } sass::string unvendored = Util::unvendor(name); if (unvendored == "not" || unvendored == "matches" || unvendored == "current" || unvendored == "any" || unvendored == "has" || unvendored == "host" || unvendored == "host-context" || unvendored == "slotted") { if (SelectorListObj wrapped = parseSelectorList(true)) { if (wrapped && lex_css< exactly<')'> >()) { PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element); pseudo->selector(wrapped); return pseudo; } } } else { String_Schema_Obj arg = parse_css_variable_value(); PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element); pseudo->argument(arg); if (lex_css< exactly<')'> >()) { return pseudo; } } } } // EO if pseudo selector else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) { return SASS_MEMORY_NEW(PseudoSelector, pstate, lexed, element); } else if (lex < pseudo_prefix >()) { css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was "); } } else { lex < identifier >(); // needed for error message? css_error("Invalid CSS", " after ", ": expected selector, was "); } css_error("Invalid CSS", " after ", ": expected \")\", was "); // unreachable statement return {}; } const char* Parser::re_attr_sensitive_close(const char* src) { return alternatives < exactly<']'>, exactly<'/'> >(src); } const char* Parser::re_attr_insensitive_close(const char* src) { return sequence < insensitive<'i'>, re_attr_sensitive_close >(src); } AttributeSelectorObj Parser::parse_attribute_selector() { SourceSpan p = pstate; if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector"); sass::string name(lexed); if (lex_css< re_attr_sensitive_close >()) { return SASS_MEMORY_NEW(AttributeSelector, p, name, "", String_Obj{}); } else if (lex_css< re_attr_insensitive_close >()) { char modifier = lexed.begin[0]; return SASS_MEMORY_NEW(AttributeSelector, p, name, "", String_Obj{}, modifier); } if (!lex_css< alternatives< exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match > >()) { error("invalid operator in attribute selector for " + name); } sass::string matcher(lexed); String_Obj value; if (lex_css< identifier >()) { value = SASS_MEMORY_NEW(String_Constant, p, lexed); } else if (lex_css< quoted_string >()) { value = parse_interpolated_chunk(lexed, true); // needed! } else { error("expected a string constant or identifier in attribute selector for " + name); } if (lex_css< re_attr_sensitive_close >()) { return SASS_MEMORY_NEW(AttributeSelector, p, name, matcher, value, 0); } else if (lex_css< re_attr_insensitive_close >()) { char modifier = lexed.begin[0]; return SASS_MEMORY_NEW(AttributeSelector, p, name, matcher, value, modifier); } error("unterminated attribute selector for " + name); return {}; // to satisfy compilers (error must not return) } /* parse block comment and add to block */ void Parser::parse_block_comments(bool store) { Block_Obj block = block_stack.back(); while (lex< block_comment >()) { bool is_important = lexed.begin[2] == '!'; // flag on second param is to skip loosely over comments String_Obj contents = parse_interpolated_chunk(lexed, true, false); if (store) block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important)); } } Declaration_Obj Parser::parse_declaration() { String_Obj prop; bool is_custom_property = false; if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) { const sass::string property(lexed); is_custom_property = property.compare(0, 2, "--") == 0; prop = parse_identifier_schema(); } else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) { const sass::string property(lexed); is_custom_property = property.compare(0, 2, "--") == 0; prop = SASS_MEMORY_NEW(String_Constant, pstate, lexed); } else { css_error("Invalid CSS", " after ", ": expected \"}\", was "); } bool is_indented = true; const sass::string property(lexed); if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + escape_string(property) + "\" must be followed by a ':'"); if (!is_custom_property && match< sequence< optional_css_comments, exactly<';'> > >()) error("style declaration must contain a value"); if (match< sequence< optional_css_comments, exactly<'{'> > >()) is_indented = false; // don't indent if value is empty if (is_custom_property) { return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_css_variable_value(), false, true); } lex < css_comments >(false); if (peek_css< static_value >()) { return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_static_value()/*, lex()*/); } else { ExpressionObj value; Lookahead lookahead = lookahead_for_value(position); if (lookahead.found) { if (lookahead.has_interpolants) { value = parse_value_schema(lookahead.found); } else { value = parse_list(DELAYED); } } else { value = parse_list(DELAYED); if (List* list = Cast(value)) { if (!list->is_bracketed() && list->length() == 0 && !peek< exactly <'{'> >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } } } lex < css_comments >(false); Declaration_Obj decl = SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, value/*, lex()*/); decl->is_indented(is_indented); decl->update_pstate(pstate); return decl; } } ExpressionObj Parser::parse_map() { NESTING_GUARD(nestings); ExpressionObj key = parse_list(); List_Obj map = SASS_MEMORY_NEW(List, pstate, 0, SASS_HASH); // it's not a map so return the lexed value as a list value if (!lex_css< exactly<':'> >()) { return key; } List_Obj l = Cast(key); if (l && l->separator() == SASS_COMMA) { css_error("Invalid CSS", " after ", ": expected \")\", was "); } ExpressionObj value = parse_space_list(); map->append(key); map->append(value); while (lex_css< exactly<','> >()) { // allow trailing commas - #495 if (peek_css< exactly<')'> >(position)) { break; } key = parse_space_list(); if (!(lex< exactly<':'> >())) { css_error("Invalid CSS", " after ", ": expected \":\", was "); } value = parse_space_list(); map->append(key); map->append(value); } SourceSpan ps = map->pstate(); ps.offset = pstate.position - ps.position + pstate.offset; map->pstate(ps); return map; } ExpressionObj Parser::parse_bracket_list() { NESTING_GUARD(nestings); // check if we have an empty list // return the empty list as such if (peek_css< list_terminator >(position)) { // return an empty list (nothing to delay) return SASS_MEMORY_NEW(List, pstate, 0, SASS_SPACE, false, true); } bool has_paren = peek_css< exactly<'('> >() != NULL; // now try to parse a space list ExpressionObj list = parse_space_list(); // if it's a singleton, return it (don't wrap it) if (!peek_css< exactly<','> >(position)) { List_Obj l = Cast(list); if (!l || l->is_bracketed() || has_paren) { List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 1, SASS_SPACE, false, true); bracketed_list->append(list); return bracketed_list; } l->is_bracketed(true); return l; } // if we got so far, we actually do have a comma list List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA, false, true); // wrap the first expression bracketed_list->append(list); while (lex_css< exactly<','> >()) { // check for abort condition if (peek_css< list_terminator >(position) ) { break; } // otherwise add another expression bracketed_list->append(parse_space_list()); } // return the list return bracketed_list; } // parse list returns either a space separated list, // a comma separated list or any bare expression found. // so to speak: we unwrap items from lists if possible here! ExpressionObj Parser::parse_list(bool delayed) { NESTING_GUARD(nestings); return parse_comma_list(delayed); } // will return singletons unwrapped ExpressionObj Parser::parse_comma_list(bool delayed) { NESTING_GUARD(nestings); // check if we have an empty list // return the empty list as such if (peek_css< list_terminator >(position)) { // return an empty list (nothing to delay) return SASS_MEMORY_NEW(List, pstate, 0); } // now try to parse a space list ExpressionObj list = parse_space_list(); // if it's a singleton, return it (don't wrap it) if (!peek_css< exactly<','> >(position)) { // set_delay doesn't apply to list children // so this will only undelay single values if (!delayed) list->set_delayed(false); return list; } // if we got so far, we actually do have a comma list List_Obj comma_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA); // wrap the first expression comma_list->append(list); while (lex_css< exactly<','> >()) { // check for abort condition if (peek_css< list_terminator >(position) ) { break; } // otherwise add another expression comma_list->append(parse_space_list()); } // return the list return comma_list; } // EO parse_comma_list // will return singletons unwrapped ExpressionObj Parser::parse_space_list() { NESTING_GUARD(nestings); ExpressionObj disj1 = parse_disjunction(); // if it's a singleton, return it (don't wrap it) if (peek_css< space_list_terminator >(position) ) { return disj1; } List_Obj space_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_SPACE); space_list->append(disj1); while ( !(peek_css< space_list_terminator >(position)) && peek_css< optional_css_whitespace >() != end ) { // the space is parsed implicitly? space_list->append(parse_disjunction()); } // return the list return space_list; } // EO parse_space_list // parse logical OR operation ExpressionObj Parser::parse_disjunction() { NESTING_GUARD(nestings); advanceToNextToken(); SourceSpan state(pstate); // parse the left hand side conjunction ExpressionObj conj = parse_conjunction(); // parse multiple right hand sides sass::vector operands; while (lex_css< kwd_or >()) operands.push_back(parse_conjunction()); // if it's a singleton, return it directly if (operands.size() == 0) return conj; // fold all operands into one binary expression ExpressionObj ex = fold_operands(conj, operands, { Sass_OP::OR }); state.offset = pstate.position - state.position + pstate.offset; ex->pstate(state); return ex; } // EO parse_disjunction // parse logical AND operation ExpressionObj Parser::parse_conjunction() { NESTING_GUARD(nestings); advanceToNextToken(); SourceSpan state(pstate); // parse the left hand side relation ExpressionObj rel = parse_relation(); // parse multiple right hand sides sass::vector operands; while (lex_css< kwd_and >()) { operands.push_back(parse_relation()); } // if it's a singleton, return it directly if (operands.size() == 0) return rel; // fold all operands into one binary expression ExpressionObj ex = fold_operands(rel, operands, { Sass_OP::AND }); state.offset = pstate.position - state.position + pstate.offset; ex->pstate(state); return ex; } // EO parse_conjunction // parse comparison operations ExpressionObj Parser::parse_relation() { NESTING_GUARD(nestings); advanceToNextToken(); SourceSpan state(pstate); // parse the left hand side expression ExpressionObj lhs = parse_expression(); sass::vector operands; sass::vector operators; // if it's a singleton, return it (don't wrap it) while (peek< alternatives < kwd_eq, kwd_neq, kwd_gte, kwd_gt, kwd_lte, kwd_lt > >(position)) { // is directly adjancent to expression? bool left_ws = peek < css_comments >() != NULL; // parse the operator enum Sass_OP op = lex() ? Sass_OP::EQ : lex() ? Sass_OP::NEQ : lex() ? Sass_OP::GTE : lex() ? Sass_OP::LTE : lex() ? Sass_OP::GT : lex() ? Sass_OP::LT // we checked the possibilities on top of fn : Sass_OP::EQ; // is directly adjacent to expression? bool right_ws = peek < css_comments >() != NULL; operators.push_back({ op, left_ws, right_ws }); operands.push_back(parse_expression()); } // we are called recursively for list, so we first // fold inner binary expression which has delayed // correctly set to zero. After folding we also unwrap // single nested items. So we cannot set delay on the // returned result here, as we have lost nestings ... ExpressionObj ex = fold_operands(lhs, operands, operators); state.offset = pstate.position - state.position + pstate.offset; ex->pstate(state); return ex; } // parse_relation // parse expression valid for operations // called from parse_relation // called from parse_for_directive // called from parse_media_expression // parse addition and subtraction operations ExpressionObj Parser::parse_expression() { NESTING_GUARD(nestings); advanceToNextToken(); SourceSpan state(pstate); // parses multiple add and subtract operations // NOTE: make sure that identifiers starting with // NOTE: dashes do NOT count as subtract operation ExpressionObj lhs = parse_operators(); // if it's a singleton, return it (don't wrap it) if (!(peek_css< exactly<'+'> >(position) || // condition is a bit mysterious, but some combinations should not be counted as operations (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) || peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position)) { return lhs; } sass::vector operands; sass::vector operators; bool left_ws = peek < css_comments >() != NULL; while ( lex_css< exactly<'+'> >() || ( ! peek_css< sequence < zero_plus < exactly <'-' > >, identifier > >(position) && lex_css< sequence< negate< digit >, exactly<'-'> > >() ) ) { bool right_ws = peek < css_comments >() != NULL; operators.push_back({ lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB, left_ws, right_ws }); operands.push_back(parse_operators()); left_ws = peek < css_comments >() != NULL; } if (operands.size() == 0) return lhs; ExpressionObj ex = fold_operands(lhs, operands, operators); state.offset = pstate.position - state.position + pstate.offset; ex->pstate(state); return ex; } // parse addition and subtraction operations ExpressionObj Parser::parse_operators() { NESTING_GUARD(nestings); advanceToNextToken(); SourceSpan state(pstate); ExpressionObj factor = parse_factor(); // if it's a singleton, return it (don't wrap it) sass::vector operands; // factors sass::vector operators; // ops // lex operations to apply to lhs const char* left_ws = peek < css_comments >(); while (lex_css< class_char< static_ops > >()) { const char* right_ws = peek < css_comments >(); switch(*lexed.begin) { case '*': operators.push_back({ Sass_OP::MUL, left_ws != 0, right_ws != 0 }); break; case '/': operators.push_back({ Sass_OP::DIV, left_ws != 0, right_ws != 0 }); break; case '%': operators.push_back({ Sass_OP::MOD, left_ws != 0, right_ws != 0 }); break; default: throw std::runtime_error("unknown static op parsed"); } operands.push_back(parse_factor()); left_ws = peek < css_comments >(); } // operands and operators to binary expression ExpressionObj ex = fold_operands(factor, operands, operators); state.offset = pstate.position - state.position + pstate.offset; ex->pstate(state); return ex; } // EO parse_operators // called from parse_operators // called from parse_value_schema ExpressionObj Parser::parse_factor() { NESTING_GUARD(nestings); lex < css_comments >(false); if (lex_css< exactly<'('> >()) { // parse_map may return a list ExpressionObj value = parse_map(); // lex the expected closing parenthesis if (!lex_css< exactly<')'> >()) error("unclosed parenthesis"); // expression can be evaluated return value; } else if (lex_css< exactly<'['> >()) { // explicit bracketed ExpressionObj value = parse_bracket_list(); // lex the expected closing square bracket if (!lex_css< exactly<']'> >()) error("unclosed squared bracket"); return value; } // string may be interpolated // if (lex< quoted_string >()) { // return &parse_string(); // } else if (peek< ie_property >()) { return parse_ie_property(); } else if (peek< ie_keyword_arg >()) { return parse_ie_keyword_arg(); } else if (peek< sequence < calc_fn_call, exactly <'('> > >()) { return parse_calc_function(); } else if (lex < functional_schema >()) { return parse_function_call_schema(); } else if (lex< identifier_schema >()) { String_Obj string = parse_identifier_schema(); if (String_Schema* schema = Cast(string)) { if (lex < exactly < '(' > >()) { schema->append(parse_list()); lex < exactly < ')' > >(); } } return string; } else if (peek< sequence< uri_prefix, W, real_uri_value > >()) { return parse_url_function_string(); } else if (peek< re_functional >()) { return parse_function_call(); } else if (lex< exactly<'+'> >()) { Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; } else if (lex< exactly<'-'> >()) { Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; } else if (lex< exactly<'/'> >()) { Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; } else if (lex< sequence< kwd_not > >()) { Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor()); if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); return ex; } else { return parse_value(); } } bool number_has_zero(const sass::string& parsed) { size_t L = parsed.length(); return !( (L > 0 && parsed.substr(0, 1) == ".") || (L > 1 && parsed.substr(0, 2) == "0.") || (L > 1 && parsed.substr(0, 2) == "-.") || (L > 2 && parsed.substr(0, 3) == "-0.") ); } Number* Parser::lexed_number(const SourceSpan& pstate, const sass::string& parsed) { Number* nr = SASS_MEMORY_NEW(Number, pstate, sass_strtod(parsed.c_str()), "", number_has_zero(parsed)); nr->is_interpolant(false); nr->is_delayed(true); return nr; } Number* Parser::lexed_percentage(const SourceSpan& pstate, const sass::string& parsed) { Number* nr = SASS_MEMORY_NEW(Number, pstate, sass_strtod(parsed.c_str()), "%", true); nr->is_interpolant(false); nr->is_delayed(true); return nr; } Number* Parser::lexed_dimension(const SourceSpan& pstate, const sass::string& parsed) { size_t L = parsed.length(); size_t num_pos = parsed.find_first_not_of(" \n\r\t"); if (num_pos == sass::string::npos) num_pos = L; size_t unit_pos = parsed.find_first_not_of("-+0123456789.", num_pos); if (parsed[unit_pos] == 'e' && is_number(parsed[unit_pos+1]) ) { unit_pos = parsed.find_first_not_of("-+0123456789.", ++ unit_pos); } if (unit_pos == sass::string::npos) unit_pos = L; const sass::string& num = parsed.substr(num_pos, unit_pos - num_pos); Number* nr = SASS_MEMORY_NEW(Number, pstate, sass_strtod(num.c_str()), Token(number(parsed.c_str())), number_has_zero(parsed)); nr->is_interpolant(false); nr->is_delayed(true); return nr; } Value* Parser::lexed_hex_color(const SourceSpan& pstate, const sass::string& parsed) { Color_RGBA* color = NULL; if (parsed[0] != '#') { return SASS_MEMORY_NEW(String_Quoted, pstate, parsed); } // chop off the '#' sass::string hext(parsed.substr(1)); if (parsed.length() == 4) { sass::string r(2, parsed[1]); sass::string g(2, parsed[2]); sass::string b(2, parsed[3]); color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast(strtol(r.c_str(), NULL, 16)), static_cast(strtol(g.c_str(), NULL, 16)), static_cast(strtol(b.c_str(), NULL, 16)), 1, // alpha channel parsed); } else if (parsed.length() == 5) { sass::string r(2, parsed[1]); sass::string g(2, parsed[2]); sass::string b(2, parsed[3]); sass::string a(2, parsed[4]); color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast(strtol(r.c_str(), NULL, 16)), static_cast(strtol(g.c_str(), NULL, 16)), static_cast(strtol(b.c_str(), NULL, 16)), static_cast(strtol(a.c_str(), NULL, 16)) / 255, parsed); } else if (parsed.length() == 7) { sass::string r(parsed.substr(1,2)); sass::string g(parsed.substr(3,2)); sass::string b(parsed.substr(5,2)); color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast(strtol(r.c_str(), NULL, 16)), static_cast(strtol(g.c_str(), NULL, 16)), static_cast(strtol(b.c_str(), NULL, 16)), 1, // alpha channel parsed); } else if (parsed.length() == 9) { sass::string r(parsed.substr(1,2)); sass::string g(parsed.substr(3,2)); sass::string b(parsed.substr(5,2)); sass::string a(parsed.substr(7,2)); color = SASS_MEMORY_NEW(Color_RGBA, pstate, static_cast(strtol(r.c_str(), NULL, 16)), static_cast(strtol(g.c_str(), NULL, 16)), static_cast(strtol(b.c_str(), NULL, 16)), static_cast(strtol(a.c_str(), NULL, 16)) / 255, parsed); } color->is_interpolant(false); color->is_delayed(false); return color; } Value* Parser::color_or_string(const sass::string& lexed) const { if (auto color = name_to_color(lexed)) { auto c = SASS_MEMORY_NEW(Color_RGBA, color); c->is_delayed(true); c->pstate(pstate); c->disp(lexed); return c; } else { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); } } // parse one value for a list ExpressionObj Parser::parse_value() { lex< css_comments >(false); if (lex< ampersand >()) { if (match< ampersand >()) { warning("In Sass, \"&&\" means two copies of the parent selector. You probably want to use \"and\" instead.", pstate); } return SASS_MEMORY_NEW(Parent_Reference, pstate); } if (lex< kwd_important >()) { return SASS_MEMORY_NEW(String_Constant, pstate, "!important"); } // parse `10%4px` into separated items and not a schema if (lex< sequence < percentage, lookahead < number > > >()) { return lexed_percentage(lexed); } if (lex< sequence < number, lookahead< sequence < op, number > > > >()) { return lexed_number(lexed); } // string may be interpolated if (lex< sequence < quoted_string, lookahead < exactly <'-'> > > >()) { return parse_string(); } if (const char* stop = peek< value_schema >()) { return parse_value_schema(stop); } // string may be interpolated if (lex< quoted_string >()) { return parse_string(); } if (lex< kwd_true >()) { return SASS_MEMORY_NEW(Boolean, pstate, true); } if (lex< kwd_false >()) { return SASS_MEMORY_NEW(Boolean, pstate, false); } if (lex< kwd_null >()) { return SASS_MEMORY_NEW(Null, pstate); } if (lex< identifier >()) { return color_or_string(lexed); } if (lex< percentage >()) { return lexed_percentage(lexed); } // match hex number first because 0x000 looks like a number followed by an identifier if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >()) { return lexed_hex_color(lexed); } if (lex< hexa >()) { return lexed_hex_color(lexed); } if (lex< sequence < exactly <'#'>, identifier > >()) { return SASS_MEMORY_NEW(String_Quoted, pstate, lexed); } // also handle the 10em- foo special case // alternatives < exactly < '.' >, .. > -- `1.5em-.75em` is split into a list, not a binary expression if (lex< sequence< dimension, optional< sequence< exactly<'-'>, lookahead< alternatives < space > > > > > >()) { return lexed_dimension(lexed); } if (lex< sequence< static_component, one_plus< strict_identifier > > >()) { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); } if (lex< number >()) { return lexed_number(lexed); } if (lex< variable >()) { return SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed)); } css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); // unreachable statement return {}; } // this parses interpolation inside other strings // means the result should later be quoted again String_Obj Parser::parse_interpolated_chunk(Token chunk, bool constant, bool css) { const char* i = chunk.begin; // see if there any interpolants const char* p = constant ? find_first_in_interval< exactly >(i, chunk.end) : find_first_in_interval< exactly, block_comment >(i, chunk.end); if (!p) { String_Quoted* str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, sass::string(i, chunk.end), 0, false, false, true, css); if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*'); return str_quoted; } String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate, 0, css); schema->is_interpolant(true); while (i < chunk.end) { p = constant ? find_first_in_interval< exactly >(i, chunk.end) : find_first_in_interval< exactly, block_comment >(i, chunk.end); if (p) { if (i < p) { // accumulate the preceding segment if it's nonempty schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, p), css)); } // we need to skip anything inside strings // create a new target in parser/prelexer if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* j = skip_over_scopes< exactly, exactly >(p + 2, chunk.end); // find the closing brace if (j) { --j; // parse the interpolant and accumulate it LocalOption partEnd(end, j); LocalOption partBeg(position, p + 2); ExpressionObj interp_node = parse_list(); interp_node->is_interpolant(true); schema->append(interp_node); i = j; } else { // throw an error if the interpolant is unterminated error("unterminated interpolant inside string constant " + chunk.to_string()); } } else { // no interpolants left; add the last segment if nonempty // check if we need quotes here (was not sure after merge) if (i < chunk.end) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, chunk.end), css)); break; } ++ i; } return schema.detach(); } String_Schema_Obj Parser::parse_css_variable_value() { String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); sass::vector brackets; while (true) { if ( (brackets.empty() && lex< css_variable_top_level_value >(false)) || (!brackets.empty() && lex< css_variable_value >(false)) ) { Token str(lexed); schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str)); } else if (ExpressionObj tok = lex_interpolation()) { if (String_Schema* s = Cast(tok)) { if (s->empty()) break; schema->concat(s); } else { schema->append(tok); } } else if (lex< quoted_string >()) { ExpressionObj tok = parse_string(); if (tok.isNull()) break; if (String_Schema* s = Cast(tok)) { if (s->empty()) break; schema->concat(s); } else { schema->append(tok); } } else if (lex< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) { const char opening_bracket = *(position - 1); brackets.push_back(opening_bracket); schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(1, opening_bracket))); } else if (const char *match = peek< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >()) { if (brackets.empty()) break; const char closing_bracket = *(match - 1); if (brackets.back() != Util::opening_bracket_for(closing_bracket)) { sass::string message = ": expected \""; message += Util::closing_bracket_for(brackets.back()); message += "\", was "; css_error("Invalid CSS", " after ", message); } lex< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >(); schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(1, closing_bracket))); brackets.pop_back(); } else { break; } } if (!brackets.empty()) { sass::string message = ": expected \""; message += Util::closing_bracket_for(brackets.back()); message += "\", was "; css_error("Invalid CSS", " after ", message); } if (schema->empty()) error("Custom property values may not be empty."); return schema.detach(); } ValueObj Parser::parse_static_value() { lex< static_value >(); Token str(lexed); // static values always have trailing white- // space and end delimiter (\s*[;]$) included --pstate.offset.column; --after_token.column; --str.end; --position; return color_or_string(str.time_wspace());; } String_Obj Parser::parse_string() { return parse_interpolated_chunk(Token(lexed)); } String_Obj Parser::parse_ie_property() { lex< ie_property >(); Token str(lexed); const char* i = str.begin; // see if there any interpolants const char* p = find_first_in_interval< exactly, block_comment >(str.begin, str.end); if (!p) { return SASS_MEMORY_NEW(String_Quoted, pstate, sass::string(str.begin, str.end)); } String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate); while (i < str.end) { p = find_first_in_interval< exactly, block_comment >(i, str.end); if (p) { if (i < p) { schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, p))); // accumulate the preceding segment if it's nonempty } if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* j = skip_over_scopes< exactly, exactly >(p+2, str.end); // find the closing brace if (j) { // parse the interpolant and accumulate it LocalOption partEnd(end, j); LocalOption partBeg(position, p + 2); ExpressionObj interp_node = parse_list(); interp_node->is_interpolant(true); schema->append(interp_node); i = j; } else { // throw an error if the interpolant is unterminated error("unterminated interpolant inside IE function " + str.to_string()); } } else { // no interpolants left; add the last segment if nonempty if (i < str.end) { schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, str.end))); } break; } } return schema; } String_Obj Parser::parse_ie_keyword_arg() { String_Schema_Obj kwd_arg = SASS_MEMORY_NEW(String_Schema, pstate, 3); if (lex< variable >()) { kwd_arg->append(SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed))); } else { lex< alternatives< identifier_schema, identifier > >(); kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); } lex< exactly<'='> >(); kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); if (peek< variable >()) kwd_arg->append(parse_list()); else if (lex< number >()) { sass::string parsed(lexed); Util::normalize_decimals(parsed); kwd_arg->append(lexed_number(parsed)); } else if (peek < ie_keyword_arg_value >()) { kwd_arg->append(parse_list()); } return kwd_arg; } String_Schema_Obj Parser::parse_value_schema(const char* stop) { // initialize the string schema object to add tokens String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); if (peek>()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* e; const char* ee = end; end = stop; size_t num_items = 0; bool need_space = false; while (position < stop) { // parse space between tokens if (lex< spaces >() && num_items) { need_space = true; } if (need_space) { need_space = false; // schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " ")); } if ((e = peek< re_functional >()) && e < stop) { schema->append(parse_function_call()); } // lex an interpolant /#{...}/ else if (lex< exactly < hash_lbrace > >()) { // Try to lex static expression first if (peek< exactly< rbrace > >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } ExpressionObj ex; if (lex< re_static_expression >()) { ex = SASS_MEMORY_NEW(String_Constant, pstate, lexed); } else { ex = parse_list(true); } ex->is_interpolant(true); schema->append(ex); if (!lex < exactly < rbrace > >()) { css_error("Invalid CSS", " after ", ": expected \"}\", was "); } } // lex some string constants or other valid token // Note: [-+] chars are left over from i.e. `#{3}+3` else if (lex< alternatives < exactly<'%'>, exactly < '-' >, exactly < '+' > > >()) { schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); } // lex a quoted string else if (lex< quoted_string >()) { // need_space = true; // if (schema->length()) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " ")); // else need_space = true; schema->append(parse_string()); if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { // need_space = true; } if (peek < exactly < '-' > >()) break; } else if (lex< identifier >()) { schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { // need_space = true; } } // lex (normalized) variable else if (lex< variable >()) { sass::string name(Util::normalize_underscores(lexed)); schema->append(SASS_MEMORY_NEW(Variable, pstate, name)); } // lex percentage value else if (lex< percentage >()) { schema->append(lexed_percentage(lexed)); } // lex dimension value else if (lex< dimension >()) { schema->append(lexed_dimension(lexed)); } // lex number value else if (lex< number >()) { schema->append(lexed_number(lexed)); } // lex hex color value else if (lex< sequence < hex, negate < exactly < '-' > > > >()) { schema->append(lexed_hex_color(lexed)); } else if (lex< sequence < exactly <'#'>, identifier > >()) { schema->append(SASS_MEMORY_NEW(String_Quoted, pstate, lexed)); } // lex a value in parentheses else if (peek< parenthese_scope >()) { schema->append(parse_factor()); } else { break; } ++num_items; } if (position != stop) { schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(position, stop))); position = stop; } end = ee; return schema; } // this parses interpolation outside other strings // means the result must not be quoted again later String_Obj Parser::parse_identifier_schema() { Token id(lexed); const char* i = id.begin; // see if there any interpolants const char* p = find_first_in_interval< exactly, block_comment >(id.begin, id.end); if (!p) { return SASS_MEMORY_NEW(String_Constant, pstate, sass::string(id.begin, id.end)); } String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); while (i < id.end) { p = find_first_in_interval< exactly, block_comment >(i, id.end); if (p) { if (i < p) { // accumulate the preceding segment if it's nonempty const char* o = position; position = i; schema->append(parse_value_schema(p)); position = o; } // we need to skip anything inside strings // create a new target in parser/prelexer if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* j = skip_over_scopes< exactly, exactly >(p+2, id.end); // find the closing brace if (j) { // parse the interpolant and accumulate it LocalOption partEnd(end, j); LocalOption partBeg(position, p + 2); ExpressionObj interp_node = parse_list(DELAYED); interp_node->is_interpolant(true); schema->append(interp_node); // schema->has_interpolants(true); i = j; } else { // throw an error if the interpolant is unterminated error("unterminated interpolant inside interpolated identifier " + id.to_string()); } } else { // no interpolants left; add the last segment if nonempty if (i < end) { const char* o = position; position = i; schema->append(parse_value_schema(id.end)); position = o; } break; } } return schema ? schema.detach() : 0; } // calc functions should preserve arguments Function_Call_Obj Parser::parse_calc_function() { lex< identifier >(); sass::string name(lexed); SourceSpan call_pos = pstate; lex< exactly<'('> >(); SourceSpan arg_pos = pstate; const char* arg_beg = position; parse_list(); const char* arg_end = position; lex< skip_over_scopes < exactly < '(' >, exactly < ')' > > >(); Argument_Obj arg = SASS_MEMORY_NEW(Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end))); Arguments_Obj args = SASS_MEMORY_NEW(Arguments, arg_pos); args->append(arg); return SASS_MEMORY_NEW(Function_Call, call_pos, name, args); } String_Obj Parser::parse_url_function_string() { sass::string prefix(""); if (lex< uri_prefix >()) { prefix = sass::string(lexed); } lex < optional_spaces >(); String_Obj url_string = parse_url_function_argument(); sass::string suffix(""); if (lex< real_uri_suffix >()) { suffix = sass::string(lexed); } sass::string uri(""); if (url_string) { uri = url_string->to_string({ NESTED, 5 }); } if (String_Schema* schema = Cast(url_string)) { String_Schema_Obj res = SASS_MEMORY_NEW(String_Schema, pstate); res->append(SASS_MEMORY_NEW(String_Constant, pstate, prefix)); res->append(schema); res->append(SASS_MEMORY_NEW(String_Constant, pstate, suffix)); return res; } else { sass::string res = prefix + uri + suffix; return SASS_MEMORY_NEW(String_Constant, pstate, res); } } String_Obj Parser::parse_url_function_argument() { const char* p = position; sass::string uri(""); if (lex< real_uri_value >(false)) { uri = lexed.to_string(); } if (peek< exactly< hash_lbrace > >()) { const char* pp = position; // TODO: error checking for unclosed interpolants while (pp && peek< exactly< hash_lbrace > >(pp)) { pp = sequence< interpolant, real_uri_value >(pp); } if (!pp) return {}; position = pp; return parse_interpolated_chunk(Token(p, position)); } else if (uri != "") { sass::string res = Util::rtrim(uri); return SASS_MEMORY_NEW(String_Constant, pstate, res); } return {}; } Function_Call_Obj Parser::parse_function_call() { lex< identifier >(); sass::string name(lexed); if (Util::normalize_underscores(name) == "content-exists" && stack.back() != Scope::Mixin) { error("Cannot call content-exists() except within a mixin."); } SourceSpan call_pos = pstate; Arguments_Obj args = parse_arguments(); return SASS_MEMORY_NEW(Function_Call, call_pos, name, args); } Function_Call_Obj Parser::parse_function_call_schema() { String_Obj name = parse_identifier_schema(); SourceSpan source_position_of_call = pstate; Arguments_Obj args = parse_arguments(); return SASS_MEMORY_NEW(Function_Call, source_position_of_call, name, args); } Content_Obj Parser::parse_content_directive() { SourceSpan call_pos = pstate; Arguments_Obj args = parse_arguments(); return SASS_MEMORY_NEW(Content, call_pos, args); } If_Obj Parser::parse_if_directive(bool else_if) { stack.push_back(Scope::Control); SourceSpan if_source_position = pstate; bool root = block_stack.back()->is_root(); ExpressionObj predicate = parse_list(); Block_Obj block = parse_block(root); Block_Obj alternative; // only throw away comment if we parse a case // we want all other comments to be parsed if (lex_css< elseif_directive >()) { alternative = SASS_MEMORY_NEW(Block, pstate); alternative->append(parse_if_directive(true)); } else if (lex_css< kwd_else_directive >()) { alternative = parse_block(root); } stack.pop_back(); return SASS_MEMORY_NEW(If, if_source_position, predicate, block, alternative); } ForRuleObj Parser::parse_for_directive() { stack.push_back(Scope::Control); SourceSpan for_source_position = pstate; bool root = block_stack.back()->is_root(); lex_variable(); sass::string var(Util::normalize_underscores(lexed)); if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive"); ExpressionObj lower_bound = parse_expression(); bool inclusive = false; if (lex< kwd_through >()) inclusive = true; else if (lex< kwd_to >()) inclusive = false; else error("expected 'through' or 'to' keyword in @for directive"); ExpressionObj upper_bound = parse_expression(); Block_Obj body = parse_block(root); stack.pop_back(); return SASS_MEMORY_NEW(ForRule, for_source_position, var, lower_bound, upper_bound, body, inclusive); } // helper to parse a var token Token Parser::lex_variable() { // peek for dollar sign first if (!peek< exactly <'$'> >()) { css_error("Invalid CSS", " after ", ": expected \"$\", was "); } // we expect a simple identifier as the call name if (!lex< sequence < exactly <'$'>, identifier > >()) { lex< exactly <'$'> >(); // move pstate and position up css_error("Invalid CSS", " after ", ": expected identifier, was "); } // return object return lexed; } // helper to parse identifier Token Parser::lex_identifier() { // we expect a simple identifier as the call name if (!lex< identifier >()) { // ToDo: pstate wrong? css_error("Invalid CSS", " after ", ": expected identifier, was "); } // return object return lexed; } EachRuleObj Parser::parse_each_directive() { stack.push_back(Scope::Control); SourceSpan each_source_position = pstate; bool root = block_stack.back()->is_root(); sass::vector vars; lex_variable(); vars.push_back(Util::normalize_underscores(lexed)); while (lex< exactly<','> >()) { if (!lex< variable >()) error("@each directive requires an iteration variable"); vars.push_back(Util::normalize_underscores(lexed)); } if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive"); ExpressionObj list = parse_list(); Block_Obj body = parse_block(root); stack.pop_back(); return SASS_MEMORY_NEW(EachRule, each_source_position, vars, list, body); } // called after parsing `kwd_while_directive` WhileRuleObj Parser::parse_while_directive() { stack.push_back(Scope::Control); bool root = block_stack.back()->is_root(); // create the initial while call object WhileRuleObj call = SASS_MEMORY_NEW(WhileRule, pstate, ExpressionObj{}, Block_Obj{}); // parse mandatory predicate ExpressionObj predicate = parse_list(); List_Obj l = Cast(predicate); if (!predicate || (l && !l->length())) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ", false); } call->predicate(predicate); // parse mandatory block call->block(parse_block(root)); // return ast node stack.pop_back(); // return ast node return call.detach(); } sass::vector Parser::parseCssMediaQueries() { sass::vector result; do { if (auto query = parseCssMediaQuery()) { result.push_back(query); } } while (lex>()); return result; } sass::string Parser::parseIdentifier() { if (lex < identifier >(false)) { return sass::string(lexed); } return sass::string(); } CssMediaQuery_Obj Parser::parseCssMediaQuery() { CssMediaQuery_Obj result = SASS_MEMORY_NEW(CssMediaQuery, pstate); lex(false); // Check if any tokens are to parse if (!peek_css>()) { sass::string token1(parseIdentifier()); lex(false); if (token1.empty()) { return {}; } sass::string token2(parseIdentifier()); lex(false); if (Util::equalsLiteral("and", token2)) { result->type(token1); } else { if (token2.empty()) { result->type(token1); } else { result->modifier(token1); result->type(token2); } if (lex < kwd_and >()) { lex(false); } else { return result; } } } sass::vector queries; do { lex(false); if (lex>()) { // In dart sass parser returns a pure string if (lex < skip_over_scopes < exactly < '(' >, exactly < ')' > > >()) { sass::string decl("(" + sass::string(lexed)); queries.push_back(decl); } // Should be: parseDeclarationValue; if (!lex>()) { // Should we throw an error here? } } } while (lex < kwd_and >()); result->features(queries); if (result->features().empty()) { if (result->type().empty()) { return {}; } } return result; } // EO parse_while_directive MediaRule_Obj Parser::parseMediaRule() { MediaRule_Obj rule = SASS_MEMORY_NEW(MediaRule, pstate); stack.push_back(Scope::Media); rule->schema(parse_media_queries()); parse_block_comments(false); rule->block(parse_css_block()); stack.pop_back(); return rule; } List_Obj Parser::parse_media_queries() { advanceToNextToken(); List_Obj queries = SASS_MEMORY_NEW(List, pstate, 0, SASS_COMMA); if (!peek_css < exactly <'{'> >()) queries->append(parse_media_query()); while (lex_css < exactly <','> >()) queries->append(parse_media_query()); queries->update_pstate(pstate); return queries.detach(); } // Expression* Parser::parse_media_query() Media_Query_Obj Parser::parse_media_query() { advanceToNextToken(); Media_Query_Obj media_query = SASS_MEMORY_NEW(Media_Query, pstate); if (lex < kwd_not >()) { media_query->is_negated(true); lex < css_comments >(false); } else if (lex < kwd_only >()) { media_query->is_restricted(true); lex < css_comments >(false); } if (lex < identifier_schema >()) media_query->media_type(parse_identifier_schema()); else if (lex < identifier >()) media_query->media_type(parse_interpolated_chunk(lexed)); else media_query->append(parse_media_expression()); while (lex_css < kwd_and >()) media_query->append(parse_media_expression()); if (lex < identifier_schema >()) { String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); if (media_query->media_type()) { schema->append(media_query->media_type()); schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " ")); } schema->append(parse_identifier_schema()); media_query->media_type(schema); } while (lex_css < kwd_and >()) media_query->append(parse_media_expression()); media_query->update_pstate(pstate); return media_query; } Media_Query_ExpressionObj Parser::parse_media_expression() { if (lex < identifier_schema >()) { String_Obj ss = parse_identifier_schema(); return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, ExpressionObj{}, true); } if (!lex_css< exactly<'('> >()) { error("media query expression must begin with '('"); } ExpressionObj feature; if (peek_css< exactly<')'> >()) { error("media feature required in media query expression"); } feature = parse_expression(); ExpressionObj expression; if (lex_css< exactly<':'> >()) { expression = parse_list(DELAYED); } if (!lex_css< exactly<')'> >()) { error("unclosed parenthesis in media query expression"); } return SASS_MEMORY_NEW(Media_Query_Expression, feature->pstate(), feature, expression); } // lexed after `kwd_supports_directive` // these are very similar to media blocks SupportsRuleObj Parser::parse_supports_directive() { SupportsConditionObj cond = parse_supports_condition(/*top_level=*/true); // create the ast node object for the support queries SupportsRuleObj query = SASS_MEMORY_NEW(SupportsRule, pstate, cond); // additional block is mandatory // parse inner block query->block(parse_block()); // return ast node return query; } // parse one query operation // may encounter nested queries SupportsConditionObj Parser::parse_supports_condition(bool top_level) { lex < css_whitespace >(); SupportsConditionObj cond; if ((cond = parse_supports_negation())) return cond; if ((cond = parse_supports_operator(top_level))) return cond; if ((cond = parse_supports_interpolation())) return cond; return cond; } SupportsConditionObj Parser::parse_supports_negation() { if (!lex < kwd_not >()) return {}; SupportsConditionObj cond = parse_supports_condition_in_parens(/*parens_required=*/true); return SASS_MEMORY_NEW(SupportsNegation, pstate, cond); } SupportsConditionObj Parser::parse_supports_operator(bool top_level) { SupportsConditionObj cond = parse_supports_condition_in_parens(/*parens_required=*/top_level); if (cond.isNull()) return {}; while (true) { SupportsOperation::Operand op = SupportsOperation::OR; if (lex < kwd_and >()) { op = SupportsOperation::AND; } else if(!lex < kwd_or >()) { break; } lex < css_whitespace >(); SupportsConditionObj right = parse_supports_condition_in_parens(/*parens_required=*/true); // SupportsCondition* cc = SASS_MEMORY_NEW(SupportsCondition, *static_cast(cond)); cond = SASS_MEMORY_NEW(SupportsOperation, pstate, cond, right, op); } return cond; } SupportsConditionObj Parser::parse_supports_interpolation() { if (!lex < interpolant >()) return {}; String_Obj interp = parse_interpolated_chunk(lexed); if (!interp) return {}; return SASS_MEMORY_NEW(Supports_Interpolation, pstate, interp); } // TODO: This needs some major work. Although feature conditions // look like declarations their semantics differ significantly SupportsConditionObj Parser::parse_supports_declaration() { SupportsCondition* cond; // parse something declaration like ExpressionObj feature = parse_expression(); ExpressionObj expression; if (lex_css< exactly<':'> >()) { expression = parse_list(DELAYED); } if (!feature || !expression) error("@supports condition expected declaration"); cond = SASS_MEMORY_NEW(SupportsDeclaration, feature->pstate(), feature, expression); // ToDo: maybe we need an additional error condition? return cond; } SupportsConditionObj Parser::parse_supports_condition_in_parens(bool parens_required) { SupportsConditionObj interp = parse_supports_interpolation(); if (interp != nullptr) return interp; if (!lex < exactly <'('> >()) { if (parens_required) { css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", /*trim=*/false); } else { return {}; } } lex < css_whitespace >(); SupportsConditionObj cond = parse_supports_condition(/*top_level=*/false); if (cond.isNull()) cond = parse_supports_declaration(); if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration"); lex < css_whitespace >(); return cond; } AtRootRuleObj Parser::parse_at_root_block() { stack.push_back(Scope::AtRoot); SourceSpan at_source_position = pstate; Block_Obj body; At_Root_Query_Obj expr; Lookahead lookahead_result; if (lex_css< exactly<'('> >()) { expr = parse_at_root_query(); } if (peek_css < exactly<'{'> >()) { lex (); body = parse_block(true); } else if ((lookahead_result = lookahead_for_selector(position)).found) { StyleRuleObj r = parse_ruleset(lookahead_result); body = SASS_MEMORY_NEW(Block, r->pstate(), 1, true); body->append(r); } AtRootRuleObj at_root = SASS_MEMORY_NEW(AtRootRule, at_source_position, body); if (!expr.isNull()) at_root->expression(expr); stack.pop_back(); return at_root; } At_Root_Query_Obj Parser::parse_at_root_query() { if (peek< exactly<')'> >()) error("at-root feature required in at-root expression"); if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) { css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was "); } ExpressionObj feature = parse_list(); if (!lex_css< exactly<':'> >()) error("style declaration must contain a value"); ExpressionObj expression = parse_list(); List_Obj value = SASS_MEMORY_NEW(List, feature->pstate(), 1); if (expression->concrete_type() == Expression::LIST) { value = Cast(expression); } else value->append(expression); At_Root_Query_Obj cond = SASS_MEMORY_NEW(At_Root_Query, value->pstate(), feature, value); if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression"); return cond; } AtRuleObj Parser::parse_directive() { AtRuleObj directive = SASS_MEMORY_NEW(AtRule, pstate, lexed); String_Schema_Obj val = parse_almost_any_value(); // strip left and right if they are of type string directive->value(val); if (peek< exactly<'{'> >()) { directive->block(parse_block()); } return directive; } ExpressionObj Parser::lex_interpolation() { if (lex < interpolant >(true) != NULL) { return parse_interpolated_chunk(lexed, true); } return {}; } ExpressionObj Parser::lex_interp_uri() { // create a string schema by lexing optional interpolations return lex_interp< re_string_uri_open, re_string_uri_close >(); } ExpressionObj Parser::lex_interp_string() { ExpressionObj rv; if ((rv = lex_interp< re_string_double_open, re_string_double_close >())) return rv; if ((rv = lex_interp< re_string_single_open, re_string_single_close >())) return rv; return rv; } ExpressionObj Parser::lex_almost_any_value_chars() { const char* match = lex < one_plus < alternatives < exactly <'>'>, sequence < exactly <'\\'>, any_char >, sequence < negate < sequence < exactly < url_kwd >, exactly <'('> > >, neg_class_char < almost_any_value_class > >, sequence < exactly <'/'>, negate < alternatives < exactly <'/'>, exactly <'*'> > > >, sequence < exactly <'\\'>, exactly <'#'>, negate < exactly <'{'> > >, sequence < exactly <'!'>, negate < alpha > > > > >(false); if (match) { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); } return {}; } ExpressionObj Parser::lex_almost_any_value_token() { ExpressionObj rv; if (*position == 0) return {}; if ((rv = lex_almost_any_value_chars())) return rv; // if ((rv = lex_block_comment())) return rv; // if ((rv = lex_single_line_comment())) return rv; if ((rv = lex_interp_string())) return rv; if ((rv = lex_interp_uri())) return rv; if ((rv = lex_interpolation())) return rv; if (lex< alternatives< hex, hex0 > >()) { return lexed_hex_color(lexed); } return rv; } String_Schema_Obj Parser::parse_almost_any_value() { String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); if (*position == 0) return {}; lex < spaces >(false); ExpressionObj token = lex_almost_any_value_token(); if (!token) return {}; schema->append(token); if (*position == 0) { schema->rtrim(); return schema.detach(); } while ((token = lex_almost_any_value_token())) { schema->append(token); } lex < css_whitespace >(); schema->rtrim(); return schema.detach(); } WarningRuleObj Parser::parse_warning() { if (stack.back() != Scope::Root && stack.back() != Scope::Function && stack.back() != Scope::Mixin && stack.back() != Scope::Control && stack.back() != Scope::Rules) { error("Illegal nesting: Only properties may be nested beneath properties."); } return SASS_MEMORY_NEW(WarningRule, pstate, parse_list(DELAYED)); } ErrorRuleObj Parser::parse_error() { if (stack.back() != Scope::Root && stack.back() != Scope::Function && stack.back() != Scope::Mixin && stack.back() != Scope::Control && stack.back() != Scope::Rules) { error("Illegal nesting: Only properties may be nested beneath properties."); } return SASS_MEMORY_NEW(ErrorRule, pstate, parse_list(DELAYED)); } DebugRuleObj Parser::parse_debug() { if (stack.back() != Scope::Root && stack.back() != Scope::Function && stack.back() != Scope::Mixin && stack.back() != Scope::Control && stack.back() != Scope::Rules) { error("Illegal nesting: Only properties may be nested beneath properties."); } return SASS_MEMORY_NEW(DebugRule, pstate, parse_list(DELAYED)); } Return_Obj Parser::parse_return_directive() { // check that we do not have an empty list (ToDo: check if we got all cases) if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } return SASS_MEMORY_NEW(Return, pstate, parse_list()); } Lookahead Parser::lookahead_for_selector(const char* start) { // init result struct Lookahead rv = Lookahead(); // get start position const char* p = start ? start : position; // match in one big "regex" rv.error = p; if (const char* q = peek < re_selector_list >(p) ) { bool could_be_property = peek< sequence< exactly<'-'>, exactly<'-'> > >(p) != 0; bool could_be_escaped = false; while (p < q) { // did we have interpolations? if (*p == '#' && *(p+1) == '{') { rv.has_interpolants = true; p = q; break; } // A property that's ambiguous with a nested selector is interpreted as a // custom property. if (*p == ':' && !could_be_escaped) { rv.is_custom_property = could_be_property || p+1 == q || peek< space >(p+1); } could_be_escaped = *p == '\\'; ++ p; } // store anyway } // ToDo: remove rv.error = q; rv.position = q; // check expected opening bracket // only after successful matching if (peek < exactly<'{'> >(q)) rv.found = q; // else if (peek < end_of_file >(q)) rv.found = q; else if (peek < exactly<'('> >(q)) rv.found = q; // else if (peek < exactly<';'> >(q)) rv.found = q; // else if (peek < exactly<'}'> >(q)) rv.found = q; if (rv.found || *p == 0) rv.error = 0; } rv.parsable = ! rv.has_interpolants; // return result return rv; } // EO lookahead_for_selector // used in parse_block_nodes and parse_special_directive // ToDo: actual usage is still not really clear to me? Lookahead Parser::lookahead_for_include(const char* start) { // we actually just lookahead for a selector Lookahead rv = lookahead_for_selector(start); // but the "found" rules are different if (const char* p = rv.position) { // check for additional abort condition if (peek < exactly<';'> >(p)) rv.found = p; else if (peek < exactly<'}'> >(p)) rv.found = p; } // return result return rv; } // EO lookahead_for_include // look ahead for a token with interpolation in it // we mostly use the result if there is an interpolation // everything that passes here gets parsed as one schema // meaning it will not be parsed as a space separated list Lookahead Parser::lookahead_for_value(const char* start) { // init result struct Lookahead rv = Lookahead(); // get start position const char* p = start ? start : position; // match in one big "regex" if (const char* q = peek < non_greedy < alternatives < // consume whitespace block_comment, // spaces, // main tokens sequence < interpolant, optional < quoted_string > >, identifier, variable, // issue #442 sequence < parenthese_scope, interpolant, optional < quoted_string > > >, sequence < // optional_spaces, alternatives < // end_of_file, exactly<'{'>, exactly<'}'>, exactly<';'> > > > >(p) ) { if (p == q) return rv; while (p < q) { // did we have interpolations? if (*p == '#' && *(p+1) == '{') { rv.has_interpolants = true; p = q; break; } ++ p; } // store anyway // ToDo: remove rv.position = q; // check expected opening bracket // only after successful matching if (peek < exactly<'{'> >(q)) rv.found = q; else if (peek < exactly<';'> >(q)) rv.found = q; else if (peek < exactly<'}'> >(q)) rv.found = q; } // return result return rv; } // EO lookahead_for_value void Parser::read_bom() { size_t skip = 0; sass::string encoding; bool utf_8 = false; switch ((unsigned char)position[0]) { case 0xEF: skip = check_bom_chars(position, end, utf_8_bom, 3); encoding = "UTF-8"; utf_8 = true; break; case 0xFE: skip = check_bom_chars(position, end, utf_16_bom_be, 2); encoding = "UTF-16 (big endian)"; break; case 0xFF: skip = check_bom_chars(position, end, utf_16_bom_le, 2); skip += (skip ? check_bom_chars(position, end, utf_32_bom_le, 4) : 0); encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)"); break; case 0x00: skip = check_bom_chars(position, end, utf_32_bom_be, 4); encoding = "UTF-32 (big endian)"; break; case 0x2B: skip = check_bom_chars(position, end, utf_7_bom_1, 4) | check_bom_chars(position, end, utf_7_bom_2, 4) | check_bom_chars(position, end, utf_7_bom_3, 4) | check_bom_chars(position, end, utf_7_bom_4, 4) | check_bom_chars(position, end, utf_7_bom_5, 5); encoding = "UTF-7"; break; case 0xF7: skip = check_bom_chars(position, end, utf_1_bom, 3); encoding = "UTF-1"; break; case 0xDD: skip = check_bom_chars(position, end, utf_ebcdic_bom, 4); encoding = "UTF-EBCDIC"; break; case 0x0E: skip = check_bom_chars(position, end, scsu_bom, 3); encoding = "SCSU"; break; case 0xFB: skip = check_bom_chars(position, end, bocu_1_bom, 3); encoding = "BOCU-1"; break; case 0x84: skip = check_bom_chars(position, end, gb_18030_bom, 4); encoding = "GB-18030"; break; default: break; } if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding); position += skip; } size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len) { size_t skip = 0; if (src + len > end) return 0; for (size_t i = 0; i < len; ++i, ++skip) { if ((unsigned char) src[i] != bom[i]) return 0; } return skip; } ExpressionObj Parser::fold_operands(ExpressionObj base, sass::vector& operands, Operand op) { for (size_t i = 0, S = operands.size(); i < S; ++i) { base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), op, base, operands[i]); } return base; } ExpressionObj Parser::fold_operands(ExpressionObj base, sass::vector& operands, sass::vector& ops, size_t i) { if (String_Schema* schema = Cast(base)) { // return schema; if (schema->has_interpolants()) { if (i + 1 < operands.size() && ( (ops[0].operand == Sass_OP::EQ) || (ops[0].operand == Sass_OP::ADD) || (ops[0].operand == Sass_OP::DIV) || (ops[0].operand == Sass_OP::MUL) || (ops[0].operand == Sass_OP::NEQ) || (ops[0].operand == Sass_OP::LT) || (ops[0].operand == Sass_OP::GT) || (ops[0].operand == Sass_OP::LTE) || (ops[0].operand == Sass_OP::GTE) )) { ExpressionObj rhs = fold_operands(operands[i], operands, ops, i + 1); rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[0], schema, rhs); return rhs; } // return schema; } } if (operands.size() > Constants::MaxCallStack) { // XXX: this is never hit via spec tests sass::ostream stm; stm << "Stack depth exceeded max of " << Constants::MaxCallStack; error(stm.str()); } for (size_t S = operands.size(); i < S; ++i) { if (String_Schema* schema = Cast(operands[i])) { if (schema->has_interpolants()) { if (i + 1 < S) { // this whole branch is never hit via spec tests ExpressionObj rhs = fold_operands(operands[i+1], operands, ops, i + 2); rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], schema, rhs); base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, rhs); return base; } base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]); return base; } else { base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]); } } else { base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]); } Binary_Expression* b = Cast(base.ptr()); if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); } } // nested binary expression are never to be delayed if (Binary_Expression* b = Cast(base)) { if (Cast(b->left())) base->set_delayed(false); if (Cast(b->right())) base->set_delayed(false); } return base; } void Parser::error(sass::string msg) { traces.push_back(Backtrace(pstate)); throw Exception::InvalidSass(pstate, traces, msg); } // print a css parsing error with actual context information from parsed source void Parser::css_error(const sass::string& msg, const sass::string& prefix, const sass::string& middle, const bool trim) { int max_len = 18; const char* end = this->end; while (*end != 0) ++ end; const char* pos = peek < optional_spaces >(); if (!pos) pos = position; const char* last_pos(pos); if (last_pos > begin) { utf8::prior(last_pos, begin); } // backup position to last significant char while (trim && last_pos > begin&& last_pos < end) { if (!Util::ascii_isspace(static_cast(*last_pos))) break; utf8::prior(last_pos, begin); } bool ellipsis_left = false; const char* pos_left(last_pos); const char* end_left(last_pos); if (*pos_left) utf8::next(pos_left, end); if (*end_left) utf8::next(end_left, end); while (pos_left > begin) { if (utf8::distance(pos_left, end_left) >= max_len) { utf8::prior(pos_left, begin); ellipsis_left = *(pos_left) != '\n' && *(pos_left) != '\r'; utf8::next(pos_left, end); break; } const char* prev = pos_left; utf8::prior(prev, begin); if (*prev == '\r') break; if (*prev == '\n') break; pos_left = prev; } if (pos_left < begin) { pos_left = begin; } bool ellipsis_right = false; const char* end_right(pos); const char* pos_right(pos); while (end_right < end) { if (utf8::distance(pos_right, end_right) > max_len) { ellipsis_left = *(pos_right) != '\n' && *(pos_right) != '\r'; break; } if (*end_right == '\r') break; if (*end_right == '\n') break; utf8::next(end_right, end); } // if (*end_right == 0) end_right ++; sass::string left(pos_left, end_left); sass::string right(pos_right, end_right); size_t left_subpos = left.size() > 15 ? left.size() - 15 : 0; size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0; if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos); if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis; // now pass new message to the more generic error function error(msg + prefix + quote(left) + middle + quote(right)); } } golibsass-1.0.0/libsass_src/src/parser.hpp000066400000000000000000000330331405214413600206030ustar00rootroot00000000000000#ifndef SASS_PARSER_H #define SASS_PARSER_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "ast.hpp" #include "position.hpp" #include "context.hpp" #include "position.hpp" #include "prelexer.hpp" #include "source.hpp" #ifndef MAX_NESTING // Note that this limit is not an exact science // it depends on various factors, which some are // not under our control (compile time or even OS // dependent settings on the available stack size) // It should fix most common segfault cases though. #define MAX_NESTING 512 #endif struct Lookahead { const char* found; const char* error; const char* position; bool parsable; bool has_interpolants; bool is_custom_property; }; namespace Sass { class Parser : public SourceSpan { public: enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules, AtRoot }; Context& ctx; sass::vector block_stack; sass::vector stack; SourceDataObj source; const char* begin; const char* position; const char* end; Offset before_token; Offset after_token; SourceSpan pstate; Backtraces traces; size_t indentation; size_t nestings; bool allow_parent; Token lexed; Parser(SourceData* source, Context& ctx, Backtraces, bool allow_parent = true); // special static parsers to convert strings into certain selectors static SelectorListObj parse_selector(SourceData* source, Context& ctx, Backtraces, bool allow_parent = true); #ifdef __clang__ // lex and peak uses the template parameter to branch on the action, which // triggers clangs tautological comparison on the single-comparison // branches. This is not a bug, just a merging of behaviour into // one function #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-compare" #endif // skip current token and next whitespace // moves SourceSpan right before next token void advanceToNextToken(); bool peek_newline(const char* start = 0); // skip over spaces, tabs and line comments template const char* sneak(const char* start = 0) { using namespace Prelexer; // maybe use optional start position from arguments? const char* it_position = start ? start : position; // skip white-space? if (mx == spaces || mx == no_spaces || mx == css_comments || mx == css_whitespace || mx == optional_spaces || mx == optional_css_comments || mx == optional_css_whitespace ) { return it_position; } // skip over spaces, tabs and sass line comments const char* pos = optional_css_whitespace(it_position); // always return a valid position return pos ? pos : it_position; } // match will not skip over space, tabs and line comment // return the position where the lexer match will occur template const char* match(const char* start = 0) { // match the given prelexer return mx(position); } // peek will only skip over space, tabs and line comment // return the position where the lexer match will occur template const char* peek(const char* start = 0) { // sneak up to the actual token we want to lex // this should skip over white-space if desired const char* it_before_token = sneak < mx >(start); // match the given prelexer const char* match = mx(it_before_token); // check if match is in valid range return match <= end ? match : 0; } // white-space handling is built into the lexer // this way you do not need to parse it yourself // some matchers don't accept certain white-space // we do not support start arg, since we manipulate // sourcemap offset and we modify the position pointer! // lex will only skip over space, tabs and line comment template const char* lex(bool lazy = true, bool force = false) { if (*position == 0) return 0; // position considered before lexed token // we can skip whitespace or comments for // lazy developers (but we need control) const char* it_before_token = position; // sneak up to the actual token we want to lex // this should skip over white-space if desired if (lazy) it_before_token = sneak < mx >(position); // now call matcher to get position after token const char* it_after_token = mx(it_before_token); // check if match is in valid range if (it_after_token > end) return 0; // maybe we want to update the parser state anyway? if (force == false) { // assertion that we got a valid match if (it_after_token == 0) return 0; // assertion that we actually lexed something if (it_after_token == it_before_token) return 0; } // create new lexed token object (holds the parse results) lexed = Token(position, it_before_token, it_after_token); // advance position (add whitespace before current token) before_token = after_token.add(position, it_before_token); // update after_token position for current token after_token.add(it_before_token, it_after_token); // ToDo: could probably do this incremental on original object (API wants offset?) pstate = SourceSpan(source, before_token, after_token - before_token); // advance internal char iterator return position = it_after_token; } // lex_css skips over space, tabs, line and block comment // all block comments will be consumed and thrown away // source-map position will point to token after the comment template const char* lex_css() { // copy old token Token prev = lexed; // store previous pointer const char* oldpos = position; Offset bt = before_token; Offset at = after_token; SourceSpan op = pstate; // throw away comments // update srcmap position lex < Prelexer::css_comments >(); // now lex a new token const char* pos = lex< mx >(); // maybe restore prev state if (pos == 0) { pstate = op; lexed = prev; position = oldpos; after_token = at; before_token = bt; } // return match return pos; } // all block comments will be skipped and thrown away template const char* peek_css(const char* start = 0) { // now peek a token (skip comments first) return peek< mx >(peek < Prelexer::css_comments >(start)); } #ifdef __clang__ #pragma clang diagnostic pop #endif void error(sass::string msg); // generate message with given and expected sample // text before and in the middle are configurable void css_error(const sass::string& msg, const sass::string& prefix = " after ", const sass::string& middle = ", was: ", const bool trim = true); void read_bom(); Block_Obj parse(); Import_Obj parse_import(); Definition_Obj parse_definition(Definition::Type which_type); Parameters_Obj parse_parameters(); Parameter_Obj parse_parameter(); Mixin_Call_Obj parse_include_directive(); Arguments_Obj parse_arguments(); Argument_Obj parse_argument(); Assignment_Obj parse_assignment(); StyleRuleObj parse_ruleset(Lookahead lookahead); SelectorListObj parseSelectorList(bool chroot); ComplexSelectorObj parseComplexSelector(bool chroot); Selector_Schema_Obj parse_selector_schema(const char* end_of_selector, bool chroot); CompoundSelectorObj parseCompoundSelector(); SimpleSelectorObj parse_simple_selector(); PseudoSelectorObj parse_negated_selector2(); Expression* parse_binominal(); SimpleSelectorObj parse_pseudo_selector(); AttributeSelectorObj parse_attribute_selector(); Block_Obj parse_block(bool is_root = false); Block_Obj parse_css_block(bool is_root = false); bool parse_block_nodes(bool is_root = false); bool parse_block_node(bool is_root = false); Declaration_Obj parse_declaration(); ExpressionObj parse_map(); ExpressionObj parse_bracket_list(); ExpressionObj parse_list(bool delayed = false); ExpressionObj parse_comma_list(bool delayed = false); ExpressionObj parse_space_list(); ExpressionObj parse_disjunction(); ExpressionObj parse_conjunction(); ExpressionObj parse_relation(); ExpressionObj parse_expression(); ExpressionObj parse_operators(); ExpressionObj parse_factor(); ExpressionObj parse_value(); Function_Call_Obj parse_calc_function(); Function_Call_Obj parse_function_call(); Function_Call_Obj parse_function_call_schema(); String_Obj parse_url_function_string(); String_Obj parse_url_function_argument(); String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true); String_Obj parse_string(); ValueObj parse_static_value(); String_Schema_Obj parse_css_variable_value(); String_Obj parse_ie_property(); String_Obj parse_ie_keyword_arg(); String_Schema_Obj parse_value_schema(const char* stop); String_Obj parse_identifier_schema(); If_Obj parse_if_directive(bool else_if = false); ForRuleObj parse_for_directive(); EachRuleObj parse_each_directive(); WhileRuleObj parse_while_directive(); MediaRule_Obj parseMediaRule(); sass::vector parseCssMediaQueries(); sass::string parseIdentifier(); CssMediaQuery_Obj parseCssMediaQuery(); Return_Obj parse_return_directive(); Content_Obj parse_content_directive(); void parse_charset_directive(); List_Obj parse_media_queries(); Media_Query_Obj parse_media_query(); Media_Query_ExpressionObj parse_media_expression(); SupportsRuleObj parse_supports_directive(); SupportsConditionObj parse_supports_condition(bool top_level); SupportsConditionObj parse_supports_negation(); SupportsConditionObj parse_supports_operator(bool top_level); SupportsConditionObj parse_supports_interpolation(); SupportsConditionObj parse_supports_declaration(); SupportsConditionObj parse_supports_condition_in_parens(bool parens_required); AtRootRuleObj parse_at_root_block(); At_Root_Query_Obj parse_at_root_query(); String_Schema_Obj parse_almost_any_value(); AtRuleObj parse_directive(); WarningRuleObj parse_warning(); ErrorRuleObj parse_error(); DebugRuleObj parse_debug(); Value* color_or_string(const sass::string& lexed) const; // be more like ruby sass ExpressionObj lex_almost_any_value_token(); ExpressionObj lex_almost_any_value_chars(); ExpressionObj lex_interp_string(); ExpressionObj lex_interp_uri(); ExpressionObj lex_interpolation(); // these will throw errors Token lex_variable(); Token lex_identifier(); void parse_block_comments(bool store = true); Lookahead lookahead_for_value(const char* start = 0); Lookahead lookahead_for_selector(const char* start = 0); Lookahead lookahead_for_include(const char* start = 0); ExpressionObj fold_operands(ExpressionObj base, sass::vector& operands, Operand op); ExpressionObj fold_operands(ExpressionObj base, sass::vector& operands, sass::vector& ops, size_t i = 0); void throw_syntax_error(sass::string message, size_t ln = 0); void throw_read_error(sass::string message, size_t ln = 0); template ExpressionObj lex_interp() { if (lex < open >(false)) { String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); // std::cerr << "LEX [[" << sass::string(lexed) << "]]\n"; schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); if (position[0] == '#' && position[1] == '{') { ExpressionObj itpl = lex_interpolation(); if (!itpl.isNull()) schema->append(itpl); while (lex < close >(false)) { // std::cerr << "LEX [[" << sass::string(lexed) << "]]\n"; schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); if (position[0] == '#' && position[1] == '{') { ExpressionObj itpl = lex_interpolation(); if (!itpl.isNull()) schema->append(itpl); } else { return schema; } } } else { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); } } return {}; } public: static Number* lexed_number(const SourceSpan& pstate, const sass::string& parsed); static Number* lexed_dimension(const SourceSpan& pstate, const sass::string& parsed); static Number* lexed_percentage(const SourceSpan& pstate, const sass::string& parsed); static Value* lexed_hex_color(const SourceSpan& pstate, const sass::string& parsed); private: Number* lexed_number(const sass::string& parsed) { return lexed_number(pstate, parsed); }; Number* lexed_dimension(const sass::string& parsed) { return lexed_dimension(pstate, parsed); }; Number* lexed_percentage(const sass::string& parsed) { return lexed_percentage(pstate, parsed); }; Value* lexed_hex_color(const sass::string& parsed) { return lexed_hex_color(pstate, parsed); }; static const char* re_attr_sensitive_close(const char* src); static const char* re_attr_insensitive_close(const char* src); }; size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len); } #endif golibsass-1.0.0/libsass_src/src/parser_selectors.cpp000066400000000000000000000130601405214413600226570ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "parser.hpp" namespace Sass { using namespace Prelexer; using namespace Constants; ComplexSelectorObj Parser::parseComplexSelector(bool chroot) { NESTING_GUARD(nestings); lex < block_comment >(); advanceToNextToken(); ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate); if (peek < end_of_file >()) return sel; while (true) { lex < block_comment >(); advanceToNextToken(); // check for child (+) combinator if (lex < exactly < selector_combinator_child > >()) { sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::CHILD, peek_newline())); } // check for general sibling (~) combinator else if (lex < exactly < selector_combinator_general > >()) { sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::GENERAL, peek_newline())); } // check for adjecant sibling (+) combinator else if (lex < exactly < selector_combinator_adjacent > >()) { sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::ADJACENT, peek_newline())); } // check if we can parse a compound selector else if (CompoundSelectorObj compound = parseCompoundSelector()) { sel->append(compound); } else { break; } } if (sel->empty()) return {}; // check if we parsed any parent references sel->chroots(sel->has_real_parent_ref() || chroot); sel->update_pstate(pstate); return sel; } SelectorListObj Parser::parseSelectorList(bool chroot) { bool reloop; bool had_linefeed = false; NESTING_GUARD(nestings); SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate); if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) { css_error("Invalid CSS", " after ", ": expected selector, was "); } do { reloop = false; had_linefeed = had_linefeed || peek_newline(); if (peek_css< alternatives < class_char < selector_list_delims > > >()) break; // in case there are superfluous commas at the end // now parse the complex selector ComplexSelectorObj complex = parseComplexSelector(chroot); if (complex.isNull()) return list.detach(); complex->hasPreLineFeed(had_linefeed); had_linefeed = false; while (peek_css< exactly<','> >()) { lex< css_comments >(false); // consume everything up and including the comma separator reloop = lex< exactly<','> >() != 0; // remember line break (also between some commas) had_linefeed = had_linefeed || peek_newline(); // remember line break (also between some commas) } list->append(complex); } while (reloop); while (lex_css< kwd_optional >()) { list->is_optional(true); } // update for end position list->update_pstate(pstate); return list.detach(); } // parse one compound selector, which is basically // a list of simple selectors (directly adjacent) // lex them exactly (without skipping white-space) CompoundSelectorObj Parser::parseCompoundSelector() { // init an empty compound selector wrapper CompoundSelectorObj seq = SASS_MEMORY_NEW(CompoundSelector, pstate); // skip initial white-space lex < block_comment >(); advanceToNextToken(); if (lex< exactly<'&'> >(false)) { // ToDo: check the conditions and try to simplify flag passing if (!allow_parent) error("Parent selectors aren't allowed here."); // Create and append a new parent selector object seq->hasRealParent(true); } // parse list while (true) { // remove all block comments // leaves trailing white-space lex < block_comment >(); // parse parent selector if (lex< exactly<'&'> >(false)) { // parent selector only allowed at start // upcoming Sass may allow also trailing SourceSpan state(pstate); sass::string found("&"); if (lex < identifier >()) { found += sass::string(lexed); } sass::string sel(seq->hasRealParent() ? "&" : ""); if (!seq->empty()) { sel = seq->last()->to_string({ NESTED, 5 }); } // ToDo: parser should throw parser exceptions error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n" "\"" + found + "\" may only be used at the beginning of a compound selector."); } // parse functional else if (match < re_functional >()) { seq->append(parse_simple_selector()); } // parse type selector else if (lex< re_type_selector >(false)) { seq->append(SASS_MEMORY_NEW(TypeSelector, pstate, lexed)); } // peek for abort conditions else if (peek< spaces >()) break; else if (peek< end_of_file >()) { break; } else if (peek_css < class_char < selector_combinator_ops > >()) break; else if (peek_css < class_char < complex_selector_delims > >()) break; // otherwise parse another simple selector else { SimpleSelectorObj sel = parse_simple_selector(); if (!sel) return {}; seq->append(sel); } } // EO while true if (seq && !peek_css>>()) { seq->hasPostLineBreak(peek_newline()); } // We may have set hasRealParent if (seq && seq->empty() && !seq->hasRealParent()) return {}; return seq; } } golibsass-1.0.0/libsass_src/src/permutate.hpp000066400000000000000000000071361405214413600213220ustar00rootroot00000000000000#ifndef SASS_PATHS_H #define SASS_PATHS_H #include namespace Sass { // Returns a list of all possible paths through the given lists. // // For example, given `[[1, 2], [3, 4], [5, 6]]`, this returns: // // ``` // [[1, 3, 5], // [2, 3, 5], // [1, 4, 5], // [2, 4, 5], // [1, 3, 6], // [2, 3, 6], // [1, 4, 6], // [2, 4, 6]] // ``` // // Note: called `paths` in dart-sass template sass::vector> permutate( const sass::vector>& in) { size_t L = in.size(), n = 0; if (L == 0) return {}; // Exit early if any entry is empty for (size_t i = 0; i < L; i += 1) { if (in[i].size() == 0) return {}; } size_t* state = new size_t[L + 1]; sass::vector> out; // First initialize all states for every permutation group for (size_t i = 0; i < L; i += 1) { state[i] = in[i].size() - 1; } while (true) { sass::vector perm; // Create one permutation for state for (size_t i = 0; i < L; i += 1) { perm.push_back(in.at(i).at(in[i].size() - state[i] - 1)); } // Current group finished if (state[n] == 0) { // Find position of next decrement while (n < L && state[++n] == 0) {} if (n == L) { out.push_back(perm); break; } state[n] -= 1; for (size_t p = 0; p < n; p += 1) { state[p] = in[p].size() - 1; } // Restart from front n = 0; } else { state[n] -= 1; } out.push_back(perm); } delete[] state; return out; } // EO permutate // ToDo: this variant is used in resolveParentSelectors // Returns a list of all possible paths through the given lists. // // For example, given `[[1, 2], [3, 4], [5, 6]]`, this returns: // // ``` // [[1, 3, 5], // [1, 3, 6], // [1, 4, 5], // [1, 4, 6], // [2, 3, 5], // [2, 3, 6], // [2, 4, 5], // [2, 4, 6]] // ``` // template sass::vector> permutateAlt(const sass::vector>& in) { size_t L = in.size(); size_t n = in.size() - 1; if (L == 0) return {}; // Exit early if any entry is empty for (size_t i = 0; i < L; i += 1) { if (in[i].size() == 0) return {}; } size_t* state = new size_t[L]; sass::vector> out; // First initialize all states for every permutation group for (size_t i = 0; i < L; i += 1) { state[i] = in[i].size() - 1; } while (true) { /* // std::cerr << "PERM: "; for (size_t p = 0; p < L; p++) { // std::cerr << state[p] << " "; } // std::cerr << "\n"; */ sass::vector perm; // Create one permutation for state for (size_t i = 0; i < L; i += 1) { perm.push_back(in.at(i).at(in[i].size() - state[i] - 1)); } // Current group finished if (state[n] == 0) { // Find position of next decrement while (n > 0 && state[--n] == 0) {} // Check for end condition if (state[n] != 0) { // Decrease next on the left side state[n] -= 1; // Reset all counters to the right for (size_t p = n + 1; p < L; p += 1) { state[p] = in[p].size() - 1; } // Restart from end n = L - 1; } else { out.push_back(perm); break; } } else { state[n] -= 1; } out.push_back(perm); } delete[] state; return out; } // EO permutateAlt } #endif golibsass-1.0.0/libsass_src/src/plugins.cpp000066400000000000000000000142221405214413600207620ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include "output.hpp" #include "plugins.hpp" #include "util.hpp" #ifdef _WIN32 #include #else #include #include #include #include #endif namespace Sass { Plugins::Plugins(void) { } Plugins::~Plugins(void) { for (auto function : functions) { sass_delete_function(function); } for (auto importer : importers) { sass_delete_importer(importer); } for (auto header : headers) { sass_delete_importer(header); } } // check if plugin is compatible with this version // plugins may be linked static against libsass // we try to be compatible between major versions inline bool compatibility(const char* their_version) { // const char* their_version = "3.1.2"; // first check if anyone has an unknown version const char* our_version = libsass_version(); if (!strcmp(their_version, "[na]")) return false; if (!strcmp(our_version, "[na]")) return false; // find the position of the second dot size_t pos = sass::string(our_version).find('.', 0); if (pos != sass::string::npos) pos = sass::string(our_version).find('.', pos + 1); // if we do not have two dots we fallback to compare complete string if (pos == sass::string::npos) { return strcmp(their_version, our_version) ? 0 : 1; } // otherwise only compare up to the second dot (major versions) else { return strncmp(their_version, our_version, pos) ? 0 : 1; } } // load one specific plugin bool Plugins::load_plugin (const sass::string& path) { typedef const char* (*__plugin_version__)(void); typedef Sass_Function_List (*__plugin_load_fns__)(void); typedef Sass_Importer_List (*__plugin_load_imps__)(void); if (LOAD_LIB(plugin, path)) { // try to load initial function to query libsass version suppor if (LOAD_LIB_FN(__plugin_version__, plugin_version, "libsass_get_version")) { // get the libsass version of the plugin if (!compatibility(plugin_version())) return false; // try to get import address for "libsass_load_functions" if (LOAD_LIB_FN(__plugin_load_fns__, plugin_load_functions, "libsass_load_functions")) { Sass_Function_List fns = plugin_load_functions(), _p = fns; while (fns && *fns) { functions.push_back(*fns); ++ fns; } sass_free_memory(_p); // only delete the container, items not yet } // try to get import address for "libsass_load_importers" if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_importers, "libsass_load_importers")) { Sass_Importer_List imps = plugin_load_importers(), _p = imps; while (imps && *imps) { importers.push_back(*imps); ++ imps; } sass_free_memory(_p); // only delete the container, items not yet } // try to get import address for "libsass_load_headers" if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_headers, "libsass_load_headers")) { Sass_Importer_List imps = plugin_load_headers(), _p = imps; while (imps && *imps) { headers.push_back(*imps); ++ imps; } sass_free_memory(_p); // only delete the container, items not yet } // success return true; } else { // print debug message to stderr (should not happen) std::cerr << "failed loading 'libsass_support' in <" << path << ">" << std::endl; if (const char* dlsym_error = dlerror()) std::cerr << dlsym_error << std::endl; CLOSE_LIB(plugin); } } else { // print debug message to stderr (should not happen) std::cerr << "failed loading plugin <" << path << ">" << std::endl; if (const char* dlopen_error = dlerror()) std::cerr << dlopen_error << std::endl; } return false; } size_t Plugins::load_plugins(const sass::string& path) { // count plugins size_t loaded = 0; #ifdef _WIN32 try { // use wchar (utf16) WIN32_FIND_DATAW data; // trailing slash is guaranteed sass::string globsrch(path + "*.dll"); // convert to wide chars (utf16) for system call std::wstring wglobsrch(UTF_8::convert_to_utf16(globsrch)); HANDLE hFile = FindFirstFileW(wglobsrch.c_str(), &data); // check if system called returned a result // ToDo: maybe we should print a debug message if (hFile == INVALID_HANDLE_VALUE) return -1; // read directory while (true) { try { // the system will report the filenames with wide chars (utf16) sass::string entry = UTF_8::convert_from_utf16(data.cFileName); // check if file ending matches exactly if (!ends_with(entry, ".dll")) continue; // load the plugin and increase counter if (load_plugin(path + entry)) ++ loaded; // check if there should be more entries if (GetLastError() == ERROR_NO_MORE_FILES) break; // load next entry (check for return type) if (!FindNextFileW(hFile, &data)) break; } catch (...) { // report the error to the console (should not happen) // seems like we got strange data from the system call? std::cerr << "filename in plugin path has invalid utf8?" << std::endl; } } } catch (utf8::invalid_utf8&) { // report the error to the console (should not happen) // implementors should make sure to provide valid utf8 std::cerr << "plugin path contains invalid utf8" << std::endl; } #else DIR *dp; struct dirent *dirp; if((dp = opendir(path.c_str())) == NULL) return -1; while ((dirp = readdir(dp)) != NULL) { #if __APPLE__ if (!ends_with(dirp->d_name, ".dylib")) continue; #else if (!ends_with(dirp->d_name, ".so")) continue; #endif if (load_plugin(path + dirp->d_name)) ++ loaded; } closedir(dp); #endif return loaded; } } golibsass-1.0.0/libsass_src/src/plugins.hpp000066400000000000000000000027651405214413600210000ustar00rootroot00000000000000#ifndef SASS_PLUGINS_H #define SASS_PLUGINS_H #include #include #include "utf8_string.hpp" #include "sass/functions.h" #ifdef _WIN32 #define LOAD_LIB(var, path) HMODULE var = LoadLibraryW(UTF_8::convert_to_utf16(path).c_str()) #define LOAD_LIB_WCHR(var, path_wide_str) HMODULE var = LoadLibraryW(path_wide_str.c_str()) #define LOAD_LIB_FN(type, var, name) type var = (type) GetProcAddress(plugin, name) #define CLOSE_LIB(var) FreeLibrary(var) #ifndef dlerror #define dlerror() 0 #endif #else #define LOAD_LIB(var, path) void* var = dlopen(path.c_str(), RTLD_LAZY) #define LOAD_LIB_FN(type, var, name) type var = (type) dlsym(plugin, name) #define CLOSE_LIB(var) dlclose(var) #endif namespace Sass { class Plugins { public: // c-tor Plugins(void); ~Plugins(void); public: // methods // load one specific plugin bool load_plugin(const sass::string& path); // load all plugins from a directory size_t load_plugins(const sass::string& path); public: // public accessors const sass::vector get_headers(void) { return headers; } const sass::vector get_importers(void) { return importers; } const sass::vector get_functions(void) { return functions; } private: // private vars sass::vector headers; sass::vector importers; sass::vector functions; }; } #endif golibsass-1.0.0/libsass_src/src/position.cpp000066400000000000000000000105161405214413600211470ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "position.hpp" #include "source.hpp" namespace Sass { Offset::Offset(const char chr) : line(chr == '\n' ? 1 : 0), column(chr == '\n' ? 0 : 1) {} Offset::Offset(const char* string) : line(0), column(0) { *this = inc(string, string + strlen(string)); } Offset::Offset(const sass::string& text) : line(0), column(0) { *this = inc(text.c_str(), text.c_str() + text.size()); } Offset::Offset(const size_t line, const size_t column) : line(line), column(column) { } // init/create instance from const char substring Offset Offset::init(const char* beg, const char* end) { Offset offset(0, 0); if (end == 0) { end = beg + strlen(beg); } offset.add(beg, end); return offset; } // increase offset by given string (mostly called by lexer) // increase line counter and count columns on the last line Offset Offset::add(const char* begin, const char* end) { if (end == 0) return *this; while (begin < end && *begin) { if (*begin == '\n') { ++ line; // start new line column = 0; } else { // do not count any utf8 continuation bytes // https://stackoverflow.com/a/9356203/1550314 // https://en.wikipedia.org/wiki/UTF-8#Description unsigned char chr = *begin; // Ignore all `10xxxxxx` chars // '0xxxxxxx' are ASCII chars // '11xxxxxx' are utf8 starts // 64 => initial utf8 byte // 128 => regular ASCII char if ((chr & 192) != 128) { // regular ASCII char column += 1; } } ++ begin; } return *this; } // increase offset by given string (mostly called by lexer) // increase line counter and count columns on the last line Offset Offset::inc(const char* begin, const char* end) const { Offset offset(line, column); offset.add(begin, end); return offset; } bool Offset::operator== (const Offset &pos) const { return line == pos.line && column == pos.column; } bool Offset::operator!= (const Offset &pos) const { return line != pos.line || column != pos.column; } void Offset::operator+= (const Offset &off) { *this = Offset(line + off.line, off.line > 0 ? off.column : column + off.column); } Offset Offset::operator+ (const Offset &off) const { return Offset(line + off.line, off.line > 0 ? off.column : column + off.column); } Offset Offset::operator- (const Offset &off) const { return Offset(line - off.line, off.line == line ? column - off.column : column); } Position::Position(const size_t file) : Offset(0, 0), file(file) { } Position::Position(const size_t file, const Offset& offset) : Offset(offset), file(file) { } Position::Position(const size_t line, const size_t column) : Offset(line, column), file(-1) { } Position::Position(const size_t file, const size_t line, const size_t column) : Offset(line, column), file(file) { } SourceSpan::SourceSpan(const char* path) : source(SASS_MEMORY_NEW(SynthFile, path)), position(0, 0), offset(0, 0) { } SourceSpan::SourceSpan(SourceDataObj source, const Offset& position, const Offset& offset) : source(source), position(position), offset(offset) { } Position Position::add(const char* begin, const char* end) { Offset::add(begin, end); return *this; } Position Position::inc(const char* begin, const char* end) const { Offset offset(line, column); offset = offset.inc(begin, end); return Position(file, offset); } bool Position::operator== (const Position &pos) const { return file == pos.file && line == pos.line && column == pos.column; } bool Position::operator!= (const Position &pos) const { return file == pos.file || line != pos.line || column != pos.column; } void Position::operator+= (const Offset &off) { *this = Position(file, line + off.line, off.line > 0 ? off.column : column + off.column); } const Position Position::operator+ (const Offset &off) const { return Position(file, line + off.line, off.line > 0 ? off.column : column + off.column); } const Offset Position::operator- (const Offset &off) const { return Offset(line - off.line, off.line == line ? column - off.column : column); } } golibsass-1.0.0/libsass_src/src/position.hpp000066400000000000000000000076531405214413600211640ustar00rootroot00000000000000#ifndef SASS_POSITION_H #define SASS_POSITION_H #include #include #include "source_data.hpp" #include "ast_fwd_decl.hpp" namespace Sass { class Offset { public: // c-tor Offset(const char chr); Offset(const char* string); Offset(const sass::string& text); Offset(const size_t line, const size_t column); // return new position, incremented by the given string Offset add(const char* begin, const char* end); Offset inc(const char* begin, const char* end) const; // init/create instance from const char substring static Offset init(const char* beg, const char* end); public: // overload operators for position void operator+= (const Offset &pos); bool operator== (const Offset &pos) const; bool operator!= (const Offset &pos) const; Offset operator+ (const Offset &off) const; Offset operator- (const Offset &off) const; public: // overload output stream operator // friend std::ostream& operator<<(std::ostream& strm, const Offset& off); public: Offset off() { return *this; } public: size_t line; size_t column; }; class Position : public Offset { public: // c-tor Position(const size_t file); // line(0), column(0) Position(const size_t file, const Offset& offset); Position(const size_t line, const size_t column); // file(-1) Position(const size_t file, const size_t line, const size_t column); public: // overload operators for position void operator+= (const Offset &off); bool operator== (const Position &pos) const; bool operator!= (const Position &pos) const; const Position operator+ (const Offset &off) const; const Offset operator- (const Offset &off) const; // return new position, incremented by the given string Position add(const char* begin, const char* end); Position inc(const char* begin, const char* end) const; public: // overload output stream operator // friend std::ostream& operator<<(std::ostream& strm, const Position& pos); public: size_t file; }; // Token type for representing lexed chunks of text class Token { public: const char* prefix; const char* begin; const char* end; Token() : prefix(0), begin(0), end(0) { } Token(const char* b, const char* e) : prefix(b), begin(b), end(e) { } Token(const char* str) : prefix(str), begin(str), end(str + strlen(str)) { } Token(const char* p, const char* b, const char* e) : prefix(p), begin(b), end(e) { } size_t length() const { return end - begin; } sass::string ws_before() const { return sass::string(prefix, begin); } sass::string to_string() const { return sass::string(begin, end); } sass::string time_wspace() const { sass::string str(to_string()); sass::string whitespaces(" \t\f\v\n\r"); return str.erase(str.find_last_not_of(whitespaces)+1); } operator bool() { return begin && end && begin >= end; } operator sass::string() { return to_string(); } bool operator==(Token t) { return to_string() == t.to_string(); } }; class SourceSpan { public: SourceSpan(const char* path); SourceSpan(SourceDataObj source, const Offset& position = Offset(0, 0), const Offset& offset = Offset(0, 0)); const char* getPath() const { return source->getPath(); } const char* getRawData() const { return source->getRawData(); } Offset getPosition() const { return position; } size_t getLine() const { return position.line + 1; } size_t getColumn() const { return position.column + 1; } size_t getSrcId() const { return source == nullptr ? std::string::npos : source->getSrcId(); } SourceDataObj source; Offset position; Offset offset; }; } #endif golibsass-1.0.0/libsass_src/src/prelexer.cpp000066400000000000000000001377011405214413600211370ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "util.hpp" #include "util_string.hpp" #include "position.hpp" #include "prelexer.hpp" #include "constants.hpp" namespace Sass { // using namespace Lexer; using namespace Constants; namespace Prelexer { /* def string_re(open, close) /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m end end # A hash of regular expressions that are used for tokenizing strings. # # The key is a `[Symbol, Boolean]` pair. # The symbol represents which style of quotation to use, # while the boolean represents whether or not the string # is following an interpolated segment. STRING_REGULAR_EXPRESSIONS = { :double => { /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m false => string_re('"', '"'), true => string_re('', '"') }, :single => { false => string_re("'", "'"), true => string_re('', "'") }, :uri => { false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ }, # Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a # non-standard version of http://www.w3.org/TR/css3-conditional/ :url_prefix => { false => /url-prefix\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ }, :domain => { false => /domain\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ } } */ /* /#{open} ( \\. | \# (?!\{) | [^#{close}\\#] )* (#{close}|#\{) /m false => string_re('"', '"'), true => string_re('', '"') */ extern const char string_double_negates[] = "\"\\#"; const char* re_string_double_close(const char* src) { return sequence < // valid chars zero_plus < alternatives < // escaped char sequence < exactly <'\\'>, any_char >, // non interpolate hash sequence < exactly <'#'>, negate < exactly <'{'> > >, // other valid chars neg_class_char < string_double_negates > > >, // quoted string closer // or interpolate opening alternatives < exactly <'"'>, lookahead < exactly< hash_lbrace > > > >(src); } const char* re_string_double_open(const char* src) { return sequence < // quoted string opener exactly <'"'>, // valid chars zero_plus < alternatives < // escaped char sequence < exactly <'\\'>, any_char >, // non interpolate hash sequence < exactly <'#'>, negate < exactly <'{'> > >, // other valid chars neg_class_char < string_double_negates > > >, // quoted string closer // or interpolate opening alternatives < exactly <'"'>, lookahead < exactly< hash_lbrace > > > >(src); } extern const char string_single_negates[] = "'\\#"; const char* re_string_single_close(const char* src) { return sequence < // valid chars zero_plus < alternatives < // escaped char sequence < exactly <'\\'>, any_char >, // non interpolate hash sequence < exactly <'#'>, negate < exactly <'{'> > >, // other valid chars neg_class_char < string_single_negates > > >, // quoted string closer // or interpolate opening alternatives < exactly <'\''>, lookahead < exactly< hash_lbrace > > > >(src); } const char* re_string_single_open(const char* src) { return sequence < // quoted string opener exactly <'\''>, // valid chars zero_plus < alternatives < // escaped char sequence < exactly <'\\'>, any_char >, // non interpolate hash sequence < exactly <'#'>, negate < exactly <'{'> > >, // other valid chars neg_class_char < string_single_negates > > >, // quoted string closer // or interpolate opening alternatives < exactly <'\''>, lookahead < exactly< hash_lbrace > > > >(src); } /* :uri => { false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ }, */ const char* re_string_uri_close(const char* src) { return sequence < non_greedy< alternatives< class_char< real_uri_chars >, uri_character, NONASCII, ESCAPE >, alternatives< sequence < optional < W >, exactly <')'> >, lookahead < exactly< hash_lbrace > > > >, optional < sequence < optional < W >, exactly <')'> > > >(src); } const char* re_string_uri_open(const char* src) { return sequence < exactly <'u'>, exactly <'r'>, exactly <'l'>, exactly <'('>, W, alternatives< quoted_string, non_greedy< alternatives< class_char< real_uri_chars >, uri_character, NONASCII, ESCAPE >, alternatives< sequence < W, exactly <')'> >, exactly< hash_lbrace > > > > >(src); } // Match a line comment (/.*?(?=\n|\r\n?|\f|\Z)/. const char* line_comment(const char* src) { return sequence< exactly < slash_slash >, non_greedy< any_char, end_of_line > >(src); } // Match a block comment. const char* block_comment(const char* src) { return sequence< delimited_by< slash_star, star_slash, false > >(src); } /* not use anymore - remove? const char* block_comment_prefix(const char* src) { return exactly(src); } // Match either comment. const char* comment(const char* src) { return line_comment(src); } */ // Match zero plus white-space or line_comments const char* optional_css_whitespace(const char* src) { return zero_plus< alternatives >(src); } const char* css_whitespace(const char* src) { return one_plus< alternatives >(src); } // Match optional_css_whitepace plus block_comments const char* optional_css_comments(const char* src) { return zero_plus< alternatives >(src); } const char* css_comments(const char* src) { return one_plus< alternatives >(src); } // Match one backslash escaped char /\\./ const char* escape_seq(const char* src) { return sequence< exactly<'\\'>, alternatives < minmax_range< 1, 3, xdigit >, any_char >, optional < exactly <' '> > >(src); } // Match identifier start const char* identifier_alpha(const char* src) { return alternatives< unicode_seq, alpha, nonascii, exactly<'-'>, exactly<'_'>, NONASCII, ESCAPE, escape_seq >(src); } // Match identifier after start const char* identifier_alnum(const char* src) { return alternatives< unicode_seq, alnum, nonascii, exactly<'-'>, exactly<'_'>, NONASCII, ESCAPE, escape_seq >(src); } // Match CSS identifiers. const char* strict_identifier(const char* src) { return sequence< one_plus < strict_identifier_alpha >, zero_plus < strict_identifier_alnum > // word_boundary not needed >(src); } // Match CSS identifiers. const char* identifier(const char* src) { return sequence< zero_plus< exactly<'-'> >, one_plus < identifier_alpha >, zero_plus < identifier_alnum > // word_boundary not needed >(src); } const char* strict_identifier_alpha(const char* src) { return alternatives < alpha, nonascii, escape_seq, exactly<'_'> >(src); } const char* strict_identifier_alnum(const char* src) { return alternatives < alnum, nonascii, escape_seq, exactly<'_'> >(src); } // Match a single CSS unit const char* one_unit(const char* src) { return sequence < optional < exactly <'-'> >, strict_identifier_alpha, zero_plus < alternatives< strict_identifier_alnum, sequence < one_plus < exactly<'-'> >, strict_identifier_alpha > > > >(src); } // Match numerator/denominator CSS units const char* multiple_units(const char* src) { return sequence < one_unit, zero_plus < sequence < exactly <'*'>, one_unit > > >(src); } // Match complex CSS unit identifiers const char* unit_identifier(const char* src) { return sequence < multiple_units, optional < sequence < exactly <'/'>, negate < sequence < exactly < calc_fn_kwd >, exactly < '(' > > >, multiple_units > > >(src); } const char* identifier_alnums(const char* src) { return one_plus< identifier_alnum >(src); } // Match number prefix ([\+\-]+) const char* number_prefix(const char* src) { return alternatives < exactly < '+' >, sequence < exactly < '-' >, optional_css_whitespace, exactly< '-' > > >(src); } // Match interpolant schemas const char* identifier_schema(const char* src) { return sequence < one_plus < sequence < zero_plus < alternatives < sequence < optional < exactly <'$'> >, identifier >, exactly <'-'> > >, interpolant, zero_plus < alternatives < digits, sequence < optional < exactly <'$'> >, identifier >, quoted_string, exactly<'-'> > > > >, negate < exactly<'%'> > > (src); } // interpolants can be recursive/nested const char* interpolant(const char* src) { return recursive_scopes< exactly, exactly >(src); } // $re_squote = /'(?:$re_itplnt|\\.|[^'])*'/ const char* single_quoted_string(const char* src) { // match a single quoted string, while skipping interpolants return sequence < exactly <'\''>, zero_plus < alternatives < // skip escapes sequence < exactly < '\\' >, re_linebreak >, escape_seq, unicode_seq, // skip interpolants interpolant, // skip non delimiters any_char_but < '\'' > > >, exactly <'\''> >(src); } // $re_dquote = /"(?:$re_itp|\\.|[^"])*"/ const char* double_quoted_string(const char* src) { // match a single quoted string, while skipping interpolants return sequence < exactly <'"'>, zero_plus < alternatives < // skip escapes sequence < exactly < '\\' >, re_linebreak >, escape_seq, unicode_seq, // skip interpolants interpolant, // skip non delimiters any_char_but < '"' > > >, exactly <'"'> >(src); } // $re_quoted = /(?:$re_squote|$re_dquote)/ const char* quoted_string(const char* src) { // match a quoted string, while skipping interpolants return alternatives< single_quoted_string, double_quoted_string >(src); } const char* sass_value(const char* src) { return alternatives < quoted_string, identifier, percentage, hex, dimension, number >(src); } // this is basically `one_plus < sass_value >` // takes care to not parse invalid combinations const char* value_combinations(const char* src) { // `2px-2px` is invalid combo bool was_number = false; const char* pos; while (src) { if ((pos = alternatives < quoted_string, identifier, percentage, hex >(src))) { was_number = false; src = pos; } else if (!was_number && !exactly<'+'>(src) && (pos = alternatives < dimension, number >(src))) { was_number = true; src = pos; } else { break; } } return src; } // must be at least one interpolant // can be surrounded by sass values // make sure to never parse (dim)(dim) // since this wrongly consumes `2px-1px` // `2px1px` is valid number (unit `px1px`) const char* value_schema(const char* src) { return sequence < one_plus < sequence < optional < value_combinations >, interpolant, optional < value_combinations > > > >(src); } // Match CSS '@' keywords. const char* at_keyword(const char* src) { return sequence, identifier>(src); } /* tok(%r{ ( \\. | (?!url\() [^"'/\#!;\{\}] # " | /(?![\*\/]) | \#(?!\{) | !(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed. )+ }xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri || interpolation(:warn_for_color) */ const char* re_almost_any_value_token(const char* src) { return alternatives < one_plus < alternatives < sequence < exactly <'\\'>, any_char >, sequence < negate < uri_prefix >, neg_class_char < almost_any_value_class > >, sequence < exactly <'/'>, negate < alternatives < exactly <'/'>, exactly <'*'> > > >, sequence < exactly <'\\'>, exactly <'#'>, negate < exactly <'{'> > >, sequence < exactly <'!'>, negate < alpha > > > >, block_comment, line_comment, interpolant, space, sequence < exactly<'u'>, exactly<'r'>, exactly<'l'>, exactly<'('>, zero_plus < alternatives < class_char< real_uri_chars >, uri_character, NONASCII, ESCAPE > >, // false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/, // true => /(#{URLCHAR}*?)(#{W}\)|#\{)/ exactly<')'> > >(src); } /* DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for, :each, :while, :if, :else, :extend, :import, :media, :charset, :content, :_moz_document, :at_root, :error] */ const char* re_special_directive(const char* src) { return alternatives < word < mixin_kwd >, word < include_kwd >, word < function_kwd >, word < return_kwd >, word < debug_kwd >, word < warn_kwd >, word < for_kwd >, word < each_kwd >, word < while_kwd >, word < if_kwd >, word < else_kwd >, word < extend_kwd >, word < import_kwd >, word < media_kwd >, word < charset_kwd >, word < content_kwd >, // exactly < moz_document_kwd >, word < at_root_kwd >, word < error_kwd > >(src); } const char* re_prefixed_directive(const char* src) { return sequence < optional < sequence < exactly <'-'>, one_plus < alnum >, exactly <'-'> > >, exactly < supports_kwd > >(src); } const char* re_reference_combinator(const char* src) { return sequence < optional < sequence < zero_plus < exactly <'-'> >, identifier, exactly <'|'> > >, zero_plus < exactly <'-'> >, identifier >(src); } const char* static_reference_combinator(const char* src) { return sequence < exactly <'/'>, re_reference_combinator, exactly <'/'> >(src); } const char* schema_reference_combinator(const char* src) { return sequence < exactly <'/'>, optional < sequence < css_ip_identifier, exactly <'|'> > >, css_ip_identifier, exactly <'/'> > (src); } const char* kwd_import(const char* src) { return word(src); } const char* kwd_at_root(const char* src) { return word(src); } const char* kwd_with_directive(const char* src) { return word(src); } const char* kwd_without_directive(const char* src) { return word(src); } const char* kwd_media(const char* src) { return word(src); } const char* kwd_supports_directive(const char* src) { return word(src); } const char* kwd_mixin(const char* src) { return word(src); } const char* kwd_function(const char* src) { return word(src); } const char* kwd_return_directive(const char* src) { return word(src); } const char* kwd_include_directive(const char* src) { return word(src); } const char* kwd_content_directive(const char* src) { return word(src); } const char* kwd_charset_directive(const char* src) { return word(src); } const char* kwd_extend(const char* src) { return word(src); } const char* kwd_if_directive(const char* src) { return word(src); } const char* kwd_else_directive(const char* src) { return word(src); } const char* elseif_directive(const char* src) { return sequence< exactly< else_kwd >, optional_css_comments, word< if_after_else_kwd > >(src); } const char* kwd_for_directive(const char* src) { return word(src); } const char* kwd_from(const char* src) { return word(src); } const char* kwd_to(const char* src) { return word(src); } const char* kwd_through(const char* src) { return word(src); } const char* kwd_each_directive(const char* src) { return word(src); } const char* kwd_in(const char* src) { return word(src); } const char* kwd_while_directive(const char* src) { return word(src); } const char* name(const char* src) { return one_plus< alternatives< alnum, exactly<'-'>, exactly<'_'>, escape_seq > >(src); } const char* kwd_warn(const char* src) { return word(src); } const char* kwd_err(const char* src) { return word(src); } const char* kwd_dbg(const char* src) { return word(src); } /* not used anymore - remove? const char* directive(const char* src) { return sequence< exactly<'@'>, identifier >(src); } */ const char* kwd_null(const char* src) { return word(src); } const char* css_identifier(const char* src) { return sequence < zero_plus < exactly <'-'> >, identifier >(src); } const char* css_ip_identifier(const char* src) { return sequence < zero_plus < exactly <'-'> >, alternatives < identifier, interpolant > >(src); } // Match CSS type selectors const char* namespace_prefix(const char* src) { return sequence < optional < alternatives < exactly <'*'>, css_identifier > >, exactly <'|'>, negate < exactly <'='> > >(src); } // Match CSS type selectors const char* namespace_schema(const char* src) { return sequence < optional < alternatives < exactly <'*'>, css_ip_identifier > >, exactly<'|'>, negate < exactly <'='> > >(src); } const char* hyphens_and_identifier(const char* src) { return sequence< zero_plus< exactly< '-' > >, identifier_alnums >(src); } const char* hyphens_and_name(const char* src) { return sequence< zero_plus< exactly< '-' > >, name >(src); } const char* universal(const char* src) { return sequence< optional, exactly<'*'> >(src); } // Match CSS id names. const char* id_name(const char* src) { return sequence, identifier_alnums >(src); } // Match CSS class names. const char* class_name(const char* src) { return sequence, identifier >(src); } // Attribute name in an attribute selector. const char* attribute_name(const char* src) { return alternatives< sequence< optional, identifier>, identifier >(src); } // match placeholder selectors const char* placeholder(const char* src) { return sequence, identifier_alnums >(src); } // Match CSS numeric constants. const char* op(const char* src) { return class_char(src); } const char* sign(const char* src) { return class_char(src); } const char* unsigned_number(const char* src) { return alternatives, exactly<'.'>, one_plus >, digits>(src); } const char* number(const char* src) { return sequence< optional, unsigned_number, optional< sequence< exactly<'e'>, optional, unsigned_number > > >(src); } const char* coefficient(const char* src) { return alternatives< sequence< optional, digits >, sign >(src); } const char* binomial(const char* src) { return sequence < optional < sign >, optional < digits >, exactly <'n'>, zero_plus < sequence < optional_css_whitespace, sign, optional_css_whitespace, digits > > >(src); } const char* percentage(const char* src) { return sequence< number, exactly<'%'> >(src); } const char* ampersand(const char* src) { return exactly<'&'>(src); } /* not used anymore - remove? const char* em(const char* src) { return sequence< number, exactly >(src); } */ const char* dimension(const char* src) { return sequence(src); } const char* hex(const char* src) { const char* p = sequence< exactly<'#'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 4 && len != 7) ? 0 : p; } const char* hexa(const char* src) { const char* p = sequence< exactly<'#'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 5 && len != 9) ? 0 : p; } const char* hex0(const char* src) { const char* p = sequence< exactly<'0'>, exactly<'x'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 5 && len != 8) ? 0 : p; } /* no longer used - remove? const char* rgb_prefix(const char* src) { return word(src); }*/ // Match CSS uri specifiers. const char* uri_prefix(const char* src) { return sequence < exactly < url_kwd >, zero_plus < sequence < exactly <'-'>, one_plus < alpha > > >, exactly <'('> >(src); } // TODO: rename the following two functions /* no longer used - remove? const char* uri(const char* src) { return sequence< exactly, optional, quoted_string, optional, exactly<')'> >(src); }*/ /* no longer used - remove? const char* url_value(const char* src) { return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol one_plus< sequence< zero_plus< exactly<'/'> >, filename > >, // one or more folders and/or trailing filename optional< exactly<'/'> > >(src); }*/ /* no longer used - remove? const char* url_schema(const char* src) { return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol filename_schema >(src); // optional trailing slash }*/ // Match CSS "!important" keyword. const char* kwd_important(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match CSS "!optional" keyword. const char* kwd_optional(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match Sass "!default" keyword. const char* default_flag(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match Sass "!global" keyword. const char* global_flag(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match CSS pseudo-class/element prefixes. const char* pseudo_prefix(const char* src) { return sequence< exactly<':'>, optional< exactly<':'> > >(src); } // Match CSS function call openers. const char* functional_schema(const char* src) { return sequence < one_plus < sequence < zero_plus < alternatives < identifier, exactly <'-'> > >, one_plus < sequence < interpolant, alternatives < digits, identifier, exactly<'+'>, exactly<'-'> > > > > >, negate < exactly <'%'> >, lookahead < exactly <'('> > > (src); } const char* re_nothing(const char* src) { return src; } const char* re_functional(const char* src) { return sequence< identifier, optional < block_comment >, exactly<'('> >(src); } const char* re_pseudo_selector(const char* src) { return sequence< identifier, optional < block_comment >, exactly<'('> >(src); } // Match the CSS negation pseudo-class. const char* pseudo_not(const char* src) { return word< pseudo_not_fn_kwd >(src); } // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. const char* even(const char* src) { return word(src); } const char* odd(const char* src) { return word(src); } // Match CSS attribute-matching operators. const char* exact_match(const char* src) { return exactly<'='>(src); } const char* class_match(const char* src) { return exactly(src); } const char* dash_match(const char* src) { return exactly(src); } const char* prefix_match(const char* src) { return exactly(src); } const char* suffix_match(const char* src) { return exactly(src); } const char* substring_match(const char* src) { return exactly(src); } // Match CSS combinators. /* not used anymore - remove? const char* adjacent_to(const char* src) { return sequence< optional_spaces, exactly<'+'> >(src); } const char* precedes(const char* src) { return sequence< optional_spaces, exactly<'~'> >(src); } const char* parent_of(const char* src) { return sequence< optional_spaces, exactly<'>'> >(src); } const char* ancestor_of(const char* src) { return sequence< spaces, negate< exactly<'{'> > >(src); }*/ // Match SCSS variable names. const char* variable(const char* src) { return sequence, identifier>(src); } // parse `calc`, `-a-calc` and `--b-c-calc` // but do not parse `foocalc` or `foo-calc` const char* calc_fn_call(const char* src) { return sequence < optional < sequence < hyphens, one_plus < sequence < strict_identifier, hyphens > > > >, exactly < calc_fn_kwd >, word_boundary >(src); } // Match Sass boolean keywords. const char* kwd_true(const char* src) { return word(src); } const char* kwd_false(const char* src) { return word(src); } const char* kwd_only(const char* src) { return keyword < only_kwd >(src); } const char* kwd_and(const char* src) { return keyword < and_kwd >(src); } const char* kwd_or(const char* src) { return keyword < or_kwd >(src); } const char* kwd_not(const char* src) { return keyword < not_kwd >(src); } const char* kwd_eq(const char* src) { return exactly(src); } const char* kwd_neq(const char* src) { return exactly(src); } const char* kwd_gt(const char* src) { return exactly(src); } const char* kwd_gte(const char* src) { return exactly(src); } const char* kwd_lt(const char* src) { return exactly(src); } const char* kwd_lte(const char* src) { return exactly(src); } const char* kwd_using(const char* src) { return keyword(src); } // match specific IE syntax const char* ie_progid(const char* src) { return sequence < word, exactly<':'>, alternatives< identifier_schema, identifier >, zero_plus< sequence< exactly<'.'>, alternatives< identifier_schema, identifier > > >, zero_plus < sequence< exactly<'('>, optional_css_whitespace, optional < sequence< alternatives< variable, identifier_schema, identifier >, optional_css_whitespace, exactly<'='>, optional_css_whitespace, alternatives< variable, identifier_schema, identifier, quoted_string, number, hex, hexa >, zero_plus< sequence< optional_css_whitespace, exactly<','>, optional_css_whitespace, sequence< alternatives< variable, identifier_schema, identifier >, optional_css_whitespace, exactly<'='>, optional_css_whitespace, alternatives< variable, identifier_schema, identifier, quoted_string, number, hex, hexa > > > > > >, optional_css_whitespace, exactly<')'> > > >(src); } const char* ie_expression(const char* src) { return sequence < word, exactly<'('>, skip_over_scopes< exactly<'('>, exactly<')'> > >(src); } const char* ie_property(const char* src) { return alternatives < ie_expression, ie_progid >(src); } // const char* ie_args(const char* src) { // return sequence< alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by< '(', ')', true> >, // zero_plus< sequence< optional_css_whitespace, exactly<','>, optional_css_whitespace, alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by<'(', ')', true> > > > >(src); // } const char* ie_keyword_arg_property(const char* src) { return alternatives < variable, identifier_schema, identifier >(src); } const char* ie_keyword_arg_value(const char* src) { return alternatives < variable, identifier_schema, identifier, quoted_string, number, hex, hexa, sequence < exactly < '(' >, skip_over_scopes < exactly < '(' >, exactly < ')' > > > >(src); } const char* ie_keyword_arg(const char* src) { return sequence < ie_keyword_arg_property, optional_css_whitespace, exactly<'='>, optional_css_whitespace, ie_keyword_arg_value >(src); } // Path matching functions. /* not used anymore - remove? const char* folder(const char* src) { return sequence< zero_plus< any_char_except<'/'> >, exactly<'/'> >(src); } const char* folders(const char* src) { return zero_plus< folder >(src); }*/ /* not used anymore - remove? const char* chunk(const char* src) { char inside_str = 0; const char* p = src; size_t depth = 0; while (true) { if (!*p) { return 0; } else if (!inside_str && (*p == '"' || *p == '\'')) { inside_str = *p; } else if (*p == inside_str && *(p-1) != '\\') { inside_str = 0; } else if (*p == '(' && !inside_str) { ++depth; } else if (*p == ')' && !inside_str) { if (depth == 0) return p; else --depth; } ++p; } // unreachable return 0; } */ // follow the CSS spec more closely and see if this helps us scan URLs correctly /* not used anymore - remove? const char* NL(const char* src) { return alternatives< exactly<'\n'>, sequence< exactly<'\r'>, exactly<'\n'> >, exactly<'\r'>, exactly<'\f'> >(src); }*/ const char* H(const char* src) { return Util::ascii_isxdigit(static_cast(*src)) ? src+1 : 0; } const char* W(const char* src) { return zero_plus< alternatives< space, exactly< '\t' >, exactly< '\r' >, exactly< '\n' >, exactly< '\f' > > >(src); } const char* UUNICODE(const char* src) { return sequence< exactly<'\\'>, between, optional< W > >(src); } const char* NONASCII(const char* src) { return nonascii(src); } const char* ESCAPE(const char* src) { return alternatives< UUNICODE, sequence< exactly<'\\'>, alternatives< NONASCII, escapable_character > > >(src); } const char* list_terminator(const char* src) { return alternatives < exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, exactly<']'>, exactly<':'>, end_of_file, exactly, default_flag, global_flag >(src); }; const char* space_list_terminator(const char* src) { return alternatives < exactly<','>, list_terminator >(src); }; // const char* real_uri_prefix(const char* src) { // return alternatives< // exactly< url_kwd >, // exactly< url_prefix_kwd > // >(src); // } const char* real_uri(const char* src) { return sequence< exactly< url_kwd >, exactly< '(' >, W, real_uri_value, exactly< ')' > >(src); } const char* real_uri_suffix(const char* src) { return sequence< W, exactly< ')' > >(src); } const char* real_uri_value(const char* src) { return sequence< non_greedy< alternatives< class_char< real_uri_chars >, uri_character, NONASCII, ESCAPE >, alternatives< real_uri_suffix, exactly< hash_lbrace > > > > (src); } const char* static_string(const char* src) { const char* pos = src; const char * s = quoted_string(pos); Token t(pos, s); const unsigned int p = count_interval< interpolant >(t.begin, t.end); return (p == 0) ? t.end : 0; } const char* unicode_seq(const char* src) { return sequence < alternatives < exactly< 'U' >, exactly< 'u' > >, exactly< '+' >, padded_token < 6, xdigit, exactly < '?' > > >(src); } const char* static_component(const char* src) { return alternatives< identifier, static_string, percentage, hex, hexa, exactly<'|'>, // exactly<'+'>, sequence < number, unit_identifier >, number, sequence< exactly<'!'>, word > >(src); } const char* static_property(const char* src) { return sequence < zero_plus< sequence < optional_css_comments, alternatives < exactly<','>, exactly<'('>, exactly<')'>, kwd_optional, quoted_string, interpolant, identifier, percentage, dimension, variable, alnum, sequence < exactly <'\\'>, any_char > > > >, lookahead < sequence < optional_css_comments, alternatives < exactly <';'>, exactly <'}'>, end_of_file > > > >(src); } const char* static_value(const char* src) { return sequence< sequence< static_component, zero_plus< identifier > >, zero_plus < sequence< alternatives< sequence< optional_spaces, alternatives< exactly < '/' >, exactly < ',' >, exactly < ' ' > >, optional_spaces >, spaces >, static_component > >, zero_plus < spaces >, alternatives< exactly<';'>, exactly<'}'> > >(src); } extern const char css_variable_url_negates[] = "()[]{}\"'#/"; const char* css_variable_value(const char* src) { return sequence< alternatives< sequence< negate< exactly< url_fn_kwd > >, one_plus< neg_class_char< css_variable_url_negates > > >, sequence< exactly<'#'>, negate< exactly<'{'> > >, sequence< exactly<'/'>, negate< exactly<'*'> > >, static_string, real_uri, block_comment > >(src); } extern const char css_variable_url_top_level_negates[] = "()[]{}\"'#/;"; const char* css_variable_top_level_value(const char* src) { return sequence< alternatives< sequence< negate< exactly< url_fn_kwd > >, one_plus< neg_class_char< css_variable_url_top_level_negates > > >, sequence< exactly<'#'>, negate< exactly<'{'> > >, sequence< exactly<'/'>, negate< exactly<'*'> > >, static_string, real_uri, block_comment > >(src); } const char* parenthese_scope(const char* src) { return sequence < exactly < '(' >, skip_over_scopes < exactly < '(' >, exactly < ')' > > >(src); } const char* re_selector_list(const char* src) { return alternatives < // partial bem selector sequence < ampersand, one_plus < exactly < '-' > >, word_boundary, optional_spaces >, // main selector matching one_plus < alternatives < // consume whitespace and comments spaces, block_comment, line_comment, // match `/deep/` selector (pass-trough) // there is no functionality for it yet schema_reference_combinator, // match selector ops /[*&%,\[\]]/ class_char < selector_lookahead_ops >, // match selector combinators /[>+~]/ class_char < selector_combinator_ops >, // match pseudo selectors sequence < exactly <'('>, optional_spaces, optional , optional_spaces, exactly <')'> >, // match attribute compare operators alternatives < exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match >, // main selector match sequence < // allow namespace prefix optional < namespace_schema >, // modifiers prefixes alternatives < sequence < exactly <'#'>, // not for interpolation negate < exactly <'{'> > >, // class match exactly <'.'>, // single or double colon sequence < optional < pseudo_prefix >, // fix libsass issue 2376 negate < uri_prefix > > >, // accept hypens in token one_plus < sequence < // can start with hyphens zero_plus < sequence < exactly <'-'>, optional_spaces > >, // now the main token alternatives < kwd_optional, exactly <'*'>, quoted_string, interpolant, identifier, variable, percentage, binomial, dimension, alnum > > >, // can also end with hyphens zero_plus < exactly<'-'> > > > > >(src); } const char* type_selector(const char* src) { return sequence< optional, identifier>(src); } const char* re_type_selector(const char* src) { return alternatives< type_selector, universal, dimension, percentage, number, identifier_alnums >(src); } const char* re_static_expression(const char* src) { return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src); } // lexer special_fn: these functions cannot be overloaded // (/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i) const char* re_special_fun(const char* src) { // match this first as we test prefix hyphens if (const char* calc = calc_fn_call(src)) { return calc; } return sequence < optional < sequence < exactly <'-'>, one_plus < alternatives < alpha, exactly <'+'>, exactly <'-'> > > > >, alternatives < word < expression_kwd >, sequence < sequence < exactly < progid_kwd >, exactly <':'> >, zero_plus < alternatives < char_range <'a', 'z'>, exactly <'.'> > > > > >(src); } } } golibsass-1.0.0/libsass_src/src/prelexer.hpp000066400000000000000000000402361405214413600211400ustar00rootroot00000000000000#ifndef SASS_PRELEXER_H #define SASS_PRELEXER_H #include #include "lexer.hpp" namespace Sass { // using namespace Lexer; namespace Prelexer { //#################################### // KEYWORD "REGEX" MATCHERS //#################################### // Match Sass boolean keywords. const char* kwd_true(const char* src); const char* kwd_false(const char* src); const char* kwd_only(const char* src); const char* kwd_and(const char* src); const char* kwd_or(const char* src); const char* kwd_not(const char* src); const char* kwd_eq(const char* src); const char* kwd_neq(const char* src); const char* kwd_gt(const char* src); const char* kwd_gte(const char* src); const char* kwd_lt(const char* src); const char* kwd_lte(const char* src); const char* kwd_using(const char* src); // Match standard control chars const char* kwd_at(const char* src); const char* kwd_dot(const char* src); const char* kwd_comma(const char* src); const char* kwd_colon(const char* src); const char* kwd_slash(const char* src); const char* kwd_star(const char* src); const char* kwd_plus(const char* src); const char* kwd_minus(const char* src); //#################################### // SPECIAL "REGEX" CONSTRUCTS //#################################### // Match a sequence of characters delimited by the supplied chars. template const char* delimited_by(const char* src) { src = exactly(src); if (!src) return 0; const char* stop; while (true) { if (!*src) return 0; stop = exactly(src); if (stop && (!esc || *(src - 1) != '\\')) return stop; src = stop ? stop : src + 1; } } // skip to delimiter (mx) inside given range // this will savely skip over all quoted strings // recursive skip stuff delimited by start/stop // first start/opener must be consumed already! template const char* skip_over_scopes(const char* src, const char* end) { size_t level = 0; bool in_squote = false; bool in_dquote = false; bool in_backslash_escape = false; while ((end == nullptr || src < end) && *src != '\0') { // has escaped sequence? if (in_backslash_escape) { in_backslash_escape = false; } else if (*src == '\\') { in_backslash_escape = true; } else if (*src == '"') { in_dquote = ! in_dquote; } else if (*src == '\'') { in_squote = ! in_squote; } else if (in_dquote || in_squote) { // take everything literally } // find another opener inside? else if (const char* pos = start(src)) { ++ level; // increase counter src = pos - 1; // advance position } // look for the closer (maybe final, maybe not) else if (const char* final = stop(src)) { // only close one level? if (level > 0) -- level; // return position at end of stop // delimiter may be multiple chars else return final; // advance position src = final - 1; } // next ++ src; } return 0; } // skip to a skip delimited by parentheses // uses smart `skip_over_scopes` internally const char* parenthese_scope(const char* src); // skip to delimiter (mx) inside given range // this will savely skip over all quoted strings // recursive skip stuff delimited by start/stop // first start/opener must be consumed already! template const char* skip_over_scopes(const char* src) { return skip_over_scopes(src, nullptr); } // Match a sequence of characters delimited by the supplied chars. template const char* recursive_scopes(const char* src) { // parse opener src = start(src); // abort if not found if (!src) return 0; // parse the rest until final closer return skip_over_scopes(src); } // Match a sequence of characters delimited by the supplied strings. template const char* delimited_by(const char* src) { src = exactly(src); if (!src) return 0; const char* stop; while (true) { if (!*src) return 0; stop = exactly(src); if (stop && (!esc || *(src - 1) != '\\')) return stop; src = stop ? stop : src + 1; } } // Tries to match a certain number of times (between the supplied interval). template const char* between(const char* src) { for (size_t i = 0; i < lo; ++i) { src = mx(src); if (!src) return 0; } for (size_t i = lo; i <= hi; ++i) { const char* new_src = mx(src); if (!new_src) return src; src = new_src; } return src; } // equivalent of STRING_REGULAR_EXPRESSIONS const char* re_string_double_open(const char* src); const char* re_string_double_close(const char* src); const char* re_string_single_open(const char* src); const char* re_string_single_close(const char* src); const char* re_string_uri_open(const char* src); const char* re_string_uri_close(const char* src); // Match a line comment. const char* line_comment(const char* src); // Match a block comment. const char* block_comment(const char* src); // Match either. const char* comment(const char* src); // Match double- and single-quoted strings. const char* double_quoted_string(const char* src); const char* single_quoted_string(const char* src); const char* quoted_string(const char* src); // Match interpolants. const char* interpolant(const char* src); // Match number prefix ([\+\-]+) const char* number_prefix(const char* src); // Match zero plus white-space or line_comments const char* optional_css_whitespace(const char* src); const char* css_whitespace(const char* src); // Match optional_css_whitepace plus block_comments const char* optional_css_comments(const char* src); const char* css_comments(const char* src); // Match one backslash escaped char const char* escape_seq(const char* src); // Match CSS css variables. const char* custom_property_name(const char* src); // Match a CSS identifier. const char* identifier(const char* src); const char* identifier_alpha(const char* src); const char* identifier_alnum(const char* src); const char* strict_identifier(const char* src); const char* strict_identifier_alpha(const char* src); const char* strict_identifier_alnum(const char* src); // Match a CSS unit identifier. const char* one_unit(const char* src); const char* multiple_units(const char* src); const char* unit_identifier(const char* src); // const char* strict_identifier_alnums(const char* src); // Match reference selector. const char* re_reference_combinator(const char* src); const char* static_reference_combinator(const char* src); const char* schema_reference_combinator(const char* src); // Match interpolant schemas const char* identifier_schema(const char* src); const char* value_schema(const char* src); const char* sass_value(const char* src); // const char* filename(const char* src); // const char* filename_schema(const char* src); // const char* url_schema(const char* src); // const char* url_value(const char* src); const char* vendor_prefix(const char* src); const char* re_special_directive(const char* src); const char* re_prefixed_directive(const char* src); const char* re_almost_any_value_token(const char* src); // Match CSS '@' keywords. const char* at_keyword(const char* src); const char* kwd_import(const char* src); const char* kwd_at_root(const char* src); const char* kwd_with_directive(const char* src); const char* kwd_without_directive(const char* src); const char* kwd_media(const char* src); const char* kwd_supports_directive(const char* src); // const char* keyframes(const char* src); // const char* keyf(const char* src); const char* kwd_mixin(const char* src); const char* kwd_function(const char* src); const char* kwd_return_directive(const char* src); const char* kwd_include_directive(const char* src); const char* kwd_content_directive(const char* src); const char* kwd_charset_directive(const char* src); const char* kwd_extend(const char* src); const char* unicode_seq(const char* src); const char* kwd_if_directive(const char* src); const char* kwd_else_directive(const char* src); const char* elseif_directive(const char* src); const char* kwd_for_directive(const char* src); const char* kwd_from(const char* src); const char* kwd_to(const char* src); const char* kwd_through(const char* src); const char* kwd_each_directive(const char* src); const char* kwd_in(const char* src); const char* kwd_while_directive(const char* src); const char* re_nothing(const char* src); const char* re_special_fun(const char* src); const char* kwd_warn(const char* src); const char* kwd_err(const char* src); const char* kwd_dbg(const char* src); const char* kwd_null(const char* src); const char* re_selector_list(const char* src); const char* re_type_selector(const char* src); const char* re_static_expression(const char* src); // identifier that can start with hyphens const char* css_identifier(const char* src); const char* css_ip_identifier(const char* src); // Match CSS type selectors const char* namespace_schema(const char* src); const char* namespace_prefix(const char* src); const char* type_selector(const char* src); const char* hyphens_and_identifier(const char* src); const char* hyphens_and_name(const char* src); const char* universal(const char* src); // Match CSS id names. const char* id_name(const char* src); // Match CSS class names. const char* class_name(const char* src); // Attribute name in an attribute selector const char* attribute_name(const char* src); // Match placeholder selectors. const char* placeholder(const char* src); // Match CSS numeric constants. const char* op(const char* src); const char* sign(const char* src); const char* unsigned_number(const char* src); const char* number(const char* src); const char* coefficient(const char* src); const char* binomial(const char* src); const char* percentage(const char* src); const char* ampersand(const char* src); const char* dimension(const char* src); const char* hex(const char* src); const char* hexa(const char* src); const char* hex0(const char* src); // const char* rgb_prefix(const char* src); // Match CSS uri specifiers. const char* uri_prefix(const char* src); // Match CSS "!important" keyword. const char* kwd_important(const char* src); // Match CSS "!optional" keyword. const char* kwd_optional(const char* src); // Match Sass "!default" keyword. const char* default_flag(const char* src); const char* global_flag(const char* src); // Match CSS pseudo-class/element prefixes const char* pseudo_prefix(const char* src); // Match CSS function call openers. const char* re_functional(const char* src); const char* re_pseudo_selector(const char* src); const char* functional_schema(const char* src); const char* pseudo_not(const char* src); // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. const char* even(const char* src); const char* odd(const char* src); // Match CSS attribute-matching operators. const char* exact_match(const char* src); const char* class_match(const char* src); const char* dash_match(const char* src); const char* prefix_match(const char* src); const char* suffix_match(const char* src); const char* substring_match(const char* src); // Match CSS combinators. // const char* adjacent_to(const char* src); // const char* precedes(const char* src); // const char* parent_of(const char* src); // const char* ancestor_of(const char* src); // Match SCSS variable names. const char* variable(const char* src); const char* calc_fn_call(const char* src); // IE stuff const char* ie_progid(const char* src); const char* ie_expression(const char* src); const char* ie_property(const char* src); const char* ie_keyword_arg(const char* src); const char* ie_keyword_arg_value(const char* src); const char* ie_keyword_arg_property(const char* src); // characters that terminate parsing of a list const char* list_terminator(const char* src); const char* space_list_terminator(const char* src); // match url() const char* H(const char* src); const char* W(const char* src); // `UNICODE` makes VS sad const char* UUNICODE(const char* src); const char* NONASCII(const char* src); const char* ESCAPE(const char* src); const char* real_uri(const char* src); const char* real_uri_suffix(const char* src); // const char* real_uri_prefix(const char* src); const char* real_uri_value(const char* src); // Path matching functions. // const char* folder(const char* src); // const char* folders(const char* src); const char* static_string(const char* src); const char* static_component(const char* src); const char* static_property(const char* src); const char* static_value(const char* src); const char* css_variable_value(const char* src); const char* css_variable_top_level_value(const char* src); // Utility functions for finding and counting characters in a string. template const char* find_first(const char* src) { while (*src && *src != c) ++src; return *src ? src : 0; } template const char* find_first(const char* src) { while (*src && !mx(src)) ++src; return *src ? src : 0; } template const char* find_first_in_interval(const char* beg, const char* end) { bool esc = false; while ((beg < end) && *beg) { if (esc) esc = false; else if (*beg == '\\') esc = true; else if (mx(beg)) return beg; ++beg; } return 0; } template const char* find_first_in_interval(const char* beg, const char* end) { bool esc = false; while ((beg < end) && *beg) { if (esc) esc = false; else if (*beg == '\\') esc = true; else if (const char* pos = skip(beg)) beg = pos; else if (mx(beg)) return beg; ++beg; } return 0; } template unsigned int count_interval(const char* beg, const char* end) { unsigned int counter = 0; bool esc = false; while (beg < end && *beg) { const char* p; if (esc) { esc = false; ++beg; } else if (*beg == '\\') { esc = true; ++beg; } else if ((p = mx(beg))) { ++counter; beg = p; } else { ++beg; } } return counter; } template const char* padded_token(const char* src) { size_t got = 0; const char* pos = src; while (got < size) { if (!mx(pos)) break; ++ pos; ++ got; } while (got < size) { if (!pad(pos)) break; ++ pos; ++ got; } return got ? pos : 0; } template const char* minmax_range(const char* src) { size_t got = 0; const char* pos = src; while (got < max) { if (!mx(pos)) break; ++ pos; ++ got; } if (got < min) return 0; if (got > max) return 0; return pos; } template const char* char_range(const char* src) { if (*src < min) return 0; if (*src > max) return 0; return src + 1; } } } #endif golibsass-1.0.0/libsass_src/src/remove_placeholders.cpp000066400000000000000000000047211405214413600233260ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "remove_placeholders.hpp" namespace Sass { Remove_Placeholders::Remove_Placeholders() { } void Remove_Placeholders::operator()(Block* b) { for (size_t i = 0, L = b->length(); i < L; ++i) { if (b->get(i)) b->get(i)->perform(this); } } void Remove_Placeholders::remove_placeholders(SimpleSelector* simple) { if (PseudoSelector * pseudo = simple->getPseudoSelector()) { if (pseudo->selector()) remove_placeholders(pseudo->selector()); } } void Remove_Placeholders::remove_placeholders(CompoundSelector* compound) { for (size_t i = 0, L = compound->length(); i < L; ++i) { if (compound->get(i)) remove_placeholders(compound->get(i)); } listEraseItemIf(compound->elements(), listIsEmpty); } void Remove_Placeholders::remove_placeholders(ComplexSelector* complex) { if (complex->has_placeholder()) { complex->clear(); // remove all } else { for (size_t i = 0, L = complex->length(); i < L; ++i) { if (CompoundSelector * compound = complex->get(i)->getCompound()) { if (compound) remove_placeholders(compound); } } listEraseItemIf(complex->elements(), listIsEmpty); } } SelectorList* Remove_Placeholders::remove_placeholders(SelectorList* sl) { for (size_t i = 0, L = sl->length(); i < L; ++i) { if (sl->get(i)) remove_placeholders(sl->get(i)); } listEraseItemIf(sl->elements(), listIsEmpty); return sl; } void Remove_Placeholders::operator()(CssMediaRule* rule) { if (rule->block()) operator()(rule->block()); } void Remove_Placeholders::operator()(StyleRule* r) { if (SelectorListObj sl = r->selector()) { // Set the new placeholder selector list r->selector((remove_placeholders(sl))); } // Iterate into child blocks Block_Obj b = r->block(); for (size_t i = 0, L = b->length(); i < L; ++i) { if (b->get(i)) { b->get(i)->perform(this); } } } void Remove_Placeholders::operator()(SupportsRule* m) { if (m->block()) operator()(m->block()); } void Remove_Placeholders::operator()(AtRule* a) { if (a->block()) a->block()->perform(this); } } golibsass-1.0.0/libsass_src/src/remove_placeholders.hpp000066400000000000000000000014251405214413600233310ustar00rootroot00000000000000#ifndef SASS_REMOVE_PLACEHOLDERS_H #define SASS_REMOVE_PLACEHOLDERS_H #include "ast_fwd_decl.hpp" #include "operation.hpp" namespace Sass { class Remove_Placeholders : public Operation_CRTP { public: SelectorList* remove_placeholders(SelectorList*); void remove_placeholders(SimpleSelector* simple); void remove_placeholders(CompoundSelector* complex); void remove_placeholders(ComplexSelector* complex); public: Remove_Placeholders(); ~Remove_Placeholders() { } void operator()(Block*); void operator()(StyleRule*); void operator()(CssMediaRule*); void operator()(SupportsRule*); void operator()(AtRule*); // ignore missed types template void fallback(U x) {} }; } #endif golibsass-1.0.0/libsass_src/src/sass.cpp000066400000000000000000000115031405214413600202510ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include "sass.h" #include "file.hpp" #include "util.hpp" #include "context.hpp" #include "sass_context.hpp" #include "sass_functions.hpp" namespace Sass { // helper to convert string list to vector sass::vector list2vec(struct string_list* cur) { sass::vector list; while (cur) { list.push_back(cur->string); cur = cur->next; } return list; } } extern "C" { using namespace Sass; // Allocate libsass heap memory // Don't forget string termination! void* ADDCALL sass_alloc_memory(size_t size) { void* ptr = malloc(size); if (ptr == NULL) { std::cerr << "Out of memory.\n"; exit(EXIT_FAILURE); } return ptr; } char* ADDCALL sass_copy_c_string(const char* str) { if (str == nullptr) return nullptr; size_t len = strlen(str) + 1; char* cpy = (char*) sass_alloc_memory(len); std::memcpy(cpy, str, len); return cpy; } // Deallocate libsass heap memory void ADDCALL sass_free_memory(void* ptr) { if (ptr) free (ptr); } // caller must free the returned memory char* ADDCALL sass_string_quote (const char *str, const char quote_mark) { sass::string quoted = quote(str, quote_mark); return sass_copy_c_string(quoted.c_str()); } // caller must free the returned memory char* ADDCALL sass_string_unquote (const char *str) { sass::string unquoted = unquote(str); return sass_copy_c_string(unquoted.c_str()); } char* ADDCALL sass_compiler_find_include (const char* file, struct Sass_Compiler* compiler) { // get the last import entry to get current base directory Sass_Import_Entry import = sass_compiler_get_last_import(compiler); const sass::vector& incs = compiler->cpp_ctx->include_paths; // create the vector with paths to lookup sass::vector paths(1 + incs.size()); paths.push_back(File::dir_name(import->abs_path)); paths.insert( paths.end(), incs.begin(), incs.end() ); // now resolve the file path relative to lookup paths sass::string resolved(File::find_include(file, paths)); return sass_copy_c_string(resolved.c_str()); } char* ADDCALL sass_compiler_find_file (const char* file, struct Sass_Compiler* compiler) { // get the last import entry to get current base directory Sass_Import_Entry import = sass_compiler_get_last_import(compiler); const sass::vector& incs = compiler->cpp_ctx->include_paths; // create the vector with paths to lookup sass::vector paths(1 + incs.size()); paths.push_back(File::dir_name(import->abs_path)); paths.insert( paths.end(), incs.begin(), incs.end() ); // now resolve the file path relative to lookup paths sass::string resolved(File::find_file(file, paths)); return sass_copy_c_string(resolved.c_str()); } // Make sure to free the returned value! // Incs array has to be null terminated! // this has the original resolve logic for sass include char* ADDCALL sass_find_include (const char* file, struct Sass_Options* opt) { sass::vector vec(list2vec(opt->include_paths)); sass::string resolved(File::find_include(file, vec)); return sass_copy_c_string(resolved.c_str()); } // Make sure to free the returned value! // Incs array has to be null terminated! char* ADDCALL sass_find_file (const char* file, struct Sass_Options* opt) { sass::vector vec(list2vec(opt->include_paths)); sass::string resolved(File::find_file(file, vec)); return sass_copy_c_string(resolved.c_str()); } // Get compiled libsass version const char* ADDCALL libsass_version(void) { return LIBSASS_VERSION; } // Get compiled libsass version const char* ADDCALL libsass_language_version(void) { return LIBSASS_LANGUAGE_VERSION; } } namespace Sass { // helper to aid dreaded MSVC debug mode char* sass_copy_string(sass::string str) { // In MSVC the following can lead to segfault: // sass_copy_c_string(stream.str().c_str()); // Reason is that the string returned by str() is disposed before // sass_copy_c_string is invoked. The string is actually a stack // object, so indeed nobody is holding on to it. So it seems // perfectly fair to release it right away. So the const char* // by c_str will point to invalid memory. I'm not sure if this is // the behavior for all compiler, but I'm pretty sure we would // have gotten more issues reported if that would be the case. // Wrapping it in a functions seems the cleanest approach as the // function must hold on to the stack variable until it's done. return sass_copy_c_string(str.c_str()); } } golibsass-1.0.0/libsass_src/src/sass.hpp000066400000000000000000000071141405214413600202610ustar00rootroot00000000000000// must be the first include in all compile units #ifndef SASS_SASS_H #define SASS_SASS_H // undefine extensions macro to tell sys includes // that we do not want any macros to be exported // mainly fixes an issue on SmartOS (SEC macro) #undef __EXTENSIONS__ #ifdef _MSC_VER #pragma warning(disable : 4005) #endif // applies to MSVC and MinGW #ifdef _WIN32 // we do not want the ERROR macro # ifndef NOGDI # define NOGDI # endif // we do not want the min/max macro # ifndef NOMINMAX # define NOMINMAX # endif // we do not want the IN/OUT macro # ifndef _NO_W32_PSEUDO_MODIFIERS # define _NO_W32_PSEUDO_MODIFIERS # endif #endif // should we be case insensitive // when dealing with files or paths #ifndef FS_CASE_SENSITIVE # ifdef _WIN32 # define FS_CASE_SENSITIVE 0 # else # define FS_CASE_SENSITIVE 1 # endif #endif // path separation char #ifndef PATH_SEP # ifdef _WIN32 # define PATH_SEP ';' # else # define PATH_SEP ':' # endif #endif // Include C-API header #include "sass/base.h" // Include allocator #include "memory.hpp" // For C++ helper #include #include // output behavior namespace Sass { // create some C++ aliases for the most used options const static Sass_Output_Style NESTED = SASS_STYLE_NESTED; const static Sass_Output_Style COMPACT = SASS_STYLE_COMPACT; const static Sass_Output_Style EXPANDED = SASS_STYLE_EXPANDED; const static Sass_Output_Style COMPRESSED = SASS_STYLE_COMPRESSED; // only used internal to trigger ruby inspect behavior const static Sass_Output_Style INSPECT = SASS_STYLE_INSPECT; const static Sass_Output_Style TO_SASS = SASS_STYLE_TO_SASS; const static Sass_Output_Style TO_CSS = SASS_STYLE_TO_CSS; // helper to aid dreaded MSVC debug mode // see implementation for more details char* sass_copy_string(sass::string str); } // input behaviors enum Sass_Input_Style { SASS_CONTEXT_NULL, SASS_CONTEXT_FILE, SASS_CONTEXT_DATA, SASS_CONTEXT_FOLDER }; // simple linked list struct string_list { string_list* next; char* string; }; // sass config options structure struct Sass_Inspect_Options { // Output style for the generated css code // A value from above SASS_STYLE_* constants enum Sass_Output_Style output_style; // Precision for fractional numbers int precision; // initialization list (constructor with defaults) Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED, int precision = 10) : output_style(style), precision(precision) { } }; // sass config options structure struct Sass_Output_Options : Sass_Inspect_Options { // String to be used for indentation const char* indent; // String to be used to for line feeds const char* linefeed; // Emit comments in the generated CSS indicating // the corresponding source line. bool source_comments; // initialization list (constructor with defaults) Sass_Output_Options(struct Sass_Inspect_Options opt, const char* indent = " ", const char* linefeed = "\n", bool source_comments = false) : Sass_Inspect_Options(opt), indent(indent), linefeed(linefeed), source_comments(source_comments) { } // initialization list (constructor with defaults) Sass_Output_Options(Sass_Output_Style style = Sass::NESTED, int precision = 10, const char* indent = " ", const char* linefeed = "\n", bool source_comments = false) : Sass_Inspect_Options(style, precision), indent(indent), linefeed(linefeed), source_comments(source_comments) { } }; #endif golibsass-1.0.0/libsass_src/src/sass2scss.cpp000066400000000000000000000611051405214413600212320ustar00rootroot00000000000000/** * sass2scss * Licensed under the MIT License * Copyright (c) Marcel Greter */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE #endif // include library #include #include #include #include #include #include #include ///* // // src comments: comments in sass syntax (staring with //) // css comments: multiline comments in css syntax (starting with /*) // // KEEP_COMMENT: keep src comments in the resulting css code // STRIP_COMMENT: strip out all comments (either src or css) // CONVERT_COMMENT: convert all src comments to css comments // //*/ // our own header #include "sass2scss.h" // add namespace for c++ namespace Sass { // return the actual prettify value from options #define PRETTIFY(converter) (converter.options - (converter.options & 248)) // query the options integer to check if the option is enables #define KEEP_COMMENT(converter) ((converter.options & SASS2SCSS_KEEP_COMMENT) == SASS2SCSS_KEEP_COMMENT) #define STRIP_COMMENT(converter) ((converter.options & SASS2SCSS_STRIP_COMMENT) == SASS2SCSS_STRIP_COMMENT) #define CONVERT_COMMENT(converter) ((converter.options & SASS2SCSS_CONVERT_COMMENT) == SASS2SCSS_CONVERT_COMMENT) // some makros to access the indentation stack #define INDENT(converter) (converter.indents.top()) // some makros to query comment parser status #define IS_PARSING(converter) (converter.comment == "") #define IS_COMMENT(converter) (converter.comment != "") #define IS_SRC_COMMENT(converter) (converter.comment == "//" && ! CONVERT_COMMENT(converter)) #define IS_CSS_COMMENT(converter) (converter.comment == "/*" || (converter.comment == "//" && CONVERT_COMMENT(converter))) // pretty printer helper function static std::string closer (const converter& converter) { return PRETTIFY(converter) == 0 ? " }" : PRETTIFY(converter) <= 1 ? " }" : "\n" + INDENT(converter) + "}"; } // pretty printer helper function static std::string opener (const converter& converter) { return PRETTIFY(converter) == 0 ? " { " : PRETTIFY(converter) <= 2 ? " {" : "\n" + INDENT(converter) + "{"; } // check if the given string is a pseudo selector // needed to differentiate from sass property syntax static bool isPseudoSelector (std::string& sel) { size_t len = sel.length(); if (len < 1) return false; size_t pos = sel.find_first_not_of("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1); if (pos != std::string::npos) sel.erase(pos, std::string::npos); size_t i = sel.length(); while (i -- > 0) { sel.at(i) = tolower(sel.at(i)); } // CSS Level 1 - Recommendation if (sel == ":link") return true; if (sel == ":visited") return true; if (sel == ":active") return true; // CSS Level 2 (Revision 1) - Recommendation if (sel == ":lang") return true; if (sel == ":first-child") return true; if (sel == ":hover") return true; if (sel == ":focus") return true; // disabled - also valid properties // if (sel == ":left") return true; // if (sel == ":right") return true; if (sel == ":first") return true; // Selectors Level 3 - Recommendation if (sel == ":target") return true; if (sel == ":root") return true; if (sel == ":nth-child") return true; if (sel == ":nth-last-of-child") return true; if (sel == ":nth-of-type") return true; if (sel == ":nth-last-of-type") return true; if (sel == ":last-child") return true; if (sel == ":first-of-type") return true; if (sel == ":last-of-type") return true; if (sel == ":only-child") return true; if (sel == ":only-of-type") return true; if (sel == ":empty") return true; if (sel == ":not") return true; // CSS Basic User Interface Module Level 3 - Working Draft if (sel == ":default") return true; if (sel == ":valid") return true; if (sel == ":invalid") return true; if (sel == ":in-range") return true; if (sel == ":out-of-range") return true; if (sel == ":required") return true; if (sel == ":optional") return true; if (sel == ":read-only") return true; if (sel == ":read-write") return true; if (sel == ":dir") return true; if (sel == ":enabled") return true; if (sel == ":disabled") return true; if (sel == ":checked") return true; if (sel == ":indeterminate") return true; if (sel == ":nth-last-child") return true; // Selectors Level 4 - Working Draft if (sel == ":any-link") return true; if (sel == ":local-link") return true; if (sel == ":scope") return true; if (sel == ":active-drop-target") return true; if (sel == ":valid-drop-target") return true; if (sel == ":invalid-drop-target") return true; if (sel == ":current") return true; if (sel == ":past") return true; if (sel == ":future") return true; if (sel == ":placeholder-shown") return true; if (sel == ":user-error") return true; if (sel == ":blank") return true; if (sel == ":nth-match") return true; if (sel == ":nth-last-match") return true; if (sel == ":nth-column") return true; if (sel == ":nth-last-column") return true; if (sel == ":matches") return true; // Fullscreen API - Living Standard if (sel == ":fullscreen") return true; // not a pseudo selector return false; } static size_t findFirstCharacter (std::string& sass, size_t pos) { return sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos); } static size_t findLastCharacter (std::string& sass, size_t pos) { return sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, pos); } static bool isUrl (std::string& sass, size_t pos) { return sass[pos] == 'u' && sass[pos+1] == 'r' && sass[pos+2] == 'l' && sass[pos+3] == '('; } // check if there is some char data // will ignore everything in comments static bool hasCharData (std::string& sass) { size_t col_pos = 0; while (true) { // try to find some meaningfull char col_pos = sass.find_first_not_of(" \t\n\v\f\r", col_pos); // there was no meaningfull char found if (col_pos == std::string::npos) return false; // found a multiline comment opener if (sass.substr(col_pos, 2) == "/*") { // find the multiline comment closer col_pos = sass.find("*/", col_pos); // maybe we did not find the closer here if (col_pos == std::string::npos) return false; // skip closer col_pos += 2; } else { return true; } } } // EO hasCharData // find src comment opener // correctly skips quoted strings static size_t findCommentOpener (std::string& sass) { size_t col_pos = 0; bool apoed = false; bool quoted = false; bool comment = false; size_t brackets = 0; while (col_pos != std::string::npos) { // process all interesting chars col_pos = sass.find_first_of("\"\'/\\*()", col_pos); // assertion for valid result if (col_pos != std::string::npos) { char character = sass.at(col_pos); if (character == '(') { if (!quoted && !apoed) brackets ++; } else if (character == ')') { if (!quoted && !apoed) brackets --; } else if (character == '\"') { // invert quote bool if (!apoed && !comment) quoted = !quoted; } else if (character == '\'') { // invert quote bool if (!quoted && !comment) apoed = !apoed; } else if (col_pos > 0 && character == '/') { if (sass.at(col_pos - 1) == '*') { comment = false; } // next needs to be a slash too else if (sass.at(col_pos - 1) == '/') { // only found if not in single or double quote, bracket or comment if (!quoted && !apoed && !comment && brackets == 0) return col_pos - 1; } } else if (character == '\\') { // skip next char if in quote if (quoted || apoed) col_pos ++; } // this might be a comment opener else if (col_pos > 0 && character == '*') { // opening a multiline comment if (sass.at(col_pos - 1) == '/') { // we are now in a comment if (!quoted && !apoed) comment = true; } } // skip char col_pos ++; } } // EO while return col_pos; } // EO findCommentOpener // remove multiline comments from sass string // correctly skips quoted strings static std::string removeMultilineComment (std::string &sass) { std::string clean = ""; size_t col_pos = 0; size_t open_pos = 0; size_t close_pos = 0; bool apoed = false; bool quoted = false; bool comment = false; // process sass til string end while (col_pos != std::string::npos) { // process all interesting chars col_pos = sass.find_first_of("\"\'/\\*", col_pos); // assertion for valid result if (col_pos != std::string::npos) { char character = sass.at(col_pos); // found quoted string delimiter if (character == '\"') { if (!apoed && !comment) quoted = !quoted; } else if (character == '\'') { if (!quoted && !comment) apoed = !apoed; } // found possible comment closer else if (character == '/') { // look back to see if it is actually a closer if (comment && col_pos > 0 && sass.at(col_pos - 1) == '*') { close_pos = col_pos + 1; comment = false; } } else if (character == '\\') { // skip escaped char if (quoted || apoed) col_pos ++; } // this might be a comment opener else if (character == '*') { // look back to see if it is actually an opener if (!quoted && !apoed && col_pos > 0 && sass.at(col_pos - 1) == '/') { comment = true; open_pos = col_pos - 1; clean += sass.substr(close_pos, open_pos - close_pos); } } // skip char col_pos ++; } } // EO while // add final parts (add half open comment text) if (comment) clean += sass.substr(open_pos); else clean += sass.substr(close_pos); // return string return clean; } // EO removeMultilineComment // right trim a given string std::string rtrim(const std::string &sass) { std::string trimmed = sass; size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r"); if (pos_ws != std::string::npos) { trimmed.erase(pos_ws + 1); } else { trimmed.clear(); } return trimmed; } // EO rtrim // flush whitespace and print additional text, but // only print additional chars and buffer whitespace std::string flush (std::string& sass, converter& converter) { // return flushed std::string scss = ""; // print whitespace buffer scss += PRETTIFY(converter) > 0 ? converter.whitespace : ""; // reset whitespace buffer converter.whitespace = ""; // remove possible newlines from string size_t pos_right = sass.find_last_not_of("\n\r"); if (pos_right == std::string::npos) return scss; // get the linefeeds from the string std::string lfs = sass.substr(pos_right + 1); sass = sass.substr(0, pos_right + 1); // find some source comment opener size_t comment_pos = findCommentOpener(sass); // check if there was a source comment if (comment_pos != std::string::npos) { // convert comment (but only outside other coments) if (CONVERT_COMMENT(converter) && !IS_COMMENT(converter)) { // convert to multiline comment sass.at(comment_pos + 1) = '*'; // add comment node to the whitespace sass += " */"; } // not at line start if (comment_pos > 0) { // also include whitespace before the actual comment opener size_t ws_pos = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, comment_pos - 1); comment_pos = ws_pos == std::string::npos ? 0 : ws_pos + 1; } if (!STRIP_COMMENT(converter)) { // add comment node to the whitespace converter.whitespace += sass.substr(comment_pos); } else { // sass = removeMultilineComments(sass); } // update the actual sass code sass = sass.substr(0, comment_pos); } // add newline as getline discharged it converter.whitespace += lfs + "\n"; // maybe remove any leading whitespace if (PRETTIFY(converter) == 0) { // remove leading whitespace and update string size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE); if (pos_left != std::string::npos) sass = sass.substr(pos_left); } // add flushed data scss += sass; // return string return scss; } // EO flush // process a line of the sass text std::string process (std::string& sass, converter& converter) { // resulting string std::string scss = ""; // strip multi line comments if (STRIP_COMMENT(converter)) { sass = removeMultilineComment(sass); } // right trim input sass = rtrim(sass); // get position of first meaningfull character in string size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE); // special case for final run if (converter.end_of_file) pos_left = 0; // maybe has only whitespace if (pos_left == std::string::npos) { // just add complete whitespace converter.whitespace += sass + "\n"; } // have meaningfull first char else { // extract and store indentation string std::string indent = sass.substr(0, pos_left); // check if current line starts a comment std::string open = sass.substr(pos_left, 2); // line has less or same indentation // finalize previous open parser context if (indent.length() <= INDENT(converter).length()) { // close multilinie comment if (IS_CSS_COMMENT(converter)) { // check if comments will be stripped anyway if (!STRIP_COMMENT(converter)) scss += " */"; } // close src comment comment else if (IS_SRC_COMMENT(converter)) { // add a newline to avoid closer on same line // this would put the bracket in the comment node // no longer needed since we parse them correctly // if (KEEP_COMMENT(converter)) scss += "\n"; } // close css properties else if (converter.property) { // add closer unless in concat mode if (!converter.comma) { // if there was no colon we have a selector // looks like there were no inner properties if (converter.selector) scss += " {}"; // add final semicolon else if (!converter.semicolon) scss += ";"; } } // reset comment state converter.comment = ""; } // make sure we close every "higher" block while (indent.length() < INDENT(converter).length()) { // pop stacked context converter.indents.pop(); // print close bracket if (IS_PARSING(converter)) { scss += closer(converter); } else { scss += " */"; } // reset comment state converter.comment = ""; } // reset converter state converter.selector = false; // looks like some undocumented behavior ... // https://github.com/mgreter/sass2scss/issues/29 if (sass.substr(pos_left, 1) == "\\") { converter.selector = true; sass[pos_left] = ' '; } // check if we have sass property syntax if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::") { // default to a selector // change back if property found converter.selector = true; // get position of first whitespace char size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left); // assertion check for valid result if (pos_wspace != std::string::npos) { // get the possible pseudo selector std::string pseudo = sass.substr(pos_left, pos_wspace - pos_left); // get position of the first real property value char // pseudo selectors get this far, but have no actual value size_t pos_value = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_wspace); // assertion check for valid result if (pos_value != std::string::npos) { // only process if not (fallowed by a semicolon or is a pseudo selector) if (!(sass.at(pos_value) == ':' || isPseudoSelector(pseudo))) { // create new string by interchanging the colon sign for property and value sass = indent + sass.substr(pos_left + 1, pos_wspace - pos_left - 1) + ":" + sass.substr(pos_wspace); // try to find a colon in the current line, but only ... size_t pos_colon = sass.find_first_not_of(":", pos_left); // assertion for valid result if (pos_colon != std::string::npos) { // ... after the first word (skip beginning colons) pos_colon = sass.find_first_of(":", pos_colon); // it is a selector if there was no colon found converter.selector = pos_colon == std::string::npos; } } } } // check if we have a BEM property (one colon and no selector) if (sass.substr(pos_left, 1) == ":" && converter.selector == true) { size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left); sass = indent + sass.substr(pos_left + 1, pos_wspace) + ":"; } } // terminate some statements immediately else if ( sass.substr(pos_left, 5) == "@warn" || sass.substr(pos_left, 6) == "@debug" || sass.substr(pos_left, 6) == "@error" || sass.substr(pos_left, 6) == "@value" || sass.substr(pos_left, 8) == "@charset" || sass.substr(pos_left, 10) == "@namespace" ) { sass = indent + sass.substr(pos_left); } // replace some specific sass shorthand directives (if not fallowed by a white space character) else if (sass.substr(pos_left, 1) == "=") { sass = indent + "@mixin " + sass.substr(pos_left + 1); } else if (sass.substr(pos_left, 1) == "+") { // must be followed by a mixin call (no whitespace afterwards or at ending directly) if (sass[pos_left+1] != 0 && sass[pos_left+1] != ' ' && sass[pos_left+1] != '\t') { sass = indent + "@include " + sass.substr(pos_left + 1); } } // add quotes for import if needed else if (sass.substr(pos_left, 7) == "@import") { // get positions for the actual import url size_t pos_import = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left + 7); size_t pos = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import); size_t start = pos; bool in_dqstr = false; bool in_sqstr = false; bool is_escaped = false; do { if (is_escaped) { is_escaped = false; } else if (sass[pos] == '\\') { is_escaped = true; } else if (sass[pos] == '"') { if (!in_sqstr) in_dqstr = ! in_dqstr; } else if (sass[pos] == '\'') { if (!in_dqstr) in_sqstr = ! in_sqstr; } else if (in_dqstr || in_sqstr) { // skip over quoted stuff } else if (sass[pos] == ',' || sass[pos] == 0) { if (sass[start] != '"' && sass[start] != '\'' && !isUrl(sass, start)) { size_t end = findLastCharacter(sass, pos - 1) + 1; sass = sass.replace(end, 0, "\""); sass = sass.replace(start, 0, "\""); pos += 2; } start = findFirstCharacter(sass, pos + 1); } } while (sass[pos++] != 0); } else if ( sass.substr(pos_left, 7) != "@return" && sass.substr(pos_left, 7) != "@extend" && sass.substr(pos_left, 8) != "@include" && sass.substr(pos_left, 8) != "@content" ) { // probably a selector anyway converter.selector = true; // try to find first colon in the current line size_t pos_colon = sass.find_first_of(":", pos_left); // assertion that we have a colon if (pos_colon != std::string::npos) { // it is not a selector if we have a space after a colon if (sass[pos_colon+1] == ' ') converter.selector = false; if (sass[pos_colon+1] == ' ') converter.selector = false; } } // current line has more indentation if (indent.length() >= INDENT(converter).length()) { // not in comment mode if (IS_PARSING(converter)) { // has meaningfull chars if (hasCharData(sass)) { // is probably a property // also true for selectors converter.property = true; } } } // current line has more indentation if (indent.length() > INDENT(converter).length()) { // not in comment mode if (IS_PARSING(converter)) { // had meaningfull chars if (converter.property) { // print block opener scss += opener(converter); // push new stack context converter.indents.push(""); // store block indentation INDENT(converter) = indent; } } // is and will be a src comment else if (!IS_CSS_COMMENT(converter)) { // scss does not allow multiline src comments // therefore add forward slashes to all lines sass.at(INDENT(converter).length()+0) = '/'; // there is an edge case here if indentation // is minimal (will overwrite the fist char) sass.at(INDENT(converter).length()+1) = '/'; // could code around that, but I dont' think // this will ever be the cause for any trouble } } // line is opening a new comment if (open == "/*" || open == "//") { // reset the property state converter.property = false; // close previous comment if (IS_CSS_COMMENT(converter) && open != "") { if (!STRIP_COMMENT(converter) && !CONVERT_COMMENT(converter)) scss += " */"; } // force single line comments // into a correct css comment if (CONVERT_COMMENT(converter)) { if (IS_PARSING(converter)) { sass.at(pos_left + 1) = '*'; } } // set comment flag converter.comment = open; } // flush data only under certain conditions if (!( // strip css and src comments if option is set (IS_COMMENT(converter) && STRIP_COMMENT(converter)) || // strip src comment even if strip option is not set // but only if the keep src comment option is not set (IS_SRC_COMMENT(converter) && ! KEEP_COMMENT(converter)) )) { // flush data and buffer whitespace scss += flush(sass, converter); } // get position of last meaningfull char size_t pos_right = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE); // check for invalid result if (pos_right != std::string::npos) { // get the last meaningfull char std::string close = sass.substr(pos_right, 1); // check if next line should be concatenated (list mode) converter.comma = IS_PARSING(converter) && close == ","; converter.semicolon = IS_PARSING(converter) && close == ";"; // check if we have more than // one meaningfull char if (pos_right > 0) { // get the last two chars from string std::string close = sass.substr(pos_right - 1, 2); // update parser status for expicitly closed comment if (close == "*/") converter.comment = ""; } } // EO have meaningfull chars from end } // EO have meaningfull chars from start // return scss return scss; } // EO process // read line with either CR, LF or CR LF format // http://stackoverflow.com/a/6089413/1550314 static std::istream& safeGetline(std::istream& is, std::string& t) { t.clear(); // The characters in the stream are read one-by-one using a std::streambuf. // That is faster than reading them one-by-one using the std::istream. // Code that uses streambuf this way must be guarded by a sentry object. // The sentry object performs various tasks, // such as thread synchronization and updating the stream state. std::istream::sentry se(is, true); std::streambuf* sb = is.rdbuf(); for(;;) { int c = sb->sbumpc(); switch (c) { case '\n': return is; case '\r': if(sb->sgetc() == '\n') sb->sbumpc(); return is; case EOF: // Also handle the case when the last line has no line ending if(t.empty()) is.setstate(std::ios::eofbit); return is; default: t += (char)c; } } } // the main converter function for c++ char* sass2scss (const std::string& sass, const int options) { // local variables std::string line; std::string scss = ""; std::stringstream stream(sass); // create converter variable converter converter; // initialise all options converter.comma = false; converter.property = false; converter.selector = false; converter.semicolon = false; converter.end_of_file = false; converter.comment = ""; converter.whitespace = ""; converter.indents.push(""); converter.options = options; // read line by line and process them while(safeGetline(stream, line) && !stream.eof()) { scss += process(line, converter); } // create mutable string std::string closer = ""; // set the end of file flag converter.end_of_file = true; // process to close all open blocks scss += process(closer, converter); // allocate new memory on the heap // caller has to free it after use char * cstr = (char*) malloc (scss.length() + 1); // create a copy of the string strcpy (cstr, scss.c_str()); // return pointer return &cstr[0]; } // EO sass2scss } // EO namespace // implement for c extern "C" { char* ADDCALL sass2scss (const char* sass, const int options) { return Sass::sass2scss(sass, options); } // Get compiled sass2scss version const char* ADDCALL sass2scss_version(void) { return SASS2SCSS_VERSION; } } golibsass-1.0.0/libsass_src/src/sass_context.cpp000066400000000000000000000677121405214413600220320ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "sass_functions.hpp" #include "json.hpp" #define LFEED "\n" // C++ helper namespace Sass { // see sass_copy_c_string(sass::string str) static inline JsonNode* json_mkstream(const sass::ostream& stream) { // hold on to string on stack! sass::string str(stream.str()); return json_mkstring(str.c_str()); } static void handle_string_error(Sass_Context* c_ctx, const sass::string& msg, int severety) { sass::ostream msg_stream; JsonNode* json_err = json_mkobject(); msg_stream << "Internal Error: " << msg << std::endl; json_append_member(json_err, "status", json_mknumber(severety)); json_append_member(json_err, "message", json_mkstring(msg.c_str())); json_append_member(json_err, "formatted", json_mkstream(msg_stream)); try { c_ctx->error_json = json_stringify(json_err, " "); } catch (...) {} c_ctx->error_message = sass_copy_string(msg_stream.str()); c_ctx->error_text = sass_copy_c_string(msg.c_str()); c_ctx->error_status = severety; c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } static int handle_error(Sass_Context* c_ctx) { try { throw; } catch (Exception::Base& e) { sass::ostream msg_stream; sass::string cwd(Sass::File::get_cwd()); sass::string msg_prefix(e.errtype()); bool got_newline = false; msg_stream << msg_prefix << ": "; const char* msg = e.what(); while (msg && *msg) { if (*msg == '\r') { got_newline = true; } else if (*msg == '\n') { got_newline = true; } else if (got_newline) { msg_stream << sass::string(msg_prefix.size() + 2, ' '); got_newline = false; } msg_stream << *msg; ++msg; } if (!got_newline) msg_stream << "\n"; if (e.traces.empty()) { // we normally should have some traces, still here as a fallback sass::string rel_path(Sass::File::abs2rel(e.pstate.getPath(), cwd, cwd)); msg_stream << sass::string(msg_prefix.size() + 2, ' '); msg_stream << " on line " << e.pstate.getLine() << " of " << rel_path << "\n"; } else { sass::string rel_path(Sass::File::abs2rel(e.pstate.getPath(), cwd, cwd)); msg_stream << traces_to_string(e.traces, " "); } // now create the code trace (ToDo: maybe have util functions?) if (e.pstate.position.line != sass::string::npos && e.pstate.position.column != sass::string::npos && e.pstate.getRawData() != nullptr && e.pstate.source != nullptr) { Offset offset(e.pstate.position); size_t lines = offset.line; // scan through src until target line // move line_beg pointer to line start const char* line_beg; for (line_beg = e.pstate.getRawData(); *line_beg != '\0'; ++line_beg) { if (lines == 0) break; if (*line_beg == '\n') --lines; } // move line_end before next newline character const char* line_end; for (line_end = line_beg; *line_end != '\0'; ++line_end) { if (*line_end == '\n' || *line_end == '\r') break; } if (*line_end != '\0') ++line_end; size_t line_len = line_end - line_beg; size_t move_in = 0; size_t shorten = 0; size_t left_chars = 42; size_t max_chars = 76; // reported excerpt should not exceed `max_chars` chars if (offset.column > line_len) left_chars = offset.column; if (offset.column > left_chars) move_in = offset.column - left_chars; if (line_len > max_chars + move_in) shorten = line_len - move_in - max_chars; utf8::advance(line_beg, move_in, line_end); utf8::retreat(line_end, shorten, line_beg); sass::string sanitized; sass::string marker(offset.column - move_in, '-'); utf8::replace_invalid(line_beg, line_end, std::back_inserter(sanitized)); msg_stream << ">> " << sanitized << "\n"; msg_stream << " " << marker << "^\n"; } JsonNode* json_err = json_mkobject(); json_append_member(json_err, "status", json_mknumber(1)); json_append_member(json_err, "file", json_mkstring(e.pstate.getPath())); json_append_member(json_err, "line", json_mknumber((double)(e.pstate.getLine()))); json_append_member(json_err, "column", json_mknumber((double)(e.pstate.getColumn()))); json_append_member(json_err, "message", json_mkstring(e.what())); json_append_member(json_err, "formatted", json_mkstream(msg_stream)); try { c_ctx->error_json = json_stringify(json_err, " "); } catch (...) {} // silently ignore this error? c_ctx->error_message = sass_copy_string(msg_stream.str()); c_ctx->error_text = sass_copy_c_string(e.what()); c_ctx->error_status = 1; c_ctx->error_file = sass_copy_c_string(e.pstate.getPath()); c_ctx->error_line = e.pstate.getLine(); c_ctx->error_column = e.pstate.getColumn(); c_ctx->error_src = sass_copy_c_string(e.pstate.getRawData()); c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } catch (std::bad_alloc& ba) { sass::ostream msg_stream; msg_stream << "Unable to allocate memory: " << ba.what(); handle_string_error(c_ctx, msg_stream.str(), 2); } catch (std::exception& e) { handle_string_error(c_ctx, e.what(), 3); } catch (sass::string& e) { handle_string_error(c_ctx, e, 4); } catch (const char* e) { handle_string_error(c_ctx, e, 4); } catch (...) { handle_string_error(c_ctx, "unknown", 5); } return c_ctx->error_status; } // allow one error handler to throw another error // this can happen with invalid utf8 and json lib static int handle_errors(Sass_Context* c_ctx) { try { return handle_error(c_ctx); } catch (...) { return handle_error(c_ctx); } } static Block_Obj sass_parse_block(Sass_Compiler* compiler) throw() { // assert valid pointer if (compiler == 0) return {}; // The cpp context must be set by now Context* cpp_ctx = compiler->cpp_ctx; Sass_Context* c_ctx = compiler->c_ctx; // We will take care to wire up the rest compiler->cpp_ctx->c_compiler = compiler; compiler->state = SASS_COMPILER_PARSED; try { // get input/output path from options sass::string input_path = safe_str(c_ctx->input_path); sass::string output_path = safe_str(c_ctx->output_path); // maybe skip some entries of included files // we do not include stdin for data contexts bool skip = c_ctx->type == SASS_CONTEXT_DATA; // dispatch parse call Block_Obj root(cpp_ctx->parse()); // abort on errors if (!root) return {}; // skip all prefixed files? (ToDo: check srcmap) // IMO source-maps should point to headers already // therefore don't skip it for now. re-enable or // remove completely once this is tested size_t headers = cpp_ctx->head_imports; // copy the included files on to the context (dont forget to free later) if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL) throw(std::bad_alloc()); // return parsed block return root; } // pass errors to generic error handler catch (...) { handle_errors(c_ctx); } // error return {}; } } extern "C" { using namespace Sass; static void sass_clear_options (struct Sass_Options* options); static void sass_reset_options (struct Sass_Options* options); static void copy_options(struct Sass_Options* to, struct Sass_Options* from) { // do not overwrite ourself if (to == from) return; // free assigned memory sass_clear_options(to); // move memory *to = *from; // Reset pointers on source sass_reset_options(from); } #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; } #define IMPLEMENT_SASS_OPTION_STRING_GETTER(type, option, def) \ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } #define IMPLEMENT_SASS_OPTION_STRING_SETTER(type, option, def) \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \ { free(options->option); options->option = option || def ? sass_copy_c_string(option ? option : def) : 0; } #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \ IMPLEMENT_SASS_OPTION_STRING_GETTER(type, option, def) \ IMPLEMENT_SASS_OPTION_STRING_SETTER(type, option, def) #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \ type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; } #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \ type sass_context_take_##option (struct Sass_Context* ctx) \ { type foo = ctx->option; ctx->option = 0; return foo; } // generic compilation function (not exported, use file/data compile instead) static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw() { try { // register our custom functions if (c_ctx->c_functions) { auto this_func_data = c_ctx->c_functions; while (this_func_data && *this_func_data) { cpp_ctx->add_c_function(*this_func_data); ++this_func_data; } } // register our custom headers if (c_ctx->c_headers) { auto this_head_data = c_ctx->c_headers; while (this_head_data && *this_head_data) { cpp_ctx->add_c_header(*this_head_data); ++this_head_data; } } // register our custom importers if (c_ctx->c_importers) { auto this_imp_data = c_ctx->c_importers; while (this_imp_data && *this_imp_data) { cpp_ctx->add_c_importer(*this_imp_data); ++this_imp_data; } } // reset error status c_ctx->error_json = 0; c_ctx->error_text = 0; c_ctx->error_message = 0; c_ctx->error_status = 0; // reset error position c_ctx->error_file = 0; c_ctx->error_src = 0; c_ctx->error_line = sass::string::npos; c_ctx->error_column = sass::string::npos; // allocate a new compiler instance void* ctxmem = calloc(1, sizeof(struct Sass_Compiler)); if (ctxmem == 0) { std::cerr << "Error allocating memory for context" << std::endl; return 0; } Sass_Compiler* compiler = (struct Sass_Compiler*) ctxmem; compiler->state = SASS_COMPILER_CREATED; // store in sass compiler compiler->c_ctx = c_ctx; compiler->cpp_ctx = cpp_ctx; cpp_ctx->c_compiler = compiler; // use to parse block return compiler; } // pass errors to generic error handler catch (...) { handle_errors(c_ctx); } // error return 0; } // generic compilation function (not exported, use file/data compile instead) static int sass_compile_context (Sass_Context* c_ctx, Context* cpp_ctx) { // prepare sass compiler with context and options Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_ctx); try { // call each compiler step sass_compiler_parse(compiler); sass_compiler_execute(compiler); } // pass errors to generic error handler catch (...) { handle_errors(c_ctx); } sass_delete_compiler(compiler); return c_ctx->error_status; } inline void init_options (struct Sass_Options* options) { options->precision = 10; options->indent = " "; options->linefeed = LFEED; } Sass_Options* ADDCALL sass_make_options (void) { struct Sass_Options* options = (struct Sass_Options*) calloc(1, sizeof(struct Sass_Options)); if (options == 0) { std::cerr << "Error allocating memory for options" << std::endl; return 0; } init_options(options); return options; } Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path) { #ifdef DEBUG_SHARED_PTR SharedObj::setTaint(true); #endif struct Sass_File_Context* ctx = (struct Sass_File_Context*) calloc(1, sizeof(struct Sass_File_Context)); if (ctx == 0) { std::cerr << "Error allocating memory for file context" << std::endl; return 0; } ctx->type = SASS_CONTEXT_FILE; init_options(ctx); try { if (input_path == 0) { throw(std::runtime_error("File context created without an input path")); } if (*input_path == 0) { throw(std::runtime_error("File context created with empty input path")); } sass_option_set_input_path(ctx, input_path); } catch (...) { handle_errors(ctx); } return ctx; } Sass_Data_Context* ADDCALL sass_make_data_context(char* source_string) { #ifdef DEBUG_SHARED_PTR SharedObj::setTaint(true); #endif struct Sass_Data_Context* ctx = (struct Sass_Data_Context*) calloc(1, sizeof(struct Sass_Data_Context)); if (ctx == 0) { std::cerr << "Error allocating memory for data context" << std::endl; return 0; } ctx->type = SASS_CONTEXT_DATA; init_options(ctx); try { if (source_string == 0) { throw(std::runtime_error("Data context created without a source string")); } if (*source_string == 0) { throw(std::runtime_error("Data context created with empty source string")); } ctx->source_string = source_string; } catch (...) { handle_errors(ctx); } return ctx; } struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 0; Context* cpp_ctx = new Data_Context(*data_ctx); return sass_prepare_context(data_ctx, cpp_ctx); } struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx) { if (file_ctx == 0) return 0; Context* cpp_ctx = new File_Context(*file_ctx); return sass_prepare_context(file_ctx, cpp_ctx); } int ADDCALL sass_compile_data_context(Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 1; if (data_ctx->error_status) return data_ctx->error_status; try { if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); } // empty source string is a valid case, even if not really useful (different than with file context) // if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } } catch (...) { return handle_errors(data_ctx) | 1; } Context* cpp_ctx = new Data_Context(*data_ctx); return sass_compile_context(data_ctx, cpp_ctx); } int ADDCALL sass_compile_file_context(Sass_File_Context* file_ctx) { if (file_ctx == 0) return 1; if (file_ctx->error_status) return file_ctx->error_status; try { if (file_ctx->input_path == 0) { throw(std::runtime_error("File context has no input path")); } if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); } } catch (...) { return handle_errors(file_ctx) | 1; } Context* cpp_ctx = new File_Context(*file_ctx); return sass_compile_context(file_ctx, cpp_ctx); } int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler) { if (compiler == 0) return 1; if (compiler->state == SASS_COMPILER_PARSED) return 0; if (compiler->state != SASS_COMPILER_CREATED) return -1; if (compiler->c_ctx == NULL) return 1; if (compiler->cpp_ctx == NULL) return 1; if (compiler->c_ctx->error_status) return compiler->c_ctx->error_status; // parse the context we have set up (file or data) compiler->root = sass_parse_block(compiler); // success return 0; } int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler) { if (compiler == 0) return 1; if (compiler->state == SASS_COMPILER_EXECUTED) return 0; if (compiler->state != SASS_COMPILER_PARSED) return -1; if (compiler->c_ctx == NULL) return 1; if (compiler->cpp_ctx == NULL) return 1; if (compiler->root.isNull()) return 1; if (compiler->c_ctx->error_status) return compiler->c_ctx->error_status; compiler->state = SASS_COMPILER_EXECUTED; Context* cpp_ctx = compiler->cpp_ctx; Block_Obj root = compiler->root; // compile the parsed root block try { compiler->c_ctx->output_string = cpp_ctx->render(root); } // pass catched errors to generic error handler catch (...) { return handle_errors(compiler->c_ctx) | 1; } // generate source map json and store on context compiler->c_ctx->source_map_string = cpp_ctx->render_srcmap(); // success return 0; } // helper function, not exported, only accessible locally static void sass_reset_options (struct Sass_Options* options) { // free pointer before // or copy/move them options->input_path = 0; options->output_path = 0; options->plugin_path = 0; options->include_path = 0; options->source_map_file = 0; options->source_map_root = 0; options->c_functions = 0; options->c_importers = 0; options->c_headers = 0; options->plugin_paths = 0; options->include_paths = 0; } // helper function, not exported, only accessible locally static void sass_clear_options (struct Sass_Options* options) { if (options == 0) return; // Deallocate custom functions, headers and imports sass_delete_function_list(options->c_functions); sass_delete_importer_list(options->c_importers); sass_delete_importer_list(options->c_headers); // Deallocate inc paths if (options->plugin_paths) { struct string_list* cur; struct string_list* next; cur = options->plugin_paths; while (cur) { next = cur->next; free(cur->string); free(cur); cur = next; } } // Deallocate inc paths if (options->include_paths) { struct string_list* cur; struct string_list* next; cur = options->include_paths; while (cur) { next = cur->next; free(cur->string); free(cur); cur = next; } } // Free options strings free(options->input_path); free(options->output_path); free(options->plugin_path); free(options->include_path); free(options->source_map_file); free(options->source_map_root); // Reset our pointers options->input_path = 0; options->output_path = 0; options->plugin_path = 0; options->include_path = 0; options->source_map_file = 0; options->source_map_root = 0; options->c_functions = 0; options->c_importers = 0; options->c_headers = 0; options->plugin_paths = 0; options->include_paths = 0; } // helper function, not exported, only accessible locally // sass_free_context is also defined in old sass_interface static void sass_clear_context (struct Sass_Context* ctx) { if (ctx == 0) return; // release the allocated memory (mostly via sass_copy_c_string) if (ctx->output_string) free(ctx->output_string); if (ctx->source_map_string) free(ctx->source_map_string); if (ctx->error_message) free(ctx->error_message); if (ctx->error_text) free(ctx->error_text); if (ctx->error_json) free(ctx->error_json); if (ctx->error_file) free(ctx->error_file); if (ctx->error_src) free(ctx->error_src); free_string_array(ctx->included_files); // play safe and reset properties ctx->output_string = 0; ctx->source_map_string = 0; ctx->error_message = 0; ctx->error_text = 0; ctx->error_json = 0; ctx->error_file = 0; ctx->error_src = 0; ctx->included_files = 0; // debug leaked memory #ifdef DEBUG_SHARED_PTR SharedObj::dumpMemLeaks(); #endif // now clear the options sass_clear_options(ctx); } void ADDCALL sass_delete_compiler (struct Sass_Compiler* compiler) { if (compiler == 0) { return; } Context* cpp_ctx = compiler->cpp_ctx; if (cpp_ctx) delete(cpp_ctx); compiler->cpp_ctx = NULL; compiler->c_ctx = NULL; compiler->root = {}; free(compiler); } void ADDCALL sass_delete_options (struct Sass_Options* options) { sass_clear_options(options); free(options); } // Deallocate all associated memory with file context void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx) { // clear the context and free it sass_clear_context(ctx); free(ctx); } // Deallocate all associated memory with data context void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx) { // clean the source string if it was not passed // we reset this member once we start parsing if (ctx->source_string) free(ctx->source_string); // clear the context and free it sass_clear_context(ctx); free(ctx); } // Getters for sass context from specific implementations struct Sass_Context* ADDCALL sass_file_context_get_context(struct Sass_File_Context* ctx) { return ctx; } struct Sass_Context* ADDCALL sass_data_context_get_context(struct Sass_Data_Context* ctx) { return ctx; } // Getters for context options from Sass_Context struct Sass_Options* ADDCALL sass_context_get_options(struct Sass_Context* ctx) { return ctx; } struct Sass_Options* ADDCALL sass_file_context_get_options(struct Sass_File_Context* ctx) { return ctx; } struct Sass_Options* ADDCALL sass_data_context_get_options(struct Sass_Data_Context* ctx) { return ctx; } void ADDCALL sass_file_context_set_options (struct Sass_File_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); } void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); } // Getters for Sass_Compiler options (get connected sass context) enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler) { return compiler->state; } struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler) { return compiler->c_ctx; } struct Sass_Options* ADDCALL sass_compiler_get_options(struct Sass_Compiler* compiler) { return compiler->c_ctx; } // Getters for Sass_Compiler options (query import stack) size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.size(); } Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.back(); } Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx) { return compiler->cpp_ctx->import_stack[idx]; } // Getters for Sass_Compiler options (query function stack) size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->callee_stack.size(); } Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler) { return &compiler->cpp_ctx->callee_stack.back(); } Sass_Callee_Entry ADDCALL sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx) { return &compiler->cpp_ctx->callee_stack[idx]; } // Calculate the size of the stored null terminated array size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx) { size_t l = 0; auto i = ctx->included_files; while (i && *i) { ++i; ++l; } return l; } // Create getter and setters for options IMPLEMENT_SASS_OPTION_ACCESSOR(int, precision); IMPLEMENT_SASS_OPTION_ACCESSOR(enum Sass_Output_Style, output_style); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_comments); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_embed); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_contents); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_file_urls); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, omit_source_map_url); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src); IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Function_List, c_functions); IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_importers); IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_headers); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed); IMPLEMENT_SASS_OPTION_STRING_SETTER(const char*, plugin_path, 0); IMPLEMENT_SASS_OPTION_STRING_SETTER(const char*, include_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root, 0); // Create getter and setters for context IMPLEMENT_SASS_CONTEXT_GETTER(int, error_status); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_json); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_message); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_text); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_file); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_src); IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_line); IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_column); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, output_string); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, source_map_string); IMPLEMENT_SASS_CONTEXT_GETTER(char**, included_files); // Take ownership of memory (value on context is set to 0) IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_json); IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_message); IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_text); IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_file); IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_src); IMPLEMENT_SASS_CONTEXT_TAKER(char*, output_string); IMPLEMENT_SASS_CONTEXT_TAKER(char*, source_map_string); IMPLEMENT_SASS_CONTEXT_TAKER(char**, included_files); // Push function for include paths (no manipulation support for now) void ADDCALL sass_option_push_include_path(struct Sass_Options* options, const char* path) { struct string_list* include_path = (struct string_list*) calloc(1, sizeof(struct string_list)); if (include_path == 0) return; include_path->string = path ? sass_copy_c_string(path) : 0; struct string_list* last = options->include_paths; if (!options->include_paths) { options->include_paths = include_path; } else { while (last->next) last = last->next; last->next = include_path; } } // Push function for include paths (no manipulation support for now) size_t ADDCALL sass_option_get_include_path_size(struct Sass_Options* options) { size_t len = 0; struct string_list* cur = options->include_paths; while (cur) { len ++; cur = cur->next; } return len; } // Push function for include paths (no manipulation support for now) const char* ADDCALL sass_option_get_include_path(struct Sass_Options* options, size_t i) { struct string_list* cur = options->include_paths; while (i) { i--; cur = cur->next; } return cur->string; } // Push function for plugin paths (no manipulation support for now) size_t ADDCALL sass_option_get_plugin_path_size(struct Sass_Options* options) { size_t len = 0; struct string_list* cur = options->plugin_paths; while (cur) { len++; cur = cur->next; } return len; } // Push function for plugin paths (no manipulation support for now) const char* ADDCALL sass_option_get_plugin_path(struct Sass_Options* options, size_t i) { struct string_list* cur = options->plugin_paths; while (i) { i--; cur = cur->next; } return cur->string; } // Push function for plugin paths (no manipulation support for now) void ADDCALL sass_option_push_plugin_path(struct Sass_Options* options, const char* path) { struct string_list* plugin_path = (struct string_list*) calloc(1, sizeof(struct string_list)); if (plugin_path == 0) return; plugin_path->string = path ? sass_copy_c_string(path) : 0; struct string_list* last = options->plugin_paths; if (!options->plugin_paths) { options->plugin_paths = plugin_path; } else { while (last->next) last = last->next; last->next = plugin_path; } } } golibsass-1.0.0/libsass_src/src/sass_context.hpp000066400000000000000000000053521405214413600220270ustar00rootroot00000000000000#ifndef SASS_SASS_CONTEXT_H #define SASS_SASS_CONTEXT_H #include "sass/base.h" #include "sass/context.h" #include "ast_fwd_decl.hpp" // sass config options structure struct Sass_Options : Sass_Output_Options { // embed sourceMappingUrl as data uri bool source_map_embed; // embed include contents in maps bool source_map_contents; // create file urls for sources bool source_map_file_urls; // Disable sourceMappingUrl in css output bool omit_source_map_url; // Treat source_string as sass (as opposed to scss) bool is_indented_syntax_src; // The input path is used for source map // generation. It can be used to define // something with string compilation or to // overload the input file path. It is // set to "stdin" for data contexts and // to the input file on file contexts. char* input_path; // The output path is used for source map // generation. LibSass will not write to // this file, it is just used to create // information in source-maps etc. char* output_path; // Colon-separated list of paths // Semicolon-separated on Windows // Maybe use array interface instead? char* include_path; char* plugin_path; // Include paths (linked string list) struct string_list* include_paths; // Plugin paths (linked string list) struct string_list* plugin_paths; // Path to source map file // Enables source map generation // Used to create sourceMappingUrl char* source_map_file; // Directly inserted in source maps char* source_map_root; // Custom functions that can be called from sccs code Sass_Function_List c_functions; // List of custom importers Sass_Importer_List c_importers; // List of custom headers Sass_Importer_List c_headers; }; // base for all contexts struct Sass_Context : Sass_Options { // store context type info enum Sass_Input_Style type; // generated output data char* output_string; // generated source map json char* source_map_string; // error status int error_status; char* error_json; char* error_text; char* error_message; // error position char* error_file; size_t error_line; size_t error_column; char* error_src; // report imported files char** included_files; }; // struct for file compilation struct Sass_File_Context : Sass_Context { // no additional fields required // input_path is already on options }; // struct for data compilation struct Sass_Data_Context : Sass_Context { // provided source string char* source_string; char* srcmap_string; }; // link c and cpp context struct Sass_Compiler { // progress status Sass_Compiler_State state; // original c context Sass_Context* c_ctx; // Sass::Context Sass::Context* cpp_ctx; // Sass::Block Sass::Block_Obj root; }; #endif golibsass-1.0.0/libsass_src/src/sass_functions.cpp000066400000000000000000000205321405214413600223430ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include "util.hpp" #include "context.hpp" #include "values.hpp" #include "sass/functions.h" #include "sass_functions.hpp" extern "C" { using namespace Sass; Sass_Function_List ADDCALL sass_make_function_list(size_t length) { return (Sass_Function_List) calloc(length + 1, sizeof(Sass_Function_Entry)); } Sass_Function_Entry ADDCALL sass_make_function(const char* signature, Sass_Function_Fn function, void* cookie) { Sass_Function_Entry cb = (Sass_Function_Entry) calloc(1, sizeof(Sass_Function)); if (cb == 0) return 0; cb->signature = sass_copy_c_string(signature); cb->function = function; cb->cookie = cookie; return cb; } void ADDCALL sass_delete_function(Sass_Function_Entry entry) { free(entry->signature); free(entry); } // Deallocator for the allocated memory void ADDCALL sass_delete_function_list(Sass_Function_List list) { Sass_Function_List it = list; if (list == 0) return; while(*list) { sass_delete_function(*list); ++list; } free(it); } // Setters and getters for callbacks on function lists Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos) { return list[pos]; } void sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb) { list[pos] = cb; } const char* ADDCALL sass_function_get_signature(Sass_Function_Entry cb) { return cb->signature; } Sass_Function_Fn ADDCALL sass_function_get_function(Sass_Function_Entry cb) { return cb->function; } void* ADDCALL sass_function_get_cookie(Sass_Function_Entry cb) { return cb->cookie; } Sass_Importer_Entry ADDCALL sass_make_importer(Sass_Importer_Fn importer, double priority, void* cookie) { Sass_Importer_Entry cb = (Sass_Importer_Entry) calloc(1, sizeof(Sass_Importer)); if (cb == 0) return 0; cb->importer = importer; cb->priority = priority; cb->cookie = cookie; return cb; } Sass_Importer_Fn ADDCALL sass_importer_get_function(Sass_Importer_Entry cb) { return cb->importer; } double ADDCALL sass_importer_get_priority (Sass_Importer_Entry cb) { return cb->priority; } void* ADDCALL sass_importer_get_cookie(Sass_Importer_Entry cb) { return cb->cookie; } // Just in case we have some stray import structs void ADDCALL sass_delete_importer (Sass_Importer_Entry cb) { free(cb); } // Creator for sass custom importer function list Sass_Importer_List ADDCALL sass_make_importer_list(size_t length) { return (Sass_Importer_List) calloc(length + 1, sizeof(Sass_Importer_Entry)); } // Deallocator for the allocated memory void ADDCALL sass_delete_importer_list(Sass_Importer_List list) { Sass_Importer_List it = list; if (list == 0) return; while(*list) { sass_delete_importer(*list); ++list; } free(it); } Sass_Importer_Entry ADDCALL sass_importer_get_list_entry(Sass_Importer_List list, size_t idx) { return list[idx]; } void ADDCALL sass_importer_set_list_entry(Sass_Importer_List list, size_t idx, Sass_Importer_Entry cb) { list[idx] = cb; } // Creator for sass custom importer return argument list Sass_Import_List ADDCALL sass_make_import_list(size_t length) { return (Sass_Import**) calloc(length + 1, sizeof(Sass_Import*)); } // Creator for a single import entry returned by the custom importer inside the list // We take ownership of the memory for source and srcmap (freed when context is destroyed) Sass_Import_Entry ADDCALL sass_make_import(const char* imp_path, const char* abs_path, char* source, char* srcmap) { Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import)); if (v == 0) return 0; v->imp_path = imp_path ? sass_copy_c_string(imp_path) : 0; v->abs_path = abs_path ? sass_copy_c_string(abs_path) : 0; v->source = source; v->srcmap = srcmap; v->error = 0; v->line = -1; v->column = -1; return v; } // Older style, but somehow still valid - keep around or deprecate? Sass_Import_Entry ADDCALL sass_make_import_entry(const char* path, char* source, char* srcmap) { return sass_make_import(path, path, source, srcmap); } // Upgrade a normal import entry to throw an error (original path can be re-used by error reporting) Sass_Import_Entry ADDCALL sass_import_set_error(Sass_Import_Entry import, const char* error, size_t line, size_t col) { if (import == 0) return 0; if (import->error) free(import->error); import->error = error ? sass_copy_c_string(error) : 0; import->line = line ? line : -1; import->column = col ? col : -1; return import; } // Setters and getters for entries on the import list void ADDCALL sass_import_set_list_entry(Sass_Import_List list, size_t idx, Sass_Import_Entry entry) { list[idx] = entry; } Sass_Import_Entry ADDCALL sass_import_get_list_entry(Sass_Import_List list, size_t idx) { return list[idx]; } // Deallocator for the allocated memory void ADDCALL sass_delete_import_list(Sass_Import_List list) { Sass_Import_List it = list; if (list == 0) return; while(*list) { sass_delete_import(*list); ++list; } free(it); } // Just in case we have some stray import structs void ADDCALL sass_delete_import(Sass_Import_Entry import) { free(import->imp_path); free(import->abs_path); free(import->source); free(import->srcmap); free(import->error); free(import); } // Getter for callee entry const char* ADDCALL sass_callee_get_name(Sass_Callee_Entry entry) { return entry->name; } const char* ADDCALL sass_callee_get_path(Sass_Callee_Entry entry) { return entry->path; } size_t ADDCALL sass_callee_get_line(Sass_Callee_Entry entry) { return entry->line; } size_t ADDCALL sass_callee_get_column(Sass_Callee_Entry entry) { return entry->column; } enum Sass_Callee_Type ADDCALL sass_callee_get_type(Sass_Callee_Entry entry) { return entry->type; } Sass_Env_Frame ADDCALL sass_callee_get_env (Sass_Callee_Entry entry) { return &entry->env; } // Getters and Setters for environments (lexical, local and global) union Sass_Value* ADDCALL sass_env_get_lexical (Sass_Env_Frame env, const char* name) { Expression* ex = Cast((*env->frame)[name]); return ex != NULL ? ast_node_to_sass_value(ex) : NULL; } void ADDCALL sass_env_set_lexical (Sass_Env_Frame env, const char* name, union Sass_Value* val) { (*env->frame)[name] = sass_value_to_ast_node(val); } union Sass_Value* ADDCALL sass_env_get_local (Sass_Env_Frame env, const char* name) { Expression* ex = Cast(env->frame->get_local(name)); return ex != NULL ? ast_node_to_sass_value(ex) : NULL; } void ADDCALL sass_env_set_local (Sass_Env_Frame env, const char* name, union Sass_Value* val) { env->frame->set_local(name, sass_value_to_ast_node(val)); } union Sass_Value* ADDCALL sass_env_get_global (Sass_Env_Frame env, const char* name) { Expression* ex = Cast(env->frame->get_global(name)); return ex != NULL ? ast_node_to_sass_value(ex) : NULL; } void ADDCALL sass_env_set_global (Sass_Env_Frame env, const char* name, union Sass_Value* val) { env->frame->set_global(name, sass_value_to_ast_node(val)); } // Getter for import entry const char* ADDCALL sass_import_get_imp_path(Sass_Import_Entry entry) { return entry->imp_path; } const char* ADDCALL sass_import_get_abs_path(Sass_Import_Entry entry) { return entry->abs_path; } const char* ADDCALL sass_import_get_source(Sass_Import_Entry entry) { return entry->source; } const char* ADDCALL sass_import_get_srcmap(Sass_Import_Entry entry) { return entry->srcmap; } // Getter for import error entry size_t ADDCALL sass_import_get_error_line(Sass_Import_Entry entry) { return entry->line; } size_t ADDCALL sass_import_get_error_column(Sass_Import_Entry entry) { return entry->column; } const char* ADDCALL sass_import_get_error_message(Sass_Import_Entry entry) { return entry->error; } // Explicit functions to take ownership of the memory // Resets our own property since we do not know if it is still alive char* ADDCALL sass_import_take_source(Sass_Import_Entry entry) { char* ptr = entry->source; entry->source = 0; return ptr; } char* ADDCALL sass_import_take_srcmap(Sass_Import_Entry entry) { char* ptr = entry->srcmap; entry->srcmap = 0; return ptr; } } golibsass-1.0.0/libsass_src/src/sass_functions.hpp000066400000000000000000000017161405214413600223530ustar00rootroot00000000000000#ifndef SASS_SASS_FUNCTIONS_H #define SASS_SASS_FUNCTIONS_H #include "sass.h" #include "environment.hpp" #include "fn_utils.hpp" // Struct to hold custom function callback struct Sass_Function { char* signature; Sass_Function_Fn function; void* cookie; }; // External import entry struct Sass_Import { char* imp_path; // path as found in the import statement char *abs_path; // path after importer has resolved it char* source; char* srcmap; // error handling char* error; size_t line; size_t column; }; // External environments struct Sass_Env { // links to parent frames Sass::Env* frame; }; // External call entry struct Sass_Callee { const char* name; const char* path; size_t line; size_t column; enum Sass_Callee_Type type; struct Sass_Env env; }; // Struct to hold importer callback struct Sass_Importer { Sass_Importer_Fn importer; double priority; void* cookie; }; #endifgolibsass-1.0.0/libsass_src/src/sass_values.cpp000066400000000000000000000354701405214413600216410ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include "util.hpp" #include "eval.hpp" #include "operators.hpp" #include "sass/values.h" #include "sass_values.hpp" extern "C" { using namespace Sass; // Return the sass tag for a generic sass value enum Sass_Tag ADDCALL sass_value_get_tag(const union Sass_Value* v) { return v->unknown.tag; } // Check value for specified type bool ADDCALL sass_value_is_null(const union Sass_Value* v) { return v->unknown.tag == SASS_NULL; } bool ADDCALL sass_value_is_number(const union Sass_Value* v) { return v->unknown.tag == SASS_NUMBER; } bool ADDCALL sass_value_is_string(const union Sass_Value* v) { return v->unknown.tag == SASS_STRING; } bool ADDCALL sass_value_is_boolean(const union Sass_Value* v) { return v->unknown.tag == SASS_BOOLEAN; } bool ADDCALL sass_value_is_color(const union Sass_Value* v) { return v->unknown.tag == SASS_COLOR; } bool ADDCALL sass_value_is_list(const union Sass_Value* v) { return v->unknown.tag == SASS_LIST; } bool ADDCALL sass_value_is_map(const union Sass_Value* v) { return v->unknown.tag == SASS_MAP; } bool ADDCALL sass_value_is_error(const union Sass_Value* v) { return v->unknown.tag == SASS_ERROR; } bool ADDCALL sass_value_is_warning(const union Sass_Value* v) { return v->unknown.tag == SASS_WARNING; } // Getters and setters for Sass_Number double ADDCALL sass_number_get_value(const union Sass_Value* v) { return v->number.value; } void ADDCALL sass_number_set_value(union Sass_Value* v, double value) { v->number.value = value; } const char* ADDCALL sass_number_get_unit(const union Sass_Value* v) { return v->number.unit; } void ADDCALL sass_number_set_unit(union Sass_Value* v, char* unit) { v->number.unit = unit; } // Getters and setters for Sass_String const char* ADDCALL sass_string_get_value(const union Sass_Value* v) { return v->string.value; } void ADDCALL sass_string_set_value(union Sass_Value* v, char* value) { v->string.value = value; } bool ADDCALL sass_string_is_quoted(const union Sass_Value* v) { return v->string.quoted; } void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted) { v->string.quoted = quoted; } // Getters and setters for Sass_Boolean bool ADDCALL sass_boolean_get_value(const union Sass_Value* v) { return v->boolean.value; } void ADDCALL sass_boolean_set_value(union Sass_Value* v, bool value) { v->boolean.value = value; } // Getters and setters for Sass_Color double ADDCALL sass_color_get_r(const union Sass_Value* v) { return v->color.r; } void ADDCALL sass_color_set_r(union Sass_Value* v, double r) { v->color.r = r; } double ADDCALL sass_color_get_g(const union Sass_Value* v) { return v->color.g; } void ADDCALL sass_color_set_g(union Sass_Value* v, double g) { v->color.g = g; } double ADDCALL sass_color_get_b(const union Sass_Value* v) { return v->color.b; } void ADDCALL sass_color_set_b(union Sass_Value* v, double b) { v->color.b = b; } double ADDCALL sass_color_get_a(const union Sass_Value* v) { return v->color.a; } void ADDCALL sass_color_set_a(union Sass_Value* v, double a) { v->color.a = a; } // Getters and setters for Sass_List size_t ADDCALL sass_list_get_length(const union Sass_Value* v) { return v->list.length; } enum Sass_Separator ADDCALL sass_list_get_separator(const union Sass_Value* v) { return v->list.separator; } void ADDCALL sass_list_set_separator(union Sass_Value* v, enum Sass_Separator separator) { v->list.separator = separator; } bool ADDCALL sass_list_get_is_bracketed(const union Sass_Value* v) { return v->list.is_bracketed; } void ADDCALL sass_list_set_is_bracketed(union Sass_Value* v, bool is_bracketed) { v->list.is_bracketed = is_bracketed; } // Getters and setters for Sass_List values union Sass_Value* ADDCALL sass_list_get_value(const union Sass_Value* v, size_t i) { return v->list.values[i]; } void ADDCALL sass_list_set_value(union Sass_Value* v, size_t i, union Sass_Value* value) { v->list.values[i] = value; } // Getters and setters for Sass_Map size_t ADDCALL sass_map_get_length(const union Sass_Value* v) { return v->map.length; } // Getters and setters for Sass_List keys and values union Sass_Value* ADDCALL sass_map_get_key(const union Sass_Value* v, size_t i) { return v->map.pairs[i].key; } union Sass_Value* ADDCALL sass_map_get_value(const union Sass_Value* v, size_t i) { return v->map.pairs[i].value; } void ADDCALL sass_map_set_key(union Sass_Value* v, size_t i, union Sass_Value* key) { v->map.pairs[i].key = key; } void ADDCALL sass_map_set_value(union Sass_Value* v, size_t i, union Sass_Value* val) { v->map.pairs[i].value = val; } // Getters and setters for Sass_Error char* ADDCALL sass_error_get_message(const union Sass_Value* v) { return v->error.message; }; void ADDCALL sass_error_set_message(union Sass_Value* v, char* msg) { v->error.message = msg; }; // Getters and setters for Sass_Warning char* ADDCALL sass_warning_get_message(const union Sass_Value* v) { return v->warning.message; }; void ADDCALL sass_warning_set_message(union Sass_Value* v, char* msg) { v->warning.message = msg; }; // Creator functions for all value types union Sass_Value* ADDCALL sass_make_boolean(bool val) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->boolean.tag = SASS_BOOLEAN; v->boolean.value = val; return v; } union Sass_Value* ADDCALL sass_make_number(double val, const char* unit) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->number.tag = SASS_NUMBER; v->number.value = val; v->number.unit = unit ? sass_copy_c_string(unit) : 0; if (v->number.unit == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_color(double r, double g, double b, double a) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->color.tag = SASS_COLOR; v->color.r = r; v->color.g = g; v->color.b = b; v->color.a = a; return v; } union Sass_Value* ADDCALL sass_make_string(const char* val) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->string.quoted = false; v->string.tag = SASS_STRING; v->string.value = val ? sass_copy_c_string(val) : 0; if (v->string.value == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_qstring(const char* val) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->string.quoted = true; v->string.tag = SASS_STRING; v->string.value = val ? sass_copy_c_string(val) : 0; if (v->string.value == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_list(size_t len, enum Sass_Separator sep, bool is_bracketed) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->list.tag = SASS_LIST; v->list.length = len; v->list.separator = sep; v->list.is_bracketed = is_bracketed; v->list.values = (union Sass_Value**) calloc(len, sizeof(union Sass_Value*)); if (v->list.values == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_map(size_t len) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->map.tag = SASS_MAP; v->map.length = len; v->map.pairs = (struct Sass_MapPair*) calloc(len, sizeof(struct Sass_MapPair)); if (v->map.pairs == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_null(void) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->null.tag = SASS_NULL; return v; } union Sass_Value* ADDCALL sass_make_error(const char* msg) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->error.tag = SASS_ERROR; v->error.message = msg ? sass_copy_c_string(msg) : 0; if (v->error.message == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_warning(const char* msg) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->warning.tag = SASS_WARNING; v->warning.message = msg ? sass_copy_c_string(msg) : 0; if (v->warning.message == 0) { free(v); return 0; } return v; } // will free all associated sass values void ADDCALL sass_delete_value(union Sass_Value* val) { size_t i; if (val == 0) return; switch(val->unknown.tag) { case SASS_NULL: { } break; case SASS_BOOLEAN: { } break; case SASS_NUMBER: { free(val->number.unit); } break; case SASS_COLOR: { } break; case SASS_STRING: { free(val->string.value); } break; case SASS_LIST: { for (i=0; ilist.length; i++) { sass_delete_value(val->list.values[i]); } free(val->list.values); } break; case SASS_MAP: { for (i=0; imap.length; i++) { sass_delete_value(val->map.pairs[i].key); sass_delete_value(val->map.pairs[i].value); } free(val->map.pairs); } break; case SASS_ERROR: { free(val->error.message); } break; case SASS_WARNING: { free(val->error.message); } break; default: break; } free(val); } // Make a deep cloned copy of the given sass value union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val) { size_t i; if (val == 0) return 0; switch(val->unknown.tag) { case SASS_NULL: { return sass_make_null(); } case SASS_BOOLEAN: { return sass_make_boolean(val->boolean.value); } case SASS_NUMBER: { return sass_make_number(val->number.value, val->number.unit); } case SASS_COLOR: { return sass_make_color(val->color.r, val->color.g, val->color.b, val->color.a); } case SASS_STRING: { return sass_string_is_quoted(val) ? sass_make_qstring(val->string.value) : sass_make_string(val->string.value); } case SASS_LIST: { union Sass_Value* list = sass_make_list(val->list.length, val->list.separator, val->list.is_bracketed); for (i = 0; i < list->list.length; i++) { list->list.values[i] = sass_clone_value(val->list.values[i]); } return list; } case SASS_MAP: { union Sass_Value* map = sass_make_map(val->map.length); for (i = 0; i < val->map.length; i++) { map->map.pairs[i].key = sass_clone_value(val->map.pairs[i].key); map->map.pairs[i].value = sass_clone_value(val->map.pairs[i].value); } return map; } case SASS_ERROR: { return sass_make_error(val->error.message); } case SASS_WARNING: { return sass_make_warning(val->warning.message); } default: break; } return 0; } union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* v, bool compressed, int precision) { ValueObj val = sass_value_to_ast_node(v); Sass_Inspect_Options options(compressed ? COMPRESSED : NESTED, precision); sass::string str(val->to_string(options)); return sass_make_qstring(str.c_str()); } union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b) { Sass::ValueObj rv; try { ValueObj lhs = sass_value_to_ast_node(a); ValueObj rhs = sass_value_to_ast_node(b); struct Sass_Inspect_Options options(NESTED, 5); // see if it's a relational expression switch(op) { case Sass_OP::EQ: return sass_make_boolean(Operators::eq(lhs, rhs)); case Sass_OP::NEQ: return sass_make_boolean(Operators::neq(lhs, rhs)); case Sass_OP::GT: return sass_make_boolean(Operators::gt(lhs, rhs)); case Sass_OP::GTE: return sass_make_boolean(Operators::gte(lhs, rhs)); case Sass_OP::LT: return sass_make_boolean(Operators::lt(lhs, rhs)); case Sass_OP::LTE: return sass_make_boolean(Operators::lte(lhs, rhs)); case Sass_OP::AND: return ast_node_to_sass_value(lhs->is_false() ? lhs : rhs); case Sass_OP::OR: return ast_node_to_sass_value(lhs->is_false() ? rhs : lhs); default: break; } if (sass_value_is_number(a) && sass_value_is_number(b)) { const Number* l_n = Cast(lhs); const Number* r_n = Cast(rhs); rv = Operators::op_numbers(op, *l_n, *r_n, options, l_n->pstate()); } else if (sass_value_is_number(a) && sass_value_is_color(a)) { const Number* l_n = Cast(lhs); // Direct HSLA operations are not supported // All color maths will be deprecated anyway Color_RGBA_Obj r_c = Cast(rhs)->toRGBA(); rv = Operators::op_number_color(op, *l_n, *r_c, options, l_n->pstate()); } else if (sass_value_is_color(a) && sass_value_is_number(b)) { // Direct HSLA operations are not supported // All color maths will be deprecated anyway Color_RGBA_Obj l_c = Cast(lhs)->toRGBA(); const Number* r_n = Cast(rhs); rv = Operators::op_color_number(op, *l_c, *r_n, options, l_c->pstate()); } else if (sass_value_is_color(a) && sass_value_is_color(b)) { // Direct HSLA operations are not supported // All color maths will be deprecated anyway Color_RGBA_Obj l_c = Cast(lhs)->toRGBA(); Color_RGBA_Obj r_c = Cast(rhs)->toRGBA(); rv = Operators::op_colors(op, *l_c, *r_c, options, l_c->pstate()); } else /* convert other stuff to string and apply operation */ { rv = Operators::op_strings(op, *lhs, *rhs, options, lhs->pstate()); } // ToDo: maybe we should return null value? if (!rv) return sass_make_error("invalid return value"); // convert result back to ast node return ast_node_to_sass_value(rv.ptr()); } // simply pass the error message back to the caller for now catch (Exception::InvalidSass& e) { return sass_make_error(e.what()); } catch (std::bad_alloc&) { return sass_make_error("memory exhausted"); } catch (std::exception& e) { return sass_make_error(e.what()); } catch (sass::string& e) { return sass_make_error(e.c_str()); } catch (const char* e) { return sass_make_error(e); } catch (...) { return sass_make_error("unknown"); } } } golibsass-1.0.0/libsass_src/src/sass_values.hpp000066400000000000000000000025731405214413600216440ustar00rootroot00000000000000#ifndef SASS_SASS_VALUES_H #define SASS_SASS_VALUES_H #include "sass.h" struct Sass_Unknown { enum Sass_Tag tag; }; struct Sass_Boolean { enum Sass_Tag tag; bool value; }; struct Sass_Number { enum Sass_Tag tag; double value; char* unit; }; struct Sass_Color { enum Sass_Tag tag; double r; double g; double b; double a; }; struct Sass_String { enum Sass_Tag tag; bool quoted; char* value; }; struct Sass_List { enum Sass_Tag tag; enum Sass_Separator separator; bool is_bracketed; size_t length; // null terminated "array" union Sass_Value** values; }; struct Sass_Map { enum Sass_Tag tag; size_t length; struct Sass_MapPair* pairs; }; struct Sass_Null { enum Sass_Tag tag; }; struct Sass_Error { enum Sass_Tag tag; char* message; }; struct Sass_Warning { enum Sass_Tag tag; char* message; }; union Sass_Value { struct Sass_Unknown unknown; struct Sass_Boolean boolean; struct Sass_Number number; struct Sass_Color color; struct Sass_String string; struct Sass_List list; struct Sass_Map map; struct Sass_Null null; struct Sass_Error error; struct Sass_Warning warning; }; struct Sass_MapPair { union Sass_Value* key; union Sass_Value* value; }; #endif golibsass-1.0.0/libsass_src/src/settings.hpp000066400000000000000000000011361405214413600211460ustar00rootroot00000000000000#ifndef SASS_SETTINGS_H #define SASS_SETTINGS_H // Global compile time settings should go here // When enabled we use our custom memory pool allocator // With intense workloads this can double the performance // Max memory usage mostly only grows by a slight amount // #define SASS_CUSTOM_ALLOCATOR // How many buckets should we have for the free-list // Determines when allocations go directly to malloc/free // For maximum size of managed items multiply by alignment #define SassAllocatorBuckets 512 // The size of the memory pool arenas in bytes. #define SassAllocatorArenaSize (1024 * 256) #endif golibsass-1.0.0/libsass_src/src/source.cpp000066400000000000000000000021571405214413600206050ustar00rootroot00000000000000#include #include #include "source.hpp" #include "utf8/checked.h" #include "position.hpp" namespace Sass { SourceData::SourceData() : SharedObj() { } SourceFile::SourceFile( const char* path, const char* data, size_t srcid) : SourceData(), path(sass_copy_c_string(path)), data(sass_copy_c_string(data)), length(0), srcid(srcid) { length = strlen(data); } SourceFile::~SourceFile() { sass_free_memory(path); sass_free_memory(data); } const char* SourceFile::end() const { return data + length; } const char* SourceFile::begin() const { return data; } const char* SourceFile::getRawData() const { return data; } SourceSpan SourceFile::getSourceSpan() { return SourceSpan(this); } ItplFile::ItplFile(const char* data, const SourceSpan& pstate) : SourceFile(pstate.getPath(), data, pstate.getSrcId()), pstate(pstate) {} const char* ItplFile::getRawData() const { return pstate.getRawData(); } SourceSpan ItplFile::getSourceSpan() { return SourceSpan(pstate); } } golibsass-1.0.0/libsass_src/src/source.hpp000066400000000000000000000034471405214413600206150ustar00rootroot00000000000000#ifndef SASS_SOURCE_H #define SASS_SOURCE_H #include "sass.hpp" #include "memory.hpp" #include "position.hpp" #include "source_data.hpp" namespace Sass { class SourceFile : public SourceData { protected: char* path; char* data; size_t length; size_t srcid; public: SourceFile( const char* path, const char* data, size_t srcid); ~SourceFile(); const char* end() const override final; const char* begin() const override final; virtual const char* getRawData() const override; virtual SourceSpan getSourceSpan() override; size_t size() const override final { return length; } virtual const char* getPath() const override { return path; } virtual size_t getSrcId() const override { return srcid; } }; class SynthFile : public SourceData { protected: const char* path; public: SynthFile( const char* path) : path(path) {} ~SynthFile() {} const char* end() const override final { return nullptr; } const char* begin() const override final { return nullptr; }; virtual const char* getRawData() const override { return nullptr; }; virtual SourceSpan getSourceSpan() override { return SourceSpan(path); }; size_t size() const override final { return 0; } virtual const char* getPath() const override { return path; } virtual size_t getSrcId() const override { return std::string::npos; } }; class ItplFile : public SourceFile { private: SourceSpan pstate; public: ItplFile(const char* data, const SourceSpan& pstate); // Offset getPosition() const override final; const char* getRawData() const override final; SourceSpan getSourceSpan() override final; }; } #endif golibsass-1.0.0/libsass_src/src/source_data.hpp000066400000000000000000000012621405214413600215770ustar00rootroot00000000000000#ifndef SASS_SOURCE_DATA_H #define SASS_SOURCE_DATA_H #include "sass.hpp" #include "memory.hpp" namespace Sass { class SourceSpan; class SourceData : public SharedObj { public: SourceData(); virtual size_t size() const = 0; virtual size_t getSrcId() const = 0; virtual const char* end() const = 0; virtual const char* begin() const = 0; virtual const char* getPath() const = 0; // virtual Offset getPosition() const = 0; virtual const char* getRawData() const = 0; virtual SourceSpan getSourceSpan() = 0; sass::string to_string() const override { return sass::string{ begin(), end() }; } ~SourceData() {} }; } #endif golibsass-1.0.0/libsass_src/src/source_map.cpp000066400000000000000000000156751405214413600214530ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include "ast.hpp" #include "json.hpp" #include "context.hpp" #include "position.hpp" #include "source_map.hpp" namespace Sass { SourceMap::SourceMap() : current_position(0, 0, 0), file("stdin") { } SourceMap::SourceMap(const sass::string& file) : current_position(0, 0, 0), file(file) { } sass::string SourceMap::render_srcmap(Context &ctx) { const bool include_sources = ctx.c_options.source_map_contents; const sass::vector links = ctx.srcmap_links; const sass::vector& sources(ctx.resources); JsonNode* json_srcmap = json_mkobject(); json_append_member(json_srcmap, "version", json_mknumber(3)); const char *file_name = file.c_str(); JsonNode *json_file_name = json_mkstring(file_name); json_append_member(json_srcmap, "file", json_file_name); // pass-through sourceRoot option if (!ctx.source_map_root.empty()) { JsonNode* root = json_mkstring(ctx.source_map_root.c_str()); json_append_member(json_srcmap, "sourceRoot", root); } JsonNode *json_sources = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { sass::string source(links[source_index[i]]); if (ctx.c_options.source_map_file_urls) { source = File::rel2abs(source); // check for windows abs path if (source[0] == '/') { // ends up with three slashes source = "file://" + source; } else { // needs an additional slash source = "file:///" + source; } } const char* source_name = source.c_str(); JsonNode *json_source_name = json_mkstring(source_name); json_append_element(json_sources, json_source_name); } json_append_member(json_srcmap, "sources", json_sources); if (include_sources && source_index.size()) { JsonNode *json_contents = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { const Resource& resource(sources[source_index[i]]); JsonNode *json_content = json_mkstring(resource.contents); json_append_element(json_contents, json_content); } json_append_member(json_srcmap, "sourcesContent", json_contents); } JsonNode *json_names = json_mkarray(); // so far we have no implementation for names // no problem as we do not alter any identifiers json_append_member(json_srcmap, "names", json_names); sass::string mappings = serialize_mappings(); JsonNode *json_mappings = json_mkstring(mappings.c_str()); json_append_member(json_srcmap, "mappings", json_mappings); char *str = json_stringify(json_srcmap, "\t"); sass::string result = sass::string(str); free(str); json_delete(json_srcmap); return result; } sass::string SourceMap::serialize_mappings() { sass::string result = ""; size_t previous_generated_line = 0; size_t previous_generated_column = 0; size_t previous_original_line = 0; size_t previous_original_column = 0; size_t previous_original_file = 0; for (size_t i = 0; i < mappings.size(); ++i) { const size_t generated_line = mappings[i].generated_position.line; const size_t generated_column = mappings[i].generated_position.column; const size_t original_line = mappings[i].original_position.line; const size_t original_column = mappings[i].original_position.column; const size_t original_file = mappings[i].original_position.file; if (generated_line != previous_generated_line) { previous_generated_column = 0; if (generated_line > previous_generated_line) { result += sass::string(generated_line - previous_generated_line, ';'); previous_generated_line = generated_line; } } else if (i > 0) { result += ","; } // generated column result += base64vlq.encode(static_cast(generated_column) - static_cast(previous_generated_column)); previous_generated_column = generated_column; // file result += base64vlq.encode(static_cast(original_file) - static_cast(previous_original_file)); previous_original_file = original_file; // source line result += base64vlq.encode(static_cast(original_line) - static_cast(previous_original_line)); previous_original_line = original_line; // source column result += base64vlq.encode(static_cast(original_column) - static_cast(previous_original_column)); previous_original_column = original_column; } return result; } void SourceMap::prepend(const OutputBuffer& out) { Offset size(out.smap.current_position); for (Mapping mapping : out.smap.mappings) { if (mapping.generated_position.line > size.line) { throw(std::runtime_error("prepend sourcemap has illegal line")); } if (mapping.generated_position.line == size.line) { if (mapping.generated_position.column > size.column) { throw(std::runtime_error("prepend sourcemap has illegal column")); } } } // adjust the buffer offset prepend(Offset(out.buffer)); // now add the new mappings VECTOR_UNSHIFT(mappings, out.smap.mappings); } void SourceMap::append(const OutputBuffer& out) { append(Offset(out.buffer)); } void SourceMap::prepend(const Offset& offset) { if (offset.line != 0 || offset.column != 0) { for (Mapping& mapping : mappings) { // move stuff on the first old line if (mapping.generated_position.line == 0) { mapping.generated_position.column += offset.column; } // make place for the new lines mapping.generated_position.line += offset.line; } } if (current_position.line == 0) { current_position.column += offset.column; } current_position.line += offset.line; } void SourceMap::append(const Offset& offset) { current_position += offset; } void SourceMap::add_open_mapping(const AST_Node* node) { const SourceSpan& span(node->pstate()); Position from(span.getSrcId(), span.position); mappings.push_back(Mapping(from, current_position)); } void SourceMap::add_close_mapping(const AST_Node* node) { const SourceSpan& span(node->pstate()); Position to(span.getSrcId(), span.position + span.offset); mappings.push_back(Mapping(to, current_position)); } SourceSpan SourceMap::remap(const SourceSpan& pstate) { for (size_t i = 0; i < mappings.size(); ++i) { if ( mappings[i].generated_position.file == pstate.getSrcId() && mappings[i].generated_position.line == pstate.position.line && mappings[i].generated_position.column == pstate.position.column ) return SourceSpan(pstate.source, mappings[i].original_position, pstate.offset); } return SourceSpan(pstate.source, Position(-1, -1, -1), Offset(0, 0)); } } golibsass-1.0.0/libsass_src/src/source_map.hpp000066400000000000000000000024411405214413600214430ustar00rootroot00000000000000#ifndef SASS_SOURCE_MAP_H #define SASS_SOURCE_MAP_H #include #include #include "ast_fwd_decl.hpp" #include "base64vlq.hpp" #include "position.hpp" #include "mapping.hpp" #include "backtrace.hpp" #include "memory.hpp" #define VECTOR_PUSH(vec, ins) vec.insert(vec.end(), ins.begin(), ins.end()) #define VECTOR_UNSHIFT(vec, ins) vec.insert(vec.begin(), ins.begin(), ins.end()) namespace Sass { class Context; class OutputBuffer; class SourceMap { public: sass::vector source_index; SourceMap(); SourceMap(const sass::string& file); void append(const Offset& offset); void prepend(const Offset& offset); void append(const OutputBuffer& out); void prepend(const OutputBuffer& out); void add_open_mapping(const AST_Node* node); void add_close_mapping(const AST_Node* node); sass::string render_srcmap(Context &ctx); SourceSpan remap(const SourceSpan& pstate); private: sass::string serialize_mappings(); sass::vector mappings; Position current_position; public: sass::string file; private: Base64VLQ base64vlq; }; class OutputBuffer { public: OutputBuffer(void) : buffer(), smap() { } public: sass::string buffer; SourceMap smap; }; } #endif golibsass-1.0.0/libsass_src/src/stylesheet.cpp000066400000000000000000000006101405214413600214660ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "stylesheet.hpp" namespace Sass { // Constructor Sass::StyleSheet::StyleSheet(const Resource& res, Block_Obj root) : Resource(res), root(root) { } StyleSheet::StyleSheet(const StyleSheet& sheet) : Resource(sheet), root(sheet.root) { } } golibsass-1.0.0/libsass_src/src/stylesheet.hpp000066400000000000000000000027031405214413600215000ustar00rootroot00000000000000#ifndef SASS_STYLESHEET_H #define SASS_STYLESHEET_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast_fwd_decl.hpp" #include "extender.hpp" #include "file.hpp" namespace Sass { // parsed stylesheet from loaded resource // this should be a `Module` for sass 4.0 class StyleSheet : public Resource { public: // The canonical URL for this module's source file. This may be `null` // if the module was loaded from a string without a URL provided. // Uri get url; // Modules that this module uses. // List get upstream; // The module's variables. // Map get variables; // The module's functions. Implementations must ensure // that each [Callable] is stored under its own name. // Map get functions; // The module's mixins. Implementations must ensure that // each [Callable] is stored under its own name. // Map get mixins; // The extensions defined in this module, which is also able to update // [css]'s style rules in-place based on downstream extensions. // Extender extender; // The module's CSS tree. Block_Obj root; public: // default argument constructor StyleSheet(const Resource& res, Block_Obj root); // Copy constructor StyleSheet(const StyleSheet& res); }; } #endif golibsass-1.0.0/libsass_src/src/support/000077500000000000000000000000001405214413600203105ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/src/support/libsass.pc.in000066400000000000000000000003751405214413600227060ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libsass URL: https://github.com/sass/libsass Description: A C implementation of a Sass compiler Version: @VERSION@ Libs: -L${libdir} -lsass Cflags: -I${includedir} golibsass-1.0.0/libsass_src/src/to_value.cpp000066400000000000000000000045251405214413600211240ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "ast.hpp" #include "to_value.hpp" namespace Sass { // Custom_Error is a valid value Value* To_Value::operator()(Custom_Error* e) { return e; } // Custom_Warning is a valid value Value* To_Value::operator()(Custom_Warning* w) { return w; } // Boolean is a valid value Value* To_Value::operator()(Boolean* b) { return b; } // Number is a valid value Value* To_Value::operator()(Number* n) { return n; } // Color is a valid value Value* To_Value::operator()(Color_RGBA* c) { return c; } // Color is a valid value Value* To_Value::operator()(Color_HSLA* c) { return c; } // String_Constant is a valid value Value* To_Value::operator()(String_Constant* s) { return s; } // String_Quoted is a valid value Value* To_Value::operator()(String_Quoted* s) { return s; } // List is a valid value Value* To_Value::operator()(List* l) { List_Obj ll = SASS_MEMORY_NEW(List, l->pstate(), l->length(), l->separator(), l->is_arglist(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { ll->append((*l)[i]->perform(this)); } return ll.detach(); } // Map is a valid value Value* To_Value::operator()(Map* m) { return m; } // Null is a valid value Value* To_Value::operator()(Null* n) { return n; } // Function is a valid value Value* To_Value::operator()(Function* n) { return n; } // Argument returns its value Value* To_Value::operator()(Argument* arg) { if (!arg->name().empty()) return 0; return arg->value()->perform(this); } // SelectorList is converted to a string Value* To_Value::operator()(SelectorList* s) { return SASS_MEMORY_NEW(String_Quoted, s->pstate(), s->to_string(ctx.c_options)); } // Binary_Expression is converted to a string Value* To_Value::operator()(Binary_Expression* s) { return SASS_MEMORY_NEW(String_Quoted, s->pstate(), s->to_string(ctx.c_options)); } }; golibsass-1.0.0/libsass_src/src/to_value.hpp000066400000000000000000000016721405214413600211310ustar00rootroot00000000000000#ifndef SASS_TO_VALUE_H #define SASS_TO_VALUE_H #include "operation.hpp" #include "sass/values.h" #include "ast_fwd_decl.hpp" namespace Sass { class To_Value : public Operation_CRTP { private: Context& ctx; public: To_Value(Context& ctx) : ctx(ctx) { } ~To_Value() { } using Operation::operator(); Value* operator()(Argument*); Value* operator()(Boolean*); Value* operator()(Number*); Value* operator()(Color_RGBA*); Value* operator()(Color_HSLA*); Value* operator()(String_Constant*); Value* operator()(String_Quoted*); Value* operator()(Custom_Warning*); Value* operator()(Custom_Error*); Value* operator()(List*); Value* operator()(Map*); Value* operator()(Null*); Value* operator()(Function*); // convert to string via `To_String` Value* operator()(SelectorList*); Value* operator()(Binary_Expression*); }; } #endif golibsass-1.0.0/libsass_src/src/units.cpp000066400000000000000000000370541405214413600204530ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include "units.hpp" #include "error_handling.hpp" namespace Sass { /* the conversion matrix can be readed the following way */ /* if you go down, the factor is for the numerator (multiply) */ /* if you go right, the factor is for the denominator (divide) */ /* and yes, we actually use both, not sure why, but why not!? */ const double size_conversion_factors[6][6] = { /* in cm pc mm pt px */ /* in */ { 1, 2.54, 6, 25.4, 72, 96, }, /* cm */ { 1.0/2.54, 1, 6.0/2.54, 10, 72.0/2.54, 96.0/2.54 }, /* pc */ { 1.0/6.0, 2.54/6.0, 1, 25.4/6.0, 72.0/6.0, 96.0/6.0 }, /* mm */ { 1.0/25.4, 1.0/10.0, 6.0/25.4, 1, 72.0/25.4, 96.0/25.4 }, /* pt */ { 1.0/72.0, 2.54/72.0, 6.0/72.0, 25.4/72.0, 1, 96.0/72.0 }, /* px */ { 1.0/96.0, 2.54/96.0, 6.0/96.0, 25.4/96.0, 72.0/96.0, 1, } }; const double angle_conversion_factors[4][4] = { /* deg grad rad turn */ /* deg */ { 1, 40.0/36.0, PI/180.0, 1.0/360.0 }, /* grad */ { 36.0/40.0, 1, PI/200.0, 1.0/400.0 }, /* rad */ { 180.0/PI, 200.0/PI, 1, 0.5/PI }, /* turn */ { 360.0, 400.0, 2.0*PI, 1 } }; const double time_conversion_factors[2][2] = { /* s ms */ /* s */ { 1, 1000.0 }, /* ms */ { 1/1000.0, 1 } }; const double frequency_conversion_factors[2][2] = { /* Hz kHz */ /* Hz */ { 1, 1/1000.0 }, /* kHz */ { 1000.0, 1 } }; const double resolution_conversion_factors[3][3] = { /* dpi dpcm dppx */ /* dpi */ { 1, 1/2.54, 1/96.0 }, /* dpcm */ { 2.54, 1, 2.54/96 }, /* dppx */ { 96, 96/2.54, 1 } }; UnitClass get_unit_type(UnitType unit) { switch (unit & 0xFF00) { case UnitClass::LENGTH: return UnitClass::LENGTH; case UnitClass::ANGLE: return UnitClass::ANGLE; case UnitClass::TIME: return UnitClass::TIME; case UnitClass::FREQUENCY: return UnitClass::FREQUENCY; case UnitClass::RESOLUTION: return UnitClass::RESOLUTION; default: return UnitClass::INCOMMENSURABLE; } }; sass::string get_unit_class(UnitType unit) { switch (unit & 0xFF00) { case UnitClass::LENGTH: return "LENGTH"; case UnitClass::ANGLE: return "ANGLE"; case UnitClass::TIME: return "TIME"; case UnitClass::FREQUENCY: return "FREQUENCY"; case UnitClass::RESOLUTION: return "RESOLUTION"; default: return "INCOMMENSURABLE"; } }; UnitType get_main_unit(const UnitClass unit) { switch (unit) { case UnitClass::LENGTH: return UnitType::PX; case UnitClass::ANGLE: return UnitType::DEG; case UnitClass::TIME: return UnitType::SEC; case UnitClass::FREQUENCY: return UnitType::HERTZ; case UnitClass::RESOLUTION: return UnitType::DPI; default: return UnitType::UNKNOWN; } }; UnitType string_to_unit(const sass::string& s) { // size units if (s == "px") return UnitType::PX; else if (s == "pt") return UnitType::PT; else if (s == "pc") return UnitType::PC; else if (s == "mm") return UnitType::MM; else if (s == "cm") return UnitType::CM; else if (s == "in") return UnitType::IN; // angle units else if (s == "deg") return UnitType::DEG; else if (s == "grad") return UnitType::GRAD; else if (s == "rad") return UnitType::RAD; else if (s == "turn") return UnitType::TURN; // time units else if (s == "s") return UnitType::SEC; else if (s == "ms") return UnitType::MSEC; // frequency units else if (s == "Hz") return UnitType::HERTZ; else if (s == "kHz") return UnitType::KHERTZ; // resolutions units else if (s == "dpi") return UnitType::DPI; else if (s == "dpcm") return UnitType::DPCM; else if (s == "dppx") return UnitType::DPPX; // for unknown units else return UnitType::UNKNOWN; } const char* unit_to_string(UnitType unit) { switch (unit) { // size units case UnitType::PX: return "px"; case UnitType::PT: return "pt"; case UnitType::PC: return "pc"; case UnitType::MM: return "mm"; case UnitType::CM: return "cm"; case UnitType::IN: return "in"; // angle units case UnitType::DEG: return "deg"; case UnitType::GRAD: return "grad"; case UnitType::RAD: return "rad"; case UnitType::TURN: return "turn"; // time units case UnitType::SEC: return "s"; case UnitType::MSEC: return "ms"; // frequency units case UnitType::HERTZ: return "Hz"; case UnitType::KHERTZ: return "kHz"; // resolutions units case UnitType::DPI: return "dpi"; case UnitType::DPCM: return "dpcm"; case UnitType::DPPX: return "dppx"; // for unknown units default: return ""; } } sass::string unit_to_class(const sass::string& s) { if (s == "px") return "LENGTH"; else if (s == "pt") return "LENGTH"; else if (s == "pc") return "LENGTH"; else if (s == "mm") return "LENGTH"; else if (s == "cm") return "LENGTH"; else if (s == "in") return "LENGTH"; // angle units else if (s == "deg") return "ANGLE"; else if (s == "grad") return "ANGLE"; else if (s == "rad") return "ANGLE"; else if (s == "turn") return "ANGLE"; // time units else if (s == "s") return "TIME"; else if (s == "ms") return "TIME"; // frequency units else if (s == "Hz") return "FREQUENCY"; else if (s == "kHz") return "FREQUENCY"; // resolutions units else if (s == "dpi") return "RESOLUTION"; else if (s == "dpcm") return "RESOLUTION"; else if (s == "dppx") return "RESOLUTION"; // for unknown units return "CUSTOM:" + s; } // throws incompatibleUnits exceptions double conversion_factor(const sass::string& s1, const sass::string& s2) { // assert for same units if (s1 == s2) return 1; // get unit enum from string UnitType u1 = string_to_unit(s1); UnitType u2 = string_to_unit(s2); // query unit group types UnitClass t1 = get_unit_type(u1); UnitClass t2 = get_unit_type(u2); // return the conversion factor return conversion_factor(u1, u2, t1, t2); } // throws incompatibleUnits exceptions double conversion_factor(UnitType u1, UnitType u2, UnitClass t1, UnitClass t2) { // can't convert between groups if (t1 != t2) return 0; // get absolute offset // used for array acces size_t i1 = u1 - t1; size_t i2 = u2 - t2; // process known units switch (t1) { case LENGTH: return size_conversion_factors[i1][i2]; case ANGLE: return angle_conversion_factors[i1][i2]; case TIME: return time_conversion_factors[i1][i2]; case FREQUENCY: return frequency_conversion_factors[i1][i2]; case RESOLUTION: return resolution_conversion_factors[i1][i2]; case INCOMMENSURABLE: return 0; } // fallback return 0; } double convert_units(const sass::string& lhs, const sass::string& rhs, int& lhsexp, int& rhsexp) { double f = 0; // do not convert same ones if (lhs == rhs) return 0; // skip already canceled out unit if (lhsexp == 0) return 0; if (rhsexp == 0) return 0; // check if it can be converted UnitType ulhs = string_to_unit(lhs); UnitType urhs = string_to_unit(rhs); // skip units we cannot convert if (ulhs == UNKNOWN) return 0; if (urhs == UNKNOWN) return 0; // query unit group types UnitClass clhs = get_unit_type(ulhs); UnitClass crhs = get_unit_type(urhs); // skip units we cannot convert if (clhs != crhs) return 0; // if right denominator is bigger than lhs, we want to keep it in rhs unit if (rhsexp < 0 && lhsexp > 0 && - rhsexp > lhsexp) { // get the conversion factor for units f = conversion_factor(urhs, ulhs, clhs, crhs); // left hand side has been consumned f = std::pow(f, lhsexp); rhsexp += lhsexp; lhsexp = 0; } else { // get the conversion factor for units f = conversion_factor(ulhs, urhs, clhs, crhs); // right hand side has been consumned f = std::pow(f, rhsexp); lhsexp += rhsexp; rhsexp = 0; } return f; } bool Units::operator< (const Units& rhs) const { return (numerators < rhs.numerators) && (denominators < rhs.denominators); } bool Units::operator== (const Units& rhs) const { return (numerators == rhs.numerators) && (denominators == rhs.denominators); } bool Units::operator!= (const Units& rhs) const { return ! (*this == rhs); } double Units::normalize() { size_t iL = numerators.size(); size_t nL = denominators.size(); // the final conversion factor double factor = 1; for (size_t i = 0; i < iL; i++) { sass::string &lhs = numerators[i]; UnitType ulhs = string_to_unit(lhs); if (ulhs == UNKNOWN) continue; UnitClass clhs = get_unit_type(ulhs); UnitType umain = get_main_unit(clhs); if (ulhs == umain) continue; double f(conversion_factor(umain, ulhs, clhs, clhs)); if (f == 0) throw std::runtime_error("INVALID"); numerators[i] = unit_to_string(umain); factor /= f; } for (size_t n = 0; n < nL; n++) { sass::string &rhs = denominators[n]; UnitType urhs = string_to_unit(rhs); if (urhs == UNKNOWN) continue; UnitClass crhs = get_unit_type(urhs); UnitType umain = get_main_unit(crhs); if (urhs == umain) continue; double f(conversion_factor(umain, urhs, crhs, crhs)); if (f == 0) throw std::runtime_error("INVALID"); denominators[n] = unit_to_string(umain); factor /= f; } std::sort (numerators.begin(), numerators.end()); std::sort (denominators.begin(), denominators.end()); // return for conversion return factor; } double Units::reduce() { size_t iL = numerators.size(); size_t nL = denominators.size(); // have less than two units? if (iL + nL < 2) return 1; // first make sure same units cancel each other out // it seems that a map table will fit nicely to do this // we basically construct exponents for each unit // has the advantage that they will be pre-sorted std::map exponents; // initialize by summing up occurrences in unit vectors // this will already cancel out equivalent units (e.q. px/px) for (size_t i = 0; i < iL; i ++) exponents[numerators[i]] += 1; for (size_t n = 0; n < nL; n ++) exponents[denominators[n]] -= 1; // the final conversion factor double factor = 1; // convert between compatible units for (size_t i = 0; i < iL; i++) { for (size_t n = 0; n < nL; n++) { sass::string &lhs = numerators[i], &rhs = denominators[n]; int &lhsexp = exponents[lhs], &rhsexp = exponents[rhs]; double f(convert_units(lhs, rhs, lhsexp, rhsexp)); if (f == 0) continue; factor /= f; } } // now we can build up the new unit arrays numerators.clear(); denominators.clear(); // recreate sorted units vectors for (auto exp : exponents) { int &exponent = exp.second; while (exponent > 0 && exponent --) numerators.push_back(exp.first); while (exponent < 0 && exponent ++) denominators.push_back(exp.first); } // return for conversion return factor; } sass::string Units::unit() const { sass::string u; size_t iL = numerators.size(); size_t nL = denominators.size(); for (size_t i = 0; i < iL; i += 1) { if (i) u += '*'; u += numerators[i]; } if (nL != 0) u += '/'; for (size_t n = 0; n < nL; n += 1) { if (n) u += '*'; u += denominators[n]; } return u; } bool Units::is_unitless() const { return numerators.empty() && denominators.empty(); } bool Units::is_valid_css_unit() const { return numerators.size() <= 1 && denominators.size() == 0; } // this does not cover all cases (multiple preferred units) double Units::convert_factor(const Units& r) const { sass::vector miss_nums(0); sass::vector miss_dens(0); // create copy since we need these for state keeping sass::vector r_nums(r.numerators); sass::vector r_dens(r.denominators); auto l_num_it = numerators.begin(); auto l_num_end = numerators.end(); bool l_unitless = is_unitless(); auto r_unitless = r.is_unitless(); // overall conversion double factor = 1; // process all left numerators while (l_num_it != l_num_end) { // get and increment afterwards const sass::string l_num = *(l_num_it ++); auto r_num_it = r_nums.begin(), r_num_end = r_nums.end(); bool found = false; // search for compatible numerator while (r_num_it != r_num_end) { // get and increment afterwards const sass::string r_num = *(r_num_it); // get possible conversion factor for units double conversion = conversion_factor(l_num, r_num); // skip incompatible numerator if (conversion == 0) { ++ r_num_it; continue; } // apply to global factor factor *= conversion; // remove item from vector r_nums.erase(r_num_it); // found numerator found = true; break; } // maybe we did not find any // left numerator is leftover if (!found) miss_nums.push_back(l_num); } auto l_den_it = denominators.begin(); auto l_den_end = denominators.end(); // process all left denominators while (l_den_it != l_den_end) { // get and increment afterwards const sass::string l_den = *(l_den_it ++); auto r_den_it = r_dens.begin(); auto r_den_end = r_dens.end(); bool found = false; // search for compatible denominator while (r_den_it != r_den_end) { // get and increment afterwards const sass::string r_den = *(r_den_it); // get possible conversion factor for units double conversion = conversion_factor(l_den, r_den); // skip incompatible denominator if (conversion == 0) { ++ r_den_it; continue; } // apply to global factor factor /= conversion; // remove item from vector r_dens.erase(r_den_it); // found denominator found = true; break; } // maybe we did not find any // left denominator is leftover if (!found) miss_dens.push_back(l_den); } // check left-overs (ToDo: might cancel out?) if (miss_nums.size() > 0 && !r_unitless) { throw Exception::IncompatibleUnits(r, *this); } else if (miss_dens.size() > 0 && !r_unitless) { throw Exception::IncompatibleUnits(r, *this); } else if (r_nums.size() > 0 && !l_unitless) { throw Exception::IncompatibleUnits(r, *this); } else if (r_dens.size() > 0 && !l_unitless) { throw Exception::IncompatibleUnits(r, *this); } return factor; } } golibsass-1.0.0/libsass_src/src/units.hpp000066400000000000000000000050101405214413600204430ustar00rootroot00000000000000#ifndef SASS_UNITS_H #define SASS_UNITS_H #include #include #include #include namespace Sass { const double PI = std::acos(-1); enum UnitClass { LENGTH = 0x000, ANGLE = 0x100, TIME = 0x200, FREQUENCY = 0x300, RESOLUTION = 0x400, INCOMMENSURABLE = 0x500 }; enum UnitType { // size units IN = UnitClass::LENGTH, CM, PC, MM, PT, PX, // angle units DEG = ANGLE, GRAD, RAD, TURN, // time units SEC = TIME, MSEC, // frequency units HERTZ = FREQUENCY, KHERTZ, // resolutions units DPI = RESOLUTION, DPCM, DPPX, // for unknown units UNKNOWN = INCOMMENSURABLE }; class Units { public: sass::vector numerators; sass::vector denominators; public: // default constructor Units() : numerators(), denominators() { } // copy constructor Units(const Units* ptr) : numerators(ptr->numerators), denominators(ptr->denominators) { } // convert to string sass::string unit() const; // get if units are empty bool is_unitless() const; // return if valid for css bool is_valid_css_unit() const; // reduce units for output // returns conversion factor double reduce(); // normalize units for compare // returns conversion factor double normalize(); // compare operations bool operator< (const Units& rhs) const; bool operator== (const Units& rhs) const; bool operator!= (const Units& rhs) const; // factor to convert into given units double convert_factor(const Units&) const; }; extern const double size_conversion_factors[6][6]; extern const double angle_conversion_factors[4][4]; extern const double time_conversion_factors[2][2]; extern const double frequency_conversion_factors[2][2]; extern const double resolution_conversion_factors[3][3]; UnitType get_main_unit(const UnitClass unit); enum Sass::UnitType string_to_unit(const sass::string&); const char* unit_to_string(Sass::UnitType unit); enum Sass::UnitClass get_unit_type(Sass::UnitType unit); sass::string get_unit_class(Sass::UnitType unit); sass::string unit_to_class(const sass::string&); // throws incompatibleUnits exceptions double conversion_factor(const sass::string&, const sass::string&); double conversion_factor(UnitType, UnitType, UnitClass, UnitClass); double convert_units(const sass::string&, const sass::string&, int&, int&); } #endif golibsass-1.0.0/libsass_src/src/utf8.h000066400000000000000000000027611405214413600176410ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8/checked.h" #include "utf8/unchecked.h" #endif // header guard golibsass-1.0.0/libsass_src/src/utf8/000077500000000000000000000000001405214413600174625ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/src/utf8/checked.h000066400000000000000000000303121405214413600212200ustar00rootroot00000000000000// Copyright 2006-2016 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" #include namespace utf8 { // Base for the exceptions that may be thrown from the library class exception : public ::std::exception { }; // Exceptions that may be thrown from the library functions. class invalid_code_point : public exception { uint32_t cp; public: invalid_code_point(uint32_t codepoint) : cp(codepoint) {} virtual const char* what() const throw() { return "Invalid code point"; } uint32_t code_point() const {return cp;} }; class invalid_utf8 : public exception { uint8_t u8; public: invalid_utf8 (uint8_t u) : u8(u) {} virtual const char* what() const throw() { return "Invalid UTF-8"; } uint8_t utf8_octet() const {return u8;} }; class invalid_utf16 : public exception { uint16_t u16; public: invalid_utf16 (uint16_t u) : u16(u) {} virtual const char* what() const throw() { return "Invalid UTF-16"; } uint16_t utf16_word() const {return u16;} }; class not_enough_room : public exception { public: virtual const char* what() const throw() { return "Not enough space"; } }; /// The library API - functions intended to be called by the users template octet_iterator append(uint32_t cp, octet_iterator result) { if (!utf8::internal::is_code_point_valid(cp)) throw invalid_code_point(cp); if (cp < 0x80) // one octet *(result++) = static_cast(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast((cp >> 6) | 0xc0); *(result++) = static_cast((cp & 0x3f) | 0x80); } else if (cp < 0x10000) { // three octets *(result++) = static_cast((cp >> 12) | 0xe0); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } else { // four octets *(result++) = static_cast((cp >> 18) | 0xf0); *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } return result; } template output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) { while (start != end) { octet_iterator sequence_start = start; internal::utf_error err_code = utf8::internal::validate_next(start, end); switch (err_code) { case internal::UTF8_OK : for (octet_iterator it = sequence_start; it != start; ++it) *out++ = *it; break; case internal::NOT_ENOUGH_ROOM: out = utf8::append (replacement, out); start = end; break; case internal::INVALID_LEAD: out = utf8::append (replacement, out); ++start; break; case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: case internal::INVALID_CODE_POINT: out = utf8::append (replacement, out); ++start; // just one replacement mark for the sequence while (start != end && utf8::internal::is_trail(*start)) ++start; break; } } return out; } template inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) { static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); return utf8::replace_invalid(start, end, out, replacement_marker); } template uint32_t next(octet_iterator& it, octet_iterator end) { uint32_t cp = 0; internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); switch (err_code) { case internal::UTF8_OK : break; case internal::NOT_ENOUGH_ROOM : throw not_enough_room(); case internal::INVALID_LEAD : case internal::INCOMPLETE_SEQUENCE : case internal::OVERLONG_SEQUENCE : throw invalid_utf8(*it); case internal::INVALID_CODE_POINT : throw invalid_code_point(cp); } return cp; } template uint32_t peek_next(octet_iterator it, octet_iterator end) { return utf8::next(it, end); } template uint32_t prior(octet_iterator& it, octet_iterator start) { // can't do much if it == start if (it == start) throw not_enough_room(); octet_iterator end = it; // Go back until we hit either a lead octet or start while (utf8::internal::is_trail(*(--it))) if (it == start) throw invalid_utf8(*it); // error - no lead byte in the sequence return utf8::peek_next(it, end); } /// Deprecated in versions that include "prior" template uint32_t previous(octet_iterator& it, octet_iterator pass_start) { octet_iterator end = it; while (utf8::internal::is_trail(*(--it))) if (it == pass_start) throw invalid_utf8(*it); // error - no lead byte in the sequence octet_iterator temp = it; return utf8::next(temp, end); } template void advance (octet_iterator& it, distance_type n, octet_iterator end) { for (distance_type i = 0; i < n; ++i) utf8::next(it, end); } template void retreat (octet_iterator& it, distance_type n, octet_iterator end) { for (distance_type i = 0; i < n; ++i) utf8::prior(it, end); } template typename std::iterator_traits::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) utf8::next(first, last); return dist; } template octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { if (start != end) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); if (utf8::internal::is_trail_surrogate(trail_surrogate)) cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; else throw invalid_utf16(static_cast(trail_surrogate)); } else throw invalid_utf16(static_cast(cp)); } // Lone trail surrogate else if (utf8::internal::is_trail_surrogate(cp)) throw invalid_utf16(static_cast(cp)); result = utf8::append(cp, result); } return result; } template u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start < end) { uint32_t cp = utf8::next(start, end); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast(cp); } return result; } template octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::append(*(start++), result); return result; } template u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) (*result++) = utf8::next(start, end); return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; octet_iterator range_start; octet_iterator range_end; public: iterator () {} explicit iterator (const octet_iterator& octet_it, const octet_iterator& rangestart, const octet_iterator& rangeend) : it(octet_it), range_start(rangestart), range_end(rangeend) { if (it < range_start || it > range_end) throw std::out_of_range("Invalid utf-8 iterator position"); } // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::next(temp, range_end); } bool operator == (const iterator& rhs) const { if (range_start != rhs.range_start || range_end != rhs.range_end) throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { utf8::next(it, range_end); return *this; } iterator operator ++ (int) { iterator temp = *this; utf8::next(it, range_end); return temp; } iterator& operator -- () { utf8::prior(it, range_start); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::prior(it, range_start); return temp; } }; // class iterator } // namespace utf8 #endif //header guard golibsass-1.0.0/libsass_src/src/utf8/core.h000066400000000000000000000247351405214413600205760ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include namespace utf8 { // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers // You may need to change them to match your system. // These typedefs have the same names as ones from cstdint, or boost/cstdint typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; // Helper code - not intended to be directly called by the library users. May be changed at any time namespace internal { // Unicode constants // Leading (high) surrogates: 0xd800 - 0xdbff // Trailing (low) surrogates: 0xdc00 - 0xdfff const uint16_t LEAD_SURROGATE_MIN = 0xd800u; const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; // Maximum valid value for a Unicode code point const uint32_t CODE_POINT_MAX = 0x0010ffffu; template inline uint8_t mask8(octet_type oc) { return static_cast(0xff & oc); } template inline uint16_t mask16(u16_type oc) { return static_cast(0xffff & oc); } template inline bool is_trail(octet_type oc) { return ((utf8::internal::mask8(oc) >> 6) == 0x2); } template inline bool is_lead_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); } template inline bool is_trail_surrogate(u16 cp) { return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template inline bool is_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template inline bool is_code_point_valid(u32 cp) { return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); } template inline typename std::iterator_traits::difference_type sequence_length(octet_iterator lead_it) { uint8_t lead = utf8::internal::mask8(*lead_it); if (lead < 0x80) return 1; else if ((lead >> 5) == 0x6) return 2; else if ((lead >> 4) == 0xe) return 3; else if ((lead >> 3) == 0x1e) return 4; else return 0; } template inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) { if (cp < 0x80) { if (length != 1) return true; } else if (cp < 0x800) { if (length != 2) return true; } else if (cp < 0x10000) { if (length != 3) return true; } return false; } enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; /// Helper for get_sequence_x template utf_error increase_safely(octet_iterator& it, octet_iterator end) { if (++it == end) return NOT_ENOUGH_ROOM; if (!utf8::internal::is_trail(*it)) return INCOMPLETE_SEQUENCE; return UTF8_OK; } #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} /// get_sequence_x functions decode utf-8 sequences of the length x template utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); return UTF8_OK; } template utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); return UTF8_OK; } template utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } template utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR template utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; // Save the original value of it so we can go back in case of failure // Of course, it does not make much sense with i.e. stream iterators octet_iterator original_it = it; uint32_t cp = 0; // Determine the sequence length based on the lead octet typedef typename std::iterator_traits::difference_type octet_difference_type; const octet_difference_type length = utf8::internal::sequence_length(it); // Get trail octets and calculate the code point utf_error err = UTF8_OK; switch (length) { case 0: return INVALID_LEAD; case 1: err = utf8::internal::get_sequence_1(it, end, cp); break; case 2: err = utf8::internal::get_sequence_2(it, end, cp); break; case 3: err = utf8::internal::get_sequence_3(it, end, cp); break; case 4: err = utf8::internal::get_sequence_4(it, end, cp); break; } if (err == UTF8_OK) { // Decoding succeeded. Now, security checks... if (utf8::internal::is_code_point_valid(cp)) { if (!utf8::internal::is_overlong_sequence(cp, length)){ // Passed! Return here. code_point = cp; ++it; return UTF8_OK; } else err = OVERLONG_SEQUENCE; } else err = INVALID_CODE_POINT; } // Failure branch - restore the original value of the iterator it = original_it; return err; } template inline utf_error validate_next(octet_iterator& it, octet_iterator end) { uint32_t ignored; return utf8::internal::validate_next(it, end, ignored); } } // namespace internal /// The library API - functions intended to be called by the users // Byte order mark const uint8_t bom[] = {0xef, 0xbb, 0xbf}; template octet_iterator find_invalid(octet_iterator start, octet_iterator end) { octet_iterator result = start; while (result != end) { utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); if (err_code != internal::UTF8_OK) return result; } return result; } template inline bool is_valid(octet_iterator start, octet_iterator end) { return (utf8::find_invalid(start, end) == end); } template inline bool starts_with_bom (octet_iterator it, octet_iterator end) { return ( ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) ); } //Deprecated in release 2.3 template inline bool is_bom (octet_iterator it) { return ( (utf8::internal::mask8(*it++)) == bom[0] && (utf8::internal::mask8(*it++)) == bom[1] && (utf8::internal::mask8(*it)) == bom[2] ); } } // namespace utf8 #endif // header guard golibsass-1.0.0/libsass_src/src/utf8/unchecked.h000066400000000000000000000216031405214413600215660ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" namespace utf8 { namespace unchecked { template octet_iterator append(uint32_t cp, octet_iterator result) { if (cp < 0x80) // one octet *(result++) = static_cast(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast((cp >> 6) | 0xc0); *(result++) = static_cast((cp & 0x3f) | 0x80); } else if (cp < 0x10000) { // three octets *(result++) = static_cast((cp >> 12) | 0xe0); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } else { // four octets *(result++) = static_cast((cp >> 18) | 0xf0); *(result++) = static_cast(((cp >> 12) & 0x3f)| 0x80); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } return result; } template uint32_t next(octet_iterator& it) { uint32_t cp = utf8::internal::mask8(*it); typename std::iterator_traits::difference_type length = utf8::internal::sequence_length(it); switch (length) { case 1: break; case 2: it++; cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3: ++it; cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); ++it; cp += (*it) & 0x3f; break; case 4: ++it; cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); ++it; cp += (utf8::internal::mask8(*it) << 6) & 0xfff; ++it; cp += (*it) & 0x3f; break; } ++it; return cp; } template uint32_t peek_next(octet_iterator it) { return utf8::unchecked::next(it); } template uint32_t prior(octet_iterator& it) { while (utf8::internal::is_trail(*(--it))) ; octet_iterator temp = it; return utf8::unchecked::next(temp); } // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) template inline uint32_t previous(octet_iterator& it) { return utf8::unchecked::prior(it); } template void advance (octet_iterator& it, distance_type n) { for (distance_type i = 0; i < n; ++i) utf8::unchecked::next(it); } template void retreat (octet_iterator& it, distance_type n) { for (distance_type i = 0; i < n; ++i) utf8::unchecked::prior(it); } template typename std::iterator_traits::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) utf8::unchecked::next(first); return dist; } template octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; } result = utf8::unchecked::append(cp, result); } return result; } template u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start < end) { uint32_t cp = utf8::unchecked::next(start); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast(cp); } return result; } template octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::unchecked::append(*(start++), result); return result; } template u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) (*result++) = utf8::unchecked::next(start); return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; public: iterator () {} explicit iterator (const octet_iterator& octet_it): it(octet_it) {} // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::unchecked::next(temp); } bool operator == (const iterator& rhs) const { return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { ::std::advance(it, utf8::internal::sequence_length(it)); return *this; } iterator operator ++ (int) { iterator temp = *this; ::std::advance(it, utf8::internal::sequence_length(it)); return temp; } iterator& operator -- () { utf8::unchecked::prior(it); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::unchecked::prior(it); return temp; } }; // class iterator } // namespace utf8::unchecked } // namespace utf8 #endif // header guard golibsass-1.0.0/libsass_src/src/utf8_string.cpp000066400000000000000000000061021405214413600215530ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include #include #include #include #include "utf8.h" namespace Sass { namespace UTF_8 { // naming conventions: // offset: raw byte offset (0 based) // position: code point offset (0 based) // index: code point offset (1 based or negative) // function that will count the number of code points (utf-8 characters) from the given beginning to the given end size_t code_point_count(const sass::string& str, size_t start, size_t end) { return utf8::distance(str.begin() + start, str.begin() + end); } size_t code_point_count(const sass::string& str) { return utf8::distance(str.begin(), str.end()); } // function that will return the byte offset at a code point position size_t offset_at_position(const sass::string& str, size_t position) { sass::string::const_iterator it = str.begin(); utf8::advance(it, position, str.end()); return std::distance(str.begin(), it); } // function that returns number of bytes in a character at offset size_t code_point_size_at_offset(const sass::string& str, size_t offset) { // get iterator from string and forward by offset sass::string::const_iterator stop = str.begin() + offset; // check if beyond boundary if (stop == str.end()) return 0; // advance by one code point utf8::advance(stop, 1, str.end()); // calculate offset for code point return stop - str.begin() - offset; } // function that will return a normalized index, given a crazy one size_t normalize_index(int index, size_t len) { long signed_len = static_cast(len); // assuming the index is 1-based // we are returning a 0-based index if (index > 0 && index <= signed_len) { // positive and within string length return index-1; } else if (index > signed_len) { // positive and past string length return len; } else if (index == 0) { return 0; } else if (std::abs((double)index) <= signed_len) { // negative and within string length return index + signed_len; } else { // negative and past string length return 0; } } #ifdef _WIN32 // utf16 functions using std::wstring; // convert from utf16/wide string to utf8 string sass::string convert_from_utf16(const wstring& utf16) { sass::string utf8; // pre-allocate expected memory utf8.reserve(sizeof(utf16)/2); utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(utf8)); return utf8; } // convert from utf8 string to utf16/wide string wstring convert_to_utf16(const sass::string& utf8) { wstring utf16; // pre-allocate expected memory utf16.reserve(code_point_count(utf8)*2); utf8::utf8to16(utf8.begin(), utf8.end(), back_inserter(utf16)); return utf16; } #endif } } golibsass-1.0.0/libsass_src/src/utf8_string.hpp000066400000000000000000000022651405214413600215660ustar00rootroot00000000000000#ifndef SASS_UTF8_STRING_H #define SASS_UTF8_STRING_H #include #include "utf8.h" #include "memory.hpp" namespace Sass { namespace UTF_8 { // naming conventions: // offset: raw byte offset (0 based) // position: code point offset (0 based) // index: code point offset (1 based or negative) // function that will count the number of code points (utf-8 characters) from the beginning to the given end size_t code_point_count(const sass::string& str, size_t start, size_t end); size_t code_point_count(const sass::string& str); // function that will return the byte offset of a code point in a size_t offset_at_position(const sass::string& str, size_t position); // function that returns number of bytes in a character in a string size_t code_point_size_at_offset(const sass::string& str, size_t offset); // function that will return a normalized index, given a crazy one size_t normalize_index(int index, size_t len); #ifdef _WIN32 // functions to handle unicode paths on windows sass::string convert_from_utf16(const std::wstring& wstr); std::wstring convert_to_utf16(const sass::string& str); #endif } } #endif golibsass-1.0.0/libsass_src/src/util.cpp000066400000000000000000000470141405214413600202630ustar00rootroot00000000000000#include "sass.hpp" #include "sass.h" #include "ast.hpp" #include "util.hpp" #include "util_string.hpp" #include "lexer.hpp" #include "prelexer.hpp" #include "constants.hpp" #include "utf8/checked.h" #include #include #if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64) #include #endif namespace Sass { double round(double val, size_t precision) { // Disable FMA3-optimized implementation when compiling with VS2013 for x64 targets // See https://github.com/sass/node-sass/issues/1854 for details // FIXME: Remove this workaround when we switch to VS2015+ #if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64) static std::once_flag flag; std::call_once(flag, []() { _set_FMA3_enable(0); }); #endif // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93 if (std::fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val); else if (std::fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val); // work around some compiler issue // cygwin has it not defined in std using namespace std; return ::round(val); } /* Locale unspecific atof function. */ double sass_strtod(const char *str) { char separator = *(localeconv()->decimal_point); if(separator != '.'){ // The current locale specifies another // separator. convert the separator to the // one understood by the locale if needed const char *found = strchr(str, '.'); if(found != NULL){ // substitution is required. perform the substitution on a copy // of the string. This is slower but it is thread safe. char *copy = sass_copy_c_string(str); *(copy + (found - str)) = separator; double res = strtod(copy, NULL); free(copy); return res; } } return strtod(str, NULL); } // helper for safe access to c_ctx const char* safe_str (const char* str, const char* alt) { return str == NULL ? alt : str; } void free_string_array(char ** arr) { if(!arr) return; char **it = arr; while (it && (*it)) { free(*it); ++it; } free(arr); } char **copy_strings(const sass::vector& strings, char*** array, int skip) { int num = static_cast(strings.size()) - skip; char** arr = (char**) calloc(num + 1, sizeof(char*)); if (arr == 0) return *array = (char **)NULL; for(int i = 0; i < num; i++) { arr[i] = (char*) malloc(sizeof(char) * (strings[i + skip].size() + 1)); if (arr[i] == 0) { free_string_array(arr); return *array = (char **)NULL; } std::copy(strings[i + skip].begin(), strings[i + skip].end(), arr[i]); arr[i][strings[i + skip].size()] = '\0'; } arr[num] = 0; return *array = arr; } // read css string (handle multiline DELIM) sass::string read_css_string(const sass::string& str, bool css) { if (!css) return str; sass::string out(""); bool esc = false; for (auto i : str) { if (i == '\\') { esc = ! esc; } else if (esc && i == '\r') { continue; } else if (esc && i == '\n') { out.resize (out.size () - 1); esc = false; continue; } else { esc = false; } out.push_back(i); } // happens when parsing does not correctly skip // over escaped sequences for ie. interpolations // one example: foo\#{interpolate} // if (esc) out += '\\'; return out; } // double escape all escape sequences // keep unescaped quotes and backslashes sass::string evacuate_escapes(const sass::string& str) { sass::string out(""); bool esc = false; for (auto i : str) { if (i == '\\' && !esc) { out += '\\'; out += '\\'; esc = true; } else if (esc && i == '"') { out += '\\'; out += i; esc = false; } else if (esc && i == '\'') { out += '\\'; out += i; esc = false; } else if (esc && i == '\\') { out += '\\'; out += i; esc = false; } else { esc = false; out += i; } } // happens when parsing does not correctly skip // over escaped sequences for ie. interpolations // one example: foo\#{interpolate} // if (esc) out += '\\'; return out; } // bell characters are replaced with spaces void newline_to_space(sass::string& str) { std::replace(str.begin(), str.end(), '\n', ' '); } // 1. Removes whitespace after newlines. // 2. Replaces newlines with spaces. // // This method only considers LF and CRLF as newlines. sass::string string_to_output(const sass::string& str) { sass::string result; result.reserve(str.size()); std::size_t pos = 0; while (true) { const std::size_t newline = str.find_first_of("\n\r", pos); if (newline == sass::string::npos) break; result.append(str, pos, newline - pos); if (str[newline] == '\r') { if (str[newline + 1] == '\n') { pos = newline + 2; } else { // CR without LF: append as-is and continue. result += '\r'; pos = newline + 1; continue; } } else { pos = newline + 1; } result += ' '; const std::size_t non_space = str.find_first_not_of(" \f\n\r\t\v", pos); if (non_space != sass::string::npos) { pos = non_space; } } result.append(str, pos, sass::string::npos); return result; } sass::string escape_string(const sass::string& str) { sass::string out; out.reserve(str.size()); for (char c : str) { switch (c) { case '\n': out.append("\\n"); break; case '\r': out.append("\\r"); break; case '\f': out.append("\\f"); break; default: out += c; } } return out; } sass::string comment_to_compact_string(const sass::string& text) { sass::string str = ""; size_t has = 0; char prev = 0; bool clean = false; for (auto i : text) { if (clean) { if (i == '\n') { has = 0; } else if (i == '\t') { ++ has; } else if (i == ' ') { ++ has; } else if (i == '*') {} else { clean = false; str += ' '; if (prev == '*' && i == '/') str += "*/"; else str += i; } } else if (i == '\n') { clean = true; } else { str += i; } prev = i; } if (has) return str; else return text; } // find best quote_mark by detecting if the string contains any single // or double quotes. When a single quote is found, we not we want a double // quote as quote_mark. Otherwise we check if the string cotains any double // quotes, which will trigger the use of single quotes as best quote_mark. char detect_best_quotemark(const char* s, char qm) { // ensure valid fallback quote_mark char quote_mark = qm && qm != '*' ? qm : '"'; while (*s) { // force double quotes as soon // as one single quote is found if (*s == '\'') { return '"'; } // a single does not force quote_mark // maybe we see a double quote later else if (*s == '"') { quote_mark = '\''; } ++ s; } return quote_mark; } sass::string read_hex_escapes(const sass::string& s) { sass::string result; bool skipped = false; for (size_t i = 0, L = s.length(); i < L; ++i) { // implement the same strange ruby sass behavior // an escape sequence can also mean a unicode char if (s[i] == '\\' && !skipped) { // remember skipped = true; // escape length size_t len = 1; // parse as many sequence chars as possible // ToDo: Check if ruby aborts after possible max while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast(s[i + len]))) ++ len; if (len > 1) { // convert the extracted hex string to code point value // ToDo: Maybe we could do this without creating a substring uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16); if (s[i + len] == ' ') ++ len; // assert invalid code points if (cp == 0) cp = 0xFFFD; // replace bell character // if (cp == '\n') cp = 32; // use a very simple approach to convert via utf8 lib // maybe there is a more elegant way; maybe we shoud // convert the whole output from string to a stream!? // allocate memory for utf8 char and convert to utf8 unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u); for(size_t m = 0; m < 5 && u[m]; m++) result.push_back(u[m]); // skip some more chars? i += len - 1; skipped = false; } else { skipped = false; result.push_back(s[i]); } } else { result.push_back(s[i]); } } return result; } sass::string unquote(const sass::string& s, char* qd, bool keep_utf8_sequences, bool strict) { // not enough room for quotes // no possibility to unquote if (s.length() < 2) return s; char q; bool skipped = false; // this is no guarantee that the unquoting will work // what about whitespace before/after the quote_mark? if (*s.begin() == '"' && *s.rbegin() == '"') q = '"'; else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\''; else return s; sass::string unq; unq.reserve(s.length()-2); for (size_t i = 1, L = s.length() - 1; i < L; ++i) { // implement the same strange ruby sass behavior // an escape sequence can also mean a unicode char if (s[i] == '\\' && !skipped) { // remember skipped = true; // skip it // ++ i; // if (i == L) break; // escape length size_t len = 1; // parse as many sequence chars as possible // ToDo: Check if ruby aborts after possible max while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast(s[i + len]))) ++ len; // hex string? if (keep_utf8_sequences) { unq.push_back(s[i]); } else if (len > 1) { // convert the extracted hex string to code point value // ToDo: Maybe we could do this without creating a substring uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16); if (s[i + len] == ' ') ++ len; // assert invalid code points if (cp == 0) cp = 0xFFFD; // replace bell character // if (cp == '\n') cp = 32; // use a very simple approach to convert via utf8 lib // maybe there is a more elegant way; maybe we shoud // convert the whole output from string to a stream!? // allocate memory for utf8 char and convert to utf8 unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u); for(size_t m = 0; m < 5 && u[m]; m++) unq.push_back(u[m]); // skip some more chars? i += len - 1; skipped = false; } } // check for unexpected delimiter // be strict and throw error back // else if (!skipped && q == s[i]) { // // don't be that strict // return s; // // this basically always means an internal error and not users fault // error("Unescaped delimiter in string to unquote found. [" + s + "]", SourceSpan("[UNQUOTE]")); // } else { if (strict && !skipped) { if (s[i] == q) return s; } skipped = false; unq.push_back(s[i]); } } if (skipped) { return s; } if (qd) *qd = q; return unq; } sass::string quote(const sass::string& s, char q) { // autodetect with fallback to given quote q = detect_best_quotemark(s.c_str(), q); // return an empty quoted string if (s.empty()) return sass::string(2, q ? q : '"'); sass::string quoted; quoted.reserve(s.length()+2); quoted.push_back(q); const char* it = s.c_str(); const char* end = it + strlen(it) + 1; while (*it && it < end) { const char* now = it; if (*it == q) { quoted.push_back('\\'); } else if (*it == '\\') { quoted.push_back('\\'); } int cp = utf8::next(it, end); // in case of \r, check if the next in sequence // is \n and then advance the iterator and skip \r if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') { cp = utf8::next(it, end); } if (cp == '\n') { quoted.push_back('\\'); quoted.push_back('a'); // we hope we can remove this flag once we figure out // why ruby sass has these different output behaviors // gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ") using namespace Prelexer; if (alternatives < Prelexer::char_range<'a', 'f'>, Prelexer::char_range<'A', 'F'>, Prelexer::char_range<'0', '9'>, space >(it) != NULL) { quoted.push_back(' '); } } else if (cp < 127) { quoted.push_back((char) cp); } else { while (now < it) { quoted.push_back(*now); ++ now; } } } quoted.push_back(q); return quoted; } bool is_hex_doublet(double n) { return n == 0x00 || n == 0x11 || n == 0x22 || n == 0x33 || n == 0x44 || n == 0x55 || n == 0x66 || n == 0x77 || n == 0x88 || n == 0x99 || n == 0xAA || n == 0xBB || n == 0xCC || n == 0xDD || n == 0xEE || n == 0xFF ; } bool is_color_doublet(double r, double g, double b) { return is_hex_doublet(r) && is_hex_doublet(g) && is_hex_doublet(b); } bool peek_linefeed(const char* start) { using namespace Prelexer; using namespace Constants; return sequence < zero_plus < alternatives < exactly <' '>, exactly <'\t'>, line_comment, block_comment, delimited_by < slash_star, star_slash, false > > >, re_linebreak >(start) != 0; } namespace Util { bool isPrintable(StyleRule* r, Sass_Output_Style style) { if (r == NULL) { return false; } Block_Obj b = r->block(); SelectorList* sl = r->selector(); bool hasSelectors = sl ? sl->length() > 0 : false; if (!hasSelectors) { return false; } bool hasDeclarations = false; bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i); if (Cast(stm)) { return true; } else if (Declaration* d = Cast(stm)) { return isPrintable(d, style); } else if (ParentStatement* p = Cast(stm)) { Block_Obj pChildBlock = p->block(); if (isPrintable(pChildBlock, style)) { hasPrintableChildBlocks = true; } } else if (Comment* c = Cast(stm)) { // keep for uncompressed if (style != COMPRESSED) { hasDeclarations = true; } // output style compressed if (c->is_important()) { hasDeclarations = c->is_important(); } } else { hasDeclarations = true; } if (hasDeclarations || hasPrintableChildBlocks) { return true; } } return false; } bool isPrintable(String_Constant* s, Sass_Output_Style style) { return ! s->value().empty(); } bool isPrintable(String_Quoted* s, Sass_Output_Style style) { return true; } bool isPrintable(Declaration* d, Sass_Output_Style style) { ExpressionObj val = d->value(); if (String_Quoted_Obj sq = Cast(val)) return isPrintable(sq.ptr(), style); if (String_Constant_Obj sc = Cast(val)) return isPrintable(sc.ptr(), style); return true; } bool isPrintable(SupportsRule* f, Sass_Output_Style style) { if (f == NULL) { return false; } Block_Obj b = f->block(); bool hasDeclarations = false; bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i); if (Cast(stm) || Cast(stm)) { hasDeclarations = true; } else if (ParentStatement* b = Cast(stm)) { Block_Obj pChildBlock = b->block(); if (!b->is_invisible()) { if (isPrintable(pChildBlock, style)) { hasPrintableChildBlocks = true; } } } if (hasDeclarations || hasPrintableChildBlocks) { return true; } } return false; } bool isPrintable(CssMediaRule* m, Sass_Output_Style style) { if (m == nullptr) return false; Block_Obj b = m->block(); if (b == nullptr) return false; if (m->empty()) return false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i); if (Cast(stm)) return true; else if (Cast(stm)) return true; else if (Comment* c = Cast(stm)) { if (isPrintable(c, style)) { return true; } } else if (StyleRule* r = Cast(stm)) { if (isPrintable(r, style)) { return true; } } else if (SupportsRule* f = Cast(stm)) { if (isPrintable(f, style)) { return true; } } else if (CssMediaRule* mb = Cast(stm)) { if (isPrintable(mb, style)) { return true; } } else if (ParentStatement* b = Cast(stm)) { if (isPrintable(b->block(), style)) { return true; } } } return false; } bool isPrintable(Comment* c, Sass_Output_Style style) { // keep for uncompressed if (style != COMPRESSED) { return true; } // output style compressed if (c->is_important()) { return true; } // not printable return false; }; bool isPrintable(Block_Obj b, Sass_Output_Style style) { if (!b) { return false; } for (size_t i = 0, L = b->length(); i < L; ++i) { Statement_Obj stm = b->at(i); if (Cast(stm) || Cast(stm)) { return true; } else if (Comment* c = Cast(stm)) { if (isPrintable(c, style)) { return true; } } else if (StyleRule* r = Cast(stm)) { if (isPrintable(r, style)) { return true; } } else if (SupportsRule* f = Cast(stm)) { if (isPrintable(f, style)) { return true; } } else if (CssMediaRule * m = Cast(stm)) { if (isPrintable(m, style)) { return true; } } else if (ParentStatement* b = Cast(stm)) { if (isPrintable(b->block(), style)) { return true; } } } return false; } } } golibsass-1.0.0/libsass_src/src/util.hpp000066400000000000000000000071601405214413600202660ustar00rootroot00000000000000#ifndef SASS_UTIL_H #define SASS_UTIL_H // sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "sass/base.h" #include "ast_fwd_decl.hpp" #include #include #include #include #include #define SASS_ASSERT(cond, msg) assert(cond && msg) namespace Sass { template T clip(const T& n, const T& lower, const T& upper) { return std::max(lower, std::min(n, upper)); } template T absmod(const T& n, const T& r) { T m = std::fmod(n, r); if (m < 0.0) m += r; return m; } double round(double val, size_t precision = 0); double sass_strtod(const char* str); const char* safe_str(const char *, const char* = ""); void free_string_array(char **); char **copy_strings(const sass::vector&, char ***, int = 0); sass::string read_css_string(const sass::string& str, bool css = true); sass::string evacuate_escapes(const sass::string& str); sass::string string_to_output(const sass::string& str); sass::string comment_to_compact_string(const sass::string& text); sass::string read_hex_escapes(const sass::string& str); sass::string escape_string(const sass::string& str); void newline_to_space(sass::string& str); sass::string quote(const sass::string&, char q = 0); sass::string unquote(const sass::string&, char* q = 0, bool keep_utf8_sequences = false, bool strict = true); char detect_best_quotemark(const char* s, char qm = '"'); bool is_hex_doublet(double n); bool is_color_doublet(double r, double g, double b); bool peek_linefeed(const char* start); // Returns true iff `elements` ⊆ `container`. template bool contains_all(C container, T elements) { for (const auto &el : elements) { if (container.find(el) == container.end()) return false; } return true; } // C++20 `starts_with` equivalent. // See https://en.cppreference.com/w/cpp/string/basic_string/starts_with inline bool starts_with(const sass::string& str, const char* prefix, size_t prefix_len) { return str.compare(0, prefix_len, prefix) == 0; } inline bool starts_with(const sass::string& str, const char* prefix) { return starts_with(str, prefix, std::strlen(prefix)); } // C++20 `ends_with` equivalent. // See https://en.cppreference.com/w/cpp/string/basic_string/ends_with inline bool ends_with(const sass::string& str, const sass::string& suffix) { return suffix.size() <= str.size() && std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()); } inline bool ends_with(const sass::string& str, const char* suffix, size_t suffix_len) { if (suffix_len > str.size()) return false; const char* suffix_it = suffix + suffix_len; const char* str_it = str.c_str() + str.size(); while (suffix_it != suffix) if (*(--suffix_it) != *(--str_it)) return false; return true; } inline bool ends_with(const sass::string& str, const char* suffix) { return ends_with(str, suffix, std::strlen(suffix)); } namespace Util { bool isPrintable(StyleRule* r, Sass_Output_Style style = NESTED); bool isPrintable(SupportsRule* r, Sass_Output_Style style = NESTED); bool isPrintable(CssMediaRule* r, Sass_Output_Style style = NESTED); bool isPrintable(Comment* b, Sass_Output_Style style = NESTED); bool isPrintable(Block_Obj b, Sass_Output_Style style = NESTED); bool isPrintable(String_Constant* s, Sass_Output_Style style = NESTED); bool isPrintable(String_Quoted* s, Sass_Output_Style style = NESTED); bool isPrintable(Declaration* d, Sass_Output_Style style = NESTED); } } #endif golibsass-1.0.0/libsass_src/src/util_string.cpp000066400000000000000000000073221405214413600216470ustar00rootroot00000000000000#include "util_string.hpp" #include #include namespace Sass { namespace Util { // ########################################################################## // Special case insensitive string matcher. We can optimize // the more general compare case quite a bit by requiring // consumers to obey some rules (lowercase and no space). // - `literal` must only contain lower case ascii characters // there is one edge case where this could give false positives // test could contain a (non-ascii) char exactly 32 below literal // ########################################################################## bool equalsLiteral(const char* lit, const sass::string& test) { // Work directly on characters const char* src = test.c_str(); // There is a small chance that the search string // Is longer than the rest of the string to look at while (*lit && (*src == *lit || *src + 32 == *lit)) { ++src, ++lit; } // True if literal is at end // If not test was too long return *lit == 0; } void ascii_str_tolower(sass::string* s) { for (auto& ch : *s) { ch = ascii_tolower(static_cast(ch)); } } void ascii_str_toupper(sass::string* s) { for (auto& ch : *s) { ch = ascii_toupper(static_cast(ch)); } } sass::string rtrim(sass::string str) { auto it = std::find_if_not(str.rbegin(), str.rend(), ascii_isspace); str.erase(str.rend() - it); return str; } // ########################################################################### // Returns [name] without a vendor prefix. // If [name] has no vendor prefix, it's returned as-is. // ########################################################################### sass::string unvendor(const sass::string& name) { if (name.size() < 2) return name; if (name[0] != '-') return name; if (name[1] == '-') return name; for (size_t i = 2; i < name.size(); i++) { if (name[i] == '-') return name.substr(i + 1); } return name; } // EO unvendor sass::string normalize_newlines(const sass::string& str) { sass::string result; result.reserve(str.size()); std::size_t pos = 0; while (true) { const std::size_t newline = str.find_first_of("\n\f\r", pos); if (newline == sass::string::npos) break; result.append(str, pos, newline - pos); result += '\n'; if (str[newline] == '\r' && str[newline + 1] == '\n') { pos = newline + 2; } else { pos = newline + 1; } } result.append(str, pos, sass::string::npos); return result; } sass::string normalize_underscores(const sass::string& str) { sass::string normalized = str; std::replace(normalized.begin(), normalized.end(), '_', '-'); return normalized; } sass::string normalize_decimals(const sass::string& str) { sass::string normalized; if (!str.empty() && str[0] == '.') { normalized.reserve(str.size() + 1); normalized += '0'; normalized += str; } else { normalized = str; } return normalized; } char opening_bracket_for(char closing_bracket) { switch (closing_bracket) { case ')': return '('; case ']': return '['; case '}': return '{'; default: return '\0'; } } char closing_bracket_for(char opening_bracket) { switch (opening_bracket) { case '(': return ')'; case '[': return ']'; case '{': return '}'; default: return '\0'; } } } // namespace Util } // namespace Sass golibsass-1.0.0/libsass_src/src/util_string.hpp000066400000000000000000000047611405214413600216600ustar00rootroot00000000000000#ifndef SASS_UTIL_STRING_H #define SASS_UTIL_STRING_H #include "sass.hpp" #include namespace Sass { namespace Util { // ########################################################################## // Special case insensitive string matcher. We can optimize // the more general compare case quite a bit by requiring // consumers to obey some rules (lowercase and no space). // - `literal` must only contain lower case ascii characters // there is one edge case where this could give false positives // test could contain a (non-ascii) char exactly 32 below literal // ########################################################################## bool equalsLiteral(const char* lit, const sass::string& test); // ########################################################################### // Returns [name] without a vendor prefix. // If [name] has no vendor prefix, it's returned as-is. // ########################################################################### sass::string unvendor(const sass::string& name); sass::string rtrim(sass::string str); sass::string normalize_newlines(const sass::string& str); sass::string normalize_underscores(const sass::string& str); sass::string normalize_decimals(const sass::string& str); char opening_bracket_for(char closing_bracket); char closing_bracket_for(char opening_bracket); // Locale-independent ASCII character routines. inline bool ascii_isalpha(unsigned char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } inline bool ascii_isdigit(unsigned char c) { return (c >= '0' && c <= '9'); } inline bool ascii_isalnum(unsigned char c) { return ascii_isalpha(c) || ascii_isdigit(c); } inline bool ascii_isascii(unsigned char c) { return c < 128; } inline bool ascii_isxdigit(unsigned char c) { return ascii_isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } inline bool ascii_isspace(unsigned char c) { return c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r' || c == '\n'; } inline char ascii_tolower(unsigned char c) { if (c >= 'A' && c <= 'Z') return c + 32; return c; } void ascii_str_tolower(sass::string* s); inline char ascii_toupper(unsigned char c) { if (c >= 'a' && c <= 'z') return c - 32; return c; } void ascii_str_toupper(sass::string* s); } // namespace Sass } // namespace Util #endif // SASS_UTIL_STRING_H golibsass-1.0.0/libsass_src/src/values.cpp000066400000000000000000000117401405214413600206020ustar00rootroot00000000000000// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "sass.h" #include "values.hpp" #include namespace Sass { // convert value from C++ side to C-API union Sass_Value* ast_node_to_sass_value (const Expression* val) { if (val->concrete_type() == Expression::NUMBER) { const Number* res = Cast(val); return sass_make_number(res->value(), res->unit().c_str()); } else if (val->concrete_type() == Expression::COLOR) { if (const Color_RGBA* rgba = Cast(val)) { return sass_make_color(rgba->r(), rgba->g(), rgba->b(), rgba->a()); } else { // ToDo: allow to also use HSLA colors!! Color_RGBA_Obj col = Cast(val)->copyAsRGBA(); return sass_make_color(col->r(), col->g(), col->b(), col->a()); } } else if (val->concrete_type() == Expression::LIST) { const List* l = Cast(val); union Sass_Value* list = sass_make_list(l->size(), l->separator(), l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { ExpressionObj obj = l->at(i); auto val = ast_node_to_sass_value(obj); sass_list_set_value(list, i, val); } return list; } else if (val->concrete_type() == Expression::MAP) { const Map* m = Cast(val); union Sass_Value* map = sass_make_map(m->length()); size_t i = 0; for (ExpressionObj key : m->keys()) { sass_map_set_key(map, i, ast_node_to_sass_value(key)); sass_map_set_value(map, i, ast_node_to_sass_value(m->at(key))); ++ i; } return map; } else if (val->concrete_type() == Expression::NULL_VAL) { return sass_make_null(); } else if (val->concrete_type() == Expression::BOOLEAN) { const Boolean* res = Cast(val); return sass_make_boolean(res->value()); } else if (val->concrete_type() == Expression::STRING) { if (const String_Quoted* qstr = Cast(val)) { return sass_make_qstring(qstr->value().c_str()); } else if (const String_Constant* cstr = Cast(val)) { return sass_make_string(cstr->value().c_str()); } } return sass_make_error("unknown sass value type"); } // convert value from C-API to C++ side Value* sass_value_to_ast_node (const union Sass_Value* val) { switch (sass_value_get_tag(val)) { case SASS_NUMBER: return SASS_MEMORY_NEW(Number, SourceSpan("[C-VALUE]"), sass_number_get_value(val), sass_number_get_unit(val)); case SASS_BOOLEAN: return SASS_MEMORY_NEW(Boolean, SourceSpan("[C-VALUE]"), sass_boolean_get_value(val)); case SASS_COLOR: // ToDo: allow to also use HSLA colors!! return SASS_MEMORY_NEW(Color_RGBA, SourceSpan("[C-VALUE]"), sass_color_get_r(val), sass_color_get_g(val), sass_color_get_b(val), sass_color_get_a(val)); case SASS_STRING: if (sass_string_is_quoted(val)) { return SASS_MEMORY_NEW(String_Quoted, SourceSpan("[C-VALUE]"), sass_string_get_value(val)); } return SASS_MEMORY_NEW(String_Constant, SourceSpan("[C-VALUE]"), sass_string_get_value(val)); case SASS_LIST: { List* l = SASS_MEMORY_NEW(List, SourceSpan("[C-VALUE]"), sass_list_get_length(val), sass_list_get_separator(val)); for (size_t i = 0, L = sass_list_get_length(val); i < L; ++i) { l->append(sass_value_to_ast_node(sass_list_get_value(val, i))); } l->is_bracketed(sass_list_get_is_bracketed(val)); return l; } case SASS_MAP: { Map* m = SASS_MEMORY_NEW(Map, SourceSpan("[C-VALUE]")); for (size_t i = 0, L = sass_map_get_length(val); i < L; ++i) { *m << std::make_pair( sass_value_to_ast_node(sass_map_get_key(val, i)), sass_value_to_ast_node(sass_map_get_value(val, i))); } return m; } case SASS_NULL: return SASS_MEMORY_NEW(Null, SourceSpan("[C-VALUE]")); case SASS_ERROR: return SASS_MEMORY_NEW(Custom_Error, SourceSpan("[C-VALUE]"), sass_error_get_message(val)); case SASS_WARNING: return SASS_MEMORY_NEW(Custom_Warning, SourceSpan("[C-VALUE]"), sass_warning_get_message(val)); default: break; } return 0; } } golibsass-1.0.0/libsass_src/src/values.hpp000066400000000000000000000003401405214413600206010ustar00rootroot00000000000000#ifndef SASS_VALUES_H #define SASS_VALUES_H #include "ast.hpp" namespace Sass { union Sass_Value* ast_node_to_sass_value (const Expression* val); Value* sass_value_to_ast_node (const union Sass_Value* val); } #endif golibsass-1.0.0/libsass_src/test/000077500000000000000000000000001405214413600167645ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/test/Makefile000066400000000000000000000015751405214413600204340ustar00rootroot00000000000000CXX ?= c++ CXXFLAGS := -I ../include/ -g -O1 -fno-omit-frame-pointer LIBSASS_CPPSTD ?= c++11 CXXFLAGS += -std=$(LIBSASS_CPPSTD) LDFLAGS += -std=$(LIBSASS_CPPSTD) test: test_shared_ptr test_util_string test_shared_ptr: build/test_shared_ptr @ASAN_OPTIONS="symbolize=1" build/test_shared_ptr test_util_string: build/test_util_string @ASAN_OPTIONS="symbolize=1" build/test_util_string build: @mkdir build build/test_shared_ptr: test_shared_ptr.cpp ../src/memory/shared_ptr.cpp | build $(CXX) $(CXXFLAGS) ../src/memory/allocator.cpp ../src/memory/shared_ptr.cpp -o build/test_shared_ptr test_shared_ptr.cpp build/test_util_string: test_util_string.cpp ../src/util_string.cpp | build $(CXX) $(CXXFLAGS) ../src/memory/allocator.cpp ../src/util_string.cpp -o build/test_util_string test_util_string.cpp clean: | build rm -rf build .PHONY: test test_shared_ptr test_util_string clean golibsass-1.0.0/libsass_src/test/go.mod000066400000000000000000000000001405214413600200600ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/test/test_shared_ptr.cpp000066400000000000000000000102071405214413600226620ustar00rootroot00000000000000#include "../src/memory/allocator.hpp" #include "../src/memory/shared_ptr.hpp" #include #include #include #include #define ASSERT(cond) \ if (!(cond)) { \ std::cerr << "Assertion failed: " #cond " at " __FILE__ << ":" << __LINE__ << std::endl; \ return false; \ } \ class TestObj : public Sass::SharedObj { public: TestObj(bool *destroyed) : destroyed_(destroyed) {} ~TestObj() { *destroyed_ = true; } Sass::sass::string to_string() const { Sass::sass::ostream result; result << "refcount=" << refcount << " destroyed=" << *destroyed_; return result.str(); } private: bool *destroyed_; }; using SharedTestObj = Sass::SharedImpl; bool TestOneSharedPtr() { bool destroyed = false; { SharedTestObj a = SASS_MEMORY_NEW(TestObj, &destroyed); } ASSERT(destroyed); return true; } bool TestTwoSharedPtrs() { bool destroyed = false; { SharedTestObj a = SASS_MEMORY_NEW(TestObj, &destroyed); { SharedTestObj b = a; } ASSERT(!destroyed); } ASSERT(destroyed); return true; } bool TestSelfAssignment() { bool destroyed = false; { SharedTestObj a = SASS_MEMORY_NEW(TestObj, &destroyed); a = a; ASSERT(!destroyed); } ASSERT(destroyed); return true; } bool TestPointerAssignment() { bool destroyed = false; std::unique_ptr ptr(new TestObj(&destroyed)); { SharedTestObj a = ptr.get(); } ASSERT(destroyed); ptr.release(); return true; } bool TestOneSharedPtrDetach() { bool destroyed = false; std::unique_ptr ptr(new TestObj(&destroyed)); { SharedTestObj a = ptr.get(); a.detach(); } ASSERT(!destroyed); return true; } bool TestTwoSharedPtrsDetach() { bool destroyed = false; std::unique_ptr ptr(new TestObj(&destroyed)); { SharedTestObj a = ptr.get(); { SharedTestObj b = a; b.detach(); } ASSERT(!destroyed); a.detach(); } ASSERT(!destroyed); return true; } bool TestSelfAssignDetach() { bool destroyed = false; std::unique_ptr ptr(new TestObj(&destroyed)); { SharedTestObj a = ptr.get(); a = a.detach(); ASSERT(!destroyed); } ASSERT(destroyed); ptr.release(); return true; } bool TestDetachedPtrIsNotDestroyedUntilAssignment() { bool destroyed = false; std::unique_ptr ptr(new TestObj(&destroyed)); { SharedTestObj a = ptr.get(); SharedTestObj b = a; ASSERT(a.detach() == ptr.get()); ASSERT(!destroyed); } ASSERT(!destroyed); { SharedTestObj c = ptr.get(); ASSERT(!destroyed); } ASSERT(destroyed); ptr.release(); return true; } bool TestDetachNull() { SharedTestObj a; ASSERT(a.detach() == nullptr); return true; } class EmptyTestObj : public Sass::SharedObj { public: Sass::sass::string to_string() const { return ""; } }; bool TestComparisonWithSharedPtr() { Sass::SharedImpl a = new EmptyTestObj(); ASSERT(a == a); Sass::SharedImpl b = a; ASSERT(a == b); Sass::SharedImpl c = new EmptyTestObj(); ASSERT(a != c); Sass::SharedImpl nullobj; ASSERT(a != nullobj); ASSERT(nullobj == nullobj); return true; } bool TestComparisonWithNullptr() { Sass::SharedImpl a = new EmptyTestObj(); ASSERT(a != nullptr); Sass::SharedImpl nullobj; ASSERT(nullobj == nullptr); return true; } #define TEST(fn) \ if (fn()) { \ passed.push_back(#fn); \ } else { \ failed.push_back(#fn); \ std::cerr << "Failed: " #fn << std::endl; \ } \ int main(int argc, char **argv) { std::vector passed; std::vector failed; TEST(TestOneSharedPtr); TEST(TestTwoSharedPtrs); TEST(TestSelfAssignment); TEST(TestPointerAssignment); TEST(TestOneSharedPtrDetach); TEST(TestTwoSharedPtrsDetach); TEST(TestSelfAssignDetach); TEST(TestDetachedPtrIsNotDestroyedUntilAssignment); TEST(TestDetachNull); TEST(TestComparisonWithSharedPtr); TEST(TestComparisonWithNullptr); std::cerr << argv[0] << ": Passed: " << passed.size() << ", failed: " << failed.size() << "." << std::endl; return failed.size(); } golibsass-1.0.0/libsass_src/test/test_util_string.cpp000066400000000000000000000145151405214413600231000ustar00rootroot00000000000000#include "../src/util_string.hpp" #include #include #include #include namespace { Sass::sass::string escape_string(const Sass::sass::string& str) { Sass::sass::string out; out.reserve(str.size()); for (char c : str) { switch (c) { case '\n': out.append("\\n"); break; case '\r': out.append("\\r"); break; case '\f': out.append("\\f"); break; default: out += c; } } return out; } #define ASSERT_TRUE(cond) \ if (!cond) { \ std::cerr << \ "Expected condition to be true at " << __FILE__ << ":" << __LINE__ << \ std::endl; \ return false; \ } \ #define ASSERT_FALSE(cond) \ ASSERT_TRUE(!(cond)) \ #define ASSERT_STR_EQ(a, b) \ if (a != b) { \ std::cerr << \ "Expected LHS == RHS at " << __FILE__ << ":" << __LINE__ << \ "\n LHS: [" << escape_string(a) << "]" \ "\n RHS: [" << escape_string(b) << "]" << \ std::endl; \ return false; \ } \ bool TestNormalizeNewlinesNoNewline() { Sass::sass::string input = "a"; Sass::sass::string normalized = Sass::Util::normalize_newlines(input); ASSERT_STR_EQ(input, normalized); return true; } bool TestNormalizeNewlinesLF() { Sass::sass::string input = "a\nb"; Sass::sass::string normalized = Sass::Util::normalize_newlines(input); ASSERT_STR_EQ(input, normalized); return true; } bool TestNormalizeNewlinesCR() { Sass::sass::string normalized = Sass::Util::normalize_newlines("a\rb"); ASSERT_STR_EQ("a\nb", normalized); return true; } bool TestNormalizeNewlinesCRLF() { Sass::sass::string normalized = Sass::Util::normalize_newlines("a\r\nb\r\n"); ASSERT_STR_EQ("a\nb\n", normalized); return true; } bool TestNormalizeNewlinesFF() { Sass::sass::string normalized = Sass::Util::normalize_newlines("a\fb\f"); ASSERT_STR_EQ("a\nb\n", normalized); return true; } bool TestNormalizeNewlinesMixed() { Sass::sass::string normalized = Sass::Util::normalize_newlines("a\fb\nc\rd\r\ne\ff"); ASSERT_STR_EQ("a\nb\nc\nd\ne\nf", normalized); return true; } bool TestNormalizeUnderscores() { Sass::sass::string normalized = Sass::Util::normalize_underscores("a_b_c"); ASSERT_STR_EQ("a-b-c", normalized); return true; } bool TestNormalizeDecimalsLeadingZero() { Sass::sass::string normalized = Sass::Util::normalize_decimals("0.5"); ASSERT_STR_EQ("0.5", normalized); return true; } bool TestNormalizeDecimalsNoLeadingZero() { Sass::sass::string normalized = Sass::Util::normalize_decimals(".5"); ASSERT_STR_EQ("0.5", normalized); return true; } bool testEqualsLiteral() { ASSERT_TRUE(Sass::Util::equalsLiteral("moz", "moz")); ASSERT_TRUE(Sass::Util::equalsLiteral(":moz", ":moz")); ASSERT_FALSE(Sass::Util::equalsLiteral("moz", ":moz")); ASSERT_FALSE(Sass::Util::equalsLiteral(":moz", "moz")); ASSERT_TRUE(Sass::Util::equalsLiteral("moz-foo", "MOZ-foo")); ASSERT_FALSE(Sass::Util::equalsLiteral("moz-foo", "moz_foo")); ASSERT_TRUE(Sass::Util::equalsLiteral("moz-foo", "MOZ-FOOS")); ASSERT_FALSE(Sass::Util::equalsLiteral("moz-foos", "moz-foo")); ASSERT_FALSE(Sass::Util::equalsLiteral("-moz-foo", "moz-foo")); return true; } bool TestUnvendor() { // Generated by using dart sass ASSERT_STR_EQ("moz", Sass::Util::unvendor("moz")); ASSERT_STR_EQ(":moz", Sass::Util::unvendor(":moz")); ASSERT_STR_EQ("-moz", Sass::Util::unvendor("-moz")); ASSERT_STR_EQ("--moz", Sass::Util::unvendor("--moz")); ASSERT_STR_EQ("moz-bar", Sass::Util::unvendor("moz-bar")); ASSERT_STR_EQ("bar", Sass::Util::unvendor("-moz-bar")); ASSERT_STR_EQ("bar-", Sass::Util::unvendor("-moz-bar-")); ASSERT_STR_EQ("--moz-bar", Sass::Util::unvendor("--moz-bar")); ASSERT_STR_EQ("-bar", Sass::Util::unvendor("-moz--bar")); ASSERT_STR_EQ("any", Sass::Util::unvendor("-s-any")); ASSERT_STR_EQ("any-more", Sass::Util::unvendor("-s-any-more")); ASSERT_STR_EQ("any--more", Sass::Util::unvendor("-s-any--more")); ASSERT_STR_EQ("--s-any--more", Sass::Util::unvendor("--s-any--more")); ASSERT_STR_EQ("s-any--more", Sass::Util::unvendor("s-any--more")); ASSERT_STR_EQ("_s_any_more", Sass::Util::unvendor("_s_any_more")); ASSERT_STR_EQ("more", Sass::Util::unvendor("-s_any-more")); ASSERT_STR_EQ("any_more", Sass::Util::unvendor("-s-any_more")); ASSERT_STR_EQ("_s_any_more", Sass::Util::unvendor("_s_any_more")); return true; } bool Test_ascii_str_to_lower() { Sass::sass::string str = "A B"; Sass::Util::ascii_str_tolower(&str); ASSERT_STR_EQ("a b", str); return true; } bool Test_ascii_str_to_upper() { Sass::sass::string str = "a b"; Sass::Util::ascii_str_toupper(&str); ASSERT_STR_EQ("A B", str); return true; } bool Test_ascii_isalpha() { ASSERT_TRUE(Sass::Util::ascii_isalpha('a')); ASSERT_FALSE(Sass::Util::ascii_isalpha('3')); return true; } bool Test_ascii_isxdigit() { ASSERT_TRUE(Sass::Util::ascii_isxdigit('a')); ASSERT_TRUE(Sass::Util::ascii_isxdigit('F')); ASSERT_TRUE(Sass::Util::ascii_isxdigit('3')); ASSERT_FALSE(Sass::Util::ascii_isxdigit('G')); return true; } bool Test_ascii_isspace() { ASSERT_TRUE(Sass::Util::ascii_isspace(' ')); ASSERT_TRUE(Sass::Util::ascii_isspace('\t')); ASSERT_TRUE(Sass::Util::ascii_isspace('\v')); ASSERT_TRUE(Sass::Util::ascii_isspace('\f')); ASSERT_TRUE(Sass::Util::ascii_isspace('\r')); ASSERT_TRUE(Sass::Util::ascii_isspace('\n')); ASSERT_FALSE(Sass::Util::ascii_isspace('G')); return true; } } // namespace #define TEST(fn) \ if (fn()) { \ passed.push_back(#fn); \ } else { \ failed.push_back(#fn); \ std::cerr << "Failed: " #fn << std::endl; \ } \ int main(int argc, char **argv) { std::vector passed; std::vector failed; TEST(TestNormalizeNewlinesNoNewline); TEST(TestNormalizeNewlinesLF); TEST(TestNormalizeNewlinesCR); TEST(TestNormalizeNewlinesCRLF); TEST(TestNormalizeNewlinesFF); TEST(TestNormalizeNewlinesMixed); TEST(TestNormalizeUnderscores); TEST(TestNormalizeDecimalsLeadingZero); TEST(TestNormalizeDecimalsNoLeadingZero); TEST(testEqualsLiteral); TEST(TestUnvendor); TEST(Test_ascii_str_to_lower); TEST(Test_ascii_str_to_upper); TEST(Test_ascii_isalpha); TEST(Test_ascii_isxdigit); TEST(Test_ascii_isspace); std::cerr << argv[0] << ": Passed: " << passed.size() << ", failed: " << failed.size() << "." << std::endl; return failed.size(); } golibsass-1.0.0/libsass_src/utils/000077500000000000000000000000001405214413600171455ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/utils/README.md000066400000000000000000000003471405214413600204300ustar00rootroot00000000000000This directory contains some utilities that are not essential for LibSass. # perl update-builds.pl This will update 3rd party build files from `Makefile.conf`, which is the master index file for all required sources and headers. golibsass-1.0.0/libsass_src/utils/build-skeletons/000077500000000000000000000000001405214413600222515ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/utils/build-skeletons/libsass.targets000066400000000000000000000006531405214413600253100ustar00rootroot00000000000000 {{includes}} {{headers}} {{sources}} golibsass-1.0.0/libsass_src/utils/build-skeletons/libsass.vcxproj.filters000066400000000000000000000017051405214413600270000ustar00rootroot00000000000000 {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;in;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {{includes}} {{headers}} {{sources}} golibsass-1.0.0/libsass_src/utils/update-builds.pl000066400000000000000000000060601405214413600222460ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; # Installed via `cpan install File::Slurp` # Alternative `cpanm install File::Slurp` use File::Slurp qw(read_file write_file); my $tmpl_msvc_head = < EOTMPL my $tmpl_msvc_inc = < EOTMPL my $tmpl_msvc_src = < EOTMPL my $tmpl_msvc_filter_head = < Library Includes EOTMPL my $tmpl_msvc_filter_inc = < LibSass Headers EOTMPL my $tmpl_msvc_filter_src = < LibSass Sources EOTMPL # parse source files directly from libsass makefile open(my $fh, "<", "../Makefile.conf") or die "../Makefile.conf not found"; my $srcfiles = join "", <$fh>; close $fh; my (@INCFILES, @HPPFILES, @SOURCES, @CSOURCES); # parse variable out (this is hopefully tolerant enough) if ($srcfiles =~ /^\s*INCFILES\s*=\s*((?:.*(?:\\\r?\n))*.*)/m) { @INCFILES = grep { $_ } split /(?:\s|\\\r?\n)+/, $1; } else { die "Did not find c++ INCFILES in libsass/Makefile.conf"; } if ($srcfiles =~ /^\s*HPPFILES\s*=\s*((?:.*(?:\\\r?\n))*.*)/m) { @HPPFILES = grep { $_ } split /(?:\s|\\\r?\n)+/, $1; } else { die "Did not find c++ HPPFILES in libsass/Makefile.conf"; } if ($srcfiles =~ /^\s*SOURCES\s*=\s*((?:.*(?:\\\r?\n))*.*)/m) { @SOURCES = grep { $_ } split /(?:\s|\\\r?\n)+/, $1; } else { die "Did not find c++ SOURCES in libsass/Makefile.conf"; } if ($srcfiles =~ /^\s*CSOURCES\s*=\s*((?:.*(?:\\\r?\n))*.*)/m) { @CSOURCES = grep { $_ } split /(?:\s|\\\r?\n)+/, $1; } else { die "Did not find c++ CSOURCES in libsass/Makefile.conf"; } sub renderTemplate($@) { my $str = "\n"; my $tmpl = shift; foreach my $inc (@_) { $str .= sprintf($tmpl, $inc); } $str .= " "; return $str; } @INCFILES = map { s/\//\\/gr } @INCFILES; @HPPFILES = map { s/\//\\/gr } @HPPFILES; @SOURCES = map { s/\//\\/gr } @SOURCES; @CSOURCES = map { s/\//\\/gr } @CSOURCES; my $targets = read_file("build-skeletons/libsass.targets"); $targets =~s /\{\{includes\}\}/renderTemplate($tmpl_msvc_inc, @INCFILES)/eg; $targets =~s /\{\{headers\}\}/renderTemplate($tmpl_msvc_head, @HPPFILES)/eg; $targets =~s /\{\{sources\}\}/renderTemplate($tmpl_msvc_src, @SOURCES, @CSOURCES)/eg; warn "Generating ../win/libsass.targets\n"; write_file("../win/libsass.targets", $targets); my $filters = read_file("build-skeletons/libsass.vcxproj.filters"); $filters =~s /\{\{includes\}\}/renderTemplate($tmpl_msvc_filter_inc, @INCFILES)/eg; $filters =~s /\{\{headers\}\}/renderTemplate($tmpl_msvc_filter_head, @HPPFILES)/eg; $filters =~s /\{\{sources\}\}/renderTemplate($tmpl_msvc_filter_src, @SOURCES, @CSOURCES)/eg; warn "Generating ../win/libsass.vcxproj.filters\n"; write_file("../win/libsass.vcxproj.filters", $filters); golibsass-1.0.0/libsass_src/version.sh000077500000000000000000000004541405214413600200340ustar00rootroot00000000000000if test "x$LIBSASS_VERSION" = "x"; then LIBSASS_VERSION=`git describe --abbrev=4 --dirty --always --tags 2>/dev/null` fi if test "x$LIBSASS_VERSION" = "x"; then LIBSASS_VERSION=`cat VERSION 2>/dev/null` fi if test "x$LIBSASS_VERSION" = "x"; then LIBSASS_VERSION="[na]" fi echo $LIBSASS_VERSION golibsass-1.0.0/libsass_src/win/000077500000000000000000000000001405214413600166025ustar00rootroot00000000000000golibsass-1.0.0/libsass_src/win/libsass.sln000066400000000000000000000033321405214413600207610ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsass", "libsass.vcxproj", "{E4030474-AFC9-4CC6-BEB6-D846F631502B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".SolutionItems", ".SolutionItems", "{33318C77-2391-4399-8118-C109155A4A75}" ProjectSection(SolutionItems) = preProject ..\.editorconfig = ..\.editorconfig ..\.gitattributes = ..\.gitattributes ..\.gitignore = ..\.gitignore ..\.travis.yml = ..\.travis.yml ..\appveyor.yml = ..\appveyor.yml ..\Readme.md = ..\Readme.md ..\res\resource.rc = ..\res\resource.rc EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|Win64 = Debug|Win64 Release|Win32 = Release|Win32 Release|Win64 = Release|Win64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win32.ActiveCfg = Debug|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win32.Build.0 = Debug|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win64.ActiveCfg = Debug|x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win64.Build.0 = Debug|x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win32.ActiveCfg = Release|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win32.Build.0 = Release|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win64.ActiveCfg = Release|x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal golibsass-1.0.0/libsass_src/win/libsass.sln.DotSettings000066400000000000000000000045061405214413600232330ustar00rootroot00000000000000 ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcludedgolibsass-1.0.0/libsass_src/win/libsass.targets000066400000000000000000000225451405214413600216450ustar00rootroot00000000000000 golibsass-1.0.0/libsass_src/win/libsass.vcxproj000066400000000000000000000220321405214413600216560ustar00rootroot00000000000000 [NA] ..\src ..\src ..\include %(PreprocessorDefinitions);LIBSASS_VERSION="$(LIBSASS_VERSION)"; Debug Win32 Debug x64 Release Win32 Release x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B} Win32Proj libsass libsass Unicode DynamicLibrary ADD_EXPORTS;$(PreprocessorDefinitions); StaticLibrary v120 v140 v141 v142 true true false true false true true $(SolutionDir)bin\Debug\ $(SolutionDir)bin\Debug\obj\ true $(SolutionDir)bin\Debug\ $(SolutionDir)bin\Debug\obj\ false $(SolutionDir)bin\ $(SolutionDir)bin\obj\ false $(SolutionDir)bin\ $(SolutionDir)bin\obj\ ..\include;%(AdditionalIncludeDirectories) Level3 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true true true golibsass-1.0.0/libsass_src/win/libsass.vcxproj.filters000066400000000000000000000432471405214413600233400ustar00rootroot00000000000000 {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;in;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx LibSass Headers LibSass Headers LibSass Headers LibSass Headers LibSass Headers LibSass Headers LibSass Headers Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes Library Includes LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources LibSass Sources golibsass-1.0.0/pull-libsass.sh000077500000000000000000000002541405214413600164500ustar00rootroot00000000000000#!/bin/bash if [ "$1" = "" ] then echo "Usage: $0 " exit fi git subtree pull --prefix libsass_src https://github.com/sass/libsass.git $1 --squash