Example #1
0
func (pkgr *Packager) resolveFunctions(dirName, fileName string) {
	// the existence of `fileName` should be verified by the caller
	relPath := filepath.Join(dirName, fileName)
	defs := parser.ParseFile(pkgr.MixerDir, dirName, fileName, true, make([]string, 0))

	for _, f := range defs.Functions {
		// if it's an import stub ...
		if f.GetName() == "@import" {
			importPath := f.GetDescription()
			importExists, _ := fileutil.Exists(filepath.Join(pkgr.MixerDir, importPath))
			if !importExists {
				errURL := "http://help.moovweb.com/entries/22335641-importing-non-existent-files-in-functions-main-ts"
				msg := fmt.Sprintf("\n********\nin file %s:\nattempting to import nonexistent file %s\nPlease consult %s for more information about this error.\n********\n", relPath, importPath, errURL)
				panic(msg)
			}
			pkgr.resolveFunctions(filepath.Dir(importPath), filepath.Base(importPath))
			continue // to forgo appending it to the comprehensive list of functions

			// otherwise it's a proper function definition -- see if it's native
		} else if f.GetBuiltIn() {
			pkgr.resolveNativeDeclaration(f, relPath)

			// otherwise it's a user-defined function
		} else {
			pkgr.resolveUserDefinition(f, relPath)
		}

		pkgr.Package.Functions = append(pkgr.Package.Functions, f)
	}
}
Example #2
0
func (pkgr *Packager) buildLib() {
	// tritium libraries are optional -- compile them if they're there, otherwise do nothing
	libPath := filepath.Join(pkgr.MixerDir, pkgr.LibDir, ENTRY_FILE)
	there, _ := fileutil.Exists(libPath)
	if !there {
		return
	}

	pkgr.resolveFunctions(pkgr.LibDir, ENTRY_FILE)
}
Example #3
0
func (pkgr *Packager) readDependenciesFile() {
	depPath := filepath.Join(pkgr.MixerDir, DEPS_FILE)
	depPathExists, _ := fileutil.Exists(depPath)
	if !depPathExists {
		return
	}
	data, readErr := ioutil.ReadFile(filepath.Join(pkgr.MixerDir, DEPS_FILE))
	if readErr != nil {
		panic(fmt.Sprintf("error reading dependencies file for `%s`", pkgr.Mixer.GetName()))
	}
	pkgr.Dependencies = make(map[string]string)
	yaml.Unmarshal(data, &pkgr.Dependencies)
}
Example #4
0
func (pkgr *Packager) buildLib() {
	// tritium libraries are optional -- compile them if they're there, otherwise do nothing
	libPath := filepath.Join(pkgr.MixerDir, pkgr.LibDir, ENTRY_FILE)
	there, _ := fileutil.Exists(libPath)
	if !there {
		return
	}

	pkgr.resolveFunctions(pkgr.LibDir, ENTRY_FILE)

	// legacyPackage := &legacy.Package{}
	// legacyPackage.Package = pkgr.Package
	// for _, f := range pkgr.Package.Functions {
	// 	legacyPackage.ResolveFunctionDescendants(f)
	// }
}
Example #5
0
func New(relSrcDir, libDir string, mayBuildHttpTransformers bool, logger *golog.Logger, mixerDownloader downloader) *Packager {
	pkgr := new(Packager)

	wd, wdErr := os.Getwd()
	if wdErr != nil {
		panic("unable to determine current directory for mixer creation")
	}

	absSrcDir, absErr := filepath.Abs(relSrcDir)
	if absErr != nil {
		panic("unable to absolutize mixer source directory for mixer creation")
	}
	absSrcDir = filepath.Clean(absSrcDir)

	pkgr.MixerDir = absSrcDir
	pkgr.LibDir = libDir
	pkgr.IncludePaths = make([]string, 2) // support more in the future as a command-line option
	pkgr.IncludePaths[0] = wd
	pkgr.IncludePaths[1] = filepath.Dir(absSrcDir)

	pkgr.Mixer = tp.NewMixer(absSrcDir)
	pkgr.PackagerVersion = proto.Int32(PACKAGER_VERSION)
	pkgr.readDependenciesFile()
	pkgr.AlreadyLoaded = make(map[string]bool)
	pkgr.Logger = logger
	pkgr.downloader = mixerDownloader

	pkgr.Mixer.Package = new(tp.Package)
	pkgr.Mixer.Package.Functions = make([]*tp.Function, 0)

	pkgr.MayBuildHttpTransformers = mayBuildHttpTransformers
	tSigThere, _ := fileutil.Exists(filepath.Join(pkgr.MixerDir, HTTP_TRANSFORMERS_SIGNATURE))
	if tSigThere {
		pkgr.IsHttpTransformer = true
		if !pkgr.MayBuildHttpTransformers {
			panic("you are not authorized to build HTTP transformer mixers")
		}
	}

	return pkgr
}
Example #6
0
func (pkgr *Packager) resolveTypeDeclarations() {
	// see whether a type declarations file exists; if so, read it
	typeFilePath := filepath.Join(pkgr.MixerDir, pkgr.LibDir, TYPES_FILE)
	there, _ := fileutil.Exists(typeFilePath)
	if !there {
		return
	}
	data, readErr := ioutil.ReadFile(typeFilePath)
	if readErr != nil {
		panic(fmt.Sprintf("error reading type declarations file for `%s`", pkgr.Mixer.GetName()))
	}
	typeDecs := make([]string, 0)
	yaml.Unmarshal(data, &typeDecs)
	if pkgr.TypeMap == nil {
		pkgr.TypeMap = make(map[string]int)
	}
	if pkgr.SuperclassOf == nil {
		pkgr.SuperclassOf = make(map[string]string)
	}

	// now resolve the type declarations
	for _, typeDec := range typeDecs {
		// TODO: this logic is more complicated than it needs to be. Should really
		// just disallow any duplicate declarations. Anyway....

		// if the type doesn't extend anything ...
		if !strings.Contains(typeDec, "<") {
			_, isThere := pkgr.TypeMap[typeDec]
			if !isThere {
				pkgr.TypeMap[typeDec] = len(pkgr.TypeMap)
				pkgr.SuperclassOf[typeDec] = ""
				// if the type has already been declared, check for a semantic conflict
			} else if extendee := pkgr.SuperclassOf[typeDec]; extendee != "" {
				panic(fmt.Sprintf("type declaration `%s` conflicts with previous declaration `%s < %s`", typeDec, typeDec, extendee))
			}
			// if we get to this point, the declaration is a duplicate, so do nothing

			// else it's an extension
		} else {
			// parse the subtype and supertype
			splitted := strings.Split(typeDec, "<")
			if len(splitted) != 2 {
				panic(fmt.Sprintf("invalid syntax in type declaration `%s`; only one extension is permitted per declaration", typeDec))
			}
			sub, super := strings.TrimSpace(splitted[0]), strings.TrimSpace(splitted[1])
			// make sure that the supertype in a declaration actually exists
			_, there := pkgr.TypeMap[super]
			if !there {
				panic(fmt.Sprintf("cannot extend type `%s` before it has been declared", super))
			}
			// check for conflicts with previous declarations
			// (and incidentally ensure that subtypes always have a higher id than their supertypes
			extendee, subHasAlreadyExtended := pkgr.SuperclassOf[sub]
			if subHasAlreadyExtended /* && extendee != super */ {
				var previousDec string
				if extendee == "" {
					previousDec = fmt.Sprintf("`%s` (extends nothing)", sub)
				} else if extendee == super {
					previousDec = fmt.Sprintf("`%s < %s` (duplicates not allowed)", sub, extendee)
				} else {
					previousDec = fmt.Sprintf("`%s < %s`", sub, extendee)
				}
				panic(fmt.Sprintf("type declaration `%s` conflicts with previous declaration %s", typeDec, previousDec))
			}
			// if we get this far, we can proceed with the type extension
			pkgr.TypeMap[sub] = len(pkgr.TypeMap)
			pkgr.SuperclassOf[sub] = super
		}
	}
}
Example #7
0
func (pkgr *Packager) loadDependency(name, specifiedVersion string) {
	foundMixerSrc := false
	foundCompiledMixer := false
	// loop through the include paths and build the first dependency that we
	// find, then merge that dependency into the current mixer
	for _, incPath := range pkgr.IncludePaths {
		depPath := filepath.Join(incPath, name)
		there, err := fileutil.Exists(depPath)
		if !there || err != nil {
			continue
		}
		foundMixerSrc = true
		needed := NewDependencyOf(depPath, "lib", pkgr)

		if needed.GetVersion() != specifiedVersion {
			continue
		}
		// circular dependency check
		if pkgr.NowVisiting == nil {
			pkgr.NowVisiting = make(map[string]bool)
			pkgr.NowVisiting[pkgr.GetName()] = true
		}
		if pkgr.NowVisiting[needed.GetName()] {
			panic(fmt.Sprintf("circular dependency on `%s`", needed.GetName()))
		}
		// pass the dependency stack downwards ...
		needed.NowVisiting = pkgr.NowVisiting
		// ... and push the current mixer onto it
		needed.NowVisiting[needed.GetName()] = true

		needed.Build()
		pkgr.mergeWith(needed)

		// pop the dependency stack
		needed.NowVisiting[needed.GetName()] = false
		// don't need to pass it back up because maps are shared, and extending
		// them doesn't invalidate references to them (unlike slices)
		pkgr.Logger.Infof("  - built dependency `%s` (%s) from source", name, specifiedVersion)
		return
	}

	// if a mixer src dir isn't found, try to grab a compiled version locally
	mxr, mxErr := mixer.GetMixer(name, specifiedVersion)
	if mxErr == nil {
		foundCompiledMixer = true
		needed := NewFromCompiledMixer(mxr)
		pkgr.MergeCompiled(needed)
		pkgr.Logger.Infof("  - loaded dependency `%s` (%s) from local compiled mixer", name, specifiedVersion)
		return
	}

	// otherwise, try to download a compiled version from apollo
	mxr, mxErr = pkgr.downloader(name, specifiedVersion)
	if mxErr == nil {
		foundCompiledMixer = true
		needed := NewFromCompiledMixer(mxr)
		pkgr.MergeCompiled(needed)
		pkgr.Logger.Infof("  - loaded dependency `%s` (%s) from downloaded mixer", name, specifiedVersion)
		return
	}

	if foundMixerSrc || foundCompiledMixer {
		panic(fmt.Sprintf("version %s needed for dependency `%s` of `%s`",
			specifiedVersion, name, pkgr.GetName()))
	}
	panic(fmt.Sprintf("unable to find dependency `%s` of `%s`", name, pkgr.GetName()))
}
Example #8
0
func (p *Parser) statement() (node *tp.Instruction) {
	switch p.peek().Lexeme {
	case IMPORT, OPTIONAL:
		optional := false
		if p.peek().Lexeme == OPTIONAL {
			optional = true
		}
		if p.inFunc {
			panic(fmt.Sprintf("|%s:%d -- imports not allowed inside function definitions", p.FileName, p.peek().LineNumber))
		}
		token := p.pop() // pop the "@import" or "@optional" token (includes importee)
		importPath := token.Value

		layered := false
		appliedLayers := p.AppliedLayers
		if strings.Index(importPath, "@") != -1 {
			if len(p.Layers) == 0 {
				if !optional {
					panic(fmt.Sprintf("%s:%d -- required layer not provided; please make sure you've specified all necessary layers in the start-up options", p.FileName, token.LineNumber))
				} else {
					// make a no-op if the import is optional and no layer has been provided
					node = tp.MakeText("", token.LineNumber)
					return
				}
			}
			if len(appliedLayers) > 0 {
				tmpSlice := make([]string, 2)
				tmpSlice[0] = appliedLayers
				tmpSlice[1] = p.Layers[0]
				appliedLayers = strings.Join(tmpSlice, "/")
			} else {
				appliedLayers = p.Layers[0]
			}
			importPath = strings.Replace(importPath, "@", p.Layers[0], -1)
			layered = true
		}

		scriptLocationInProject := filepath.Clean(filepath.Join(p.ScriptPath, importPath))

		// extract the root script folder from the relative path of the importee
		// (would be easier if filepath.FromSlash worked as advertised)
		dir, base := filepath.Split(p.ScriptPath)
		if len(dir) == 0 {
			dir = base
			base = ""
		}
		if dir[len(dir)-1] == os.PathSeparator {
			dir = dir[0 : len(dir)-1]
		}
		for len(base) > 0 {
			dir, base = filepath.Split(dir)
			if len(dir) == 0 {
				dir = base
				base = ""
			}
			if dir[len(dir)-1] == os.PathSeparator {
				dir = dir[0 : len(dir)-1]
			}
		}
		// make sure that the importee is under the right subfolder
		if !strings.HasPrefix(scriptLocationInProject, dir) {
			msg := fmt.Sprintf("%s:%d -- imported file must exist under the `%s` folder", p.FileName, token.LineNumber, dir)
			panic(msg)
		}

		// now make sure the file exists and give a helpful message if it's a layer file
		exists, exErr := fileutil.Exists(filepath.Join(p.ProjectPath, scriptLocationInProject))
		if !exists || exErr != nil {
			if optional {
				// make a no-op if the import is optional
				node = tp.MakeText("", token.LineNumber)
				return
			} else if layered {
				panic(fmt.Sprintf("%s:%d -- required layer (%s) has no corresponding Tritium file (want %s)", p.FileName, p.LineNumber, appliedLayers, scriptLocationInProject))
			} else {
				panic(fmt.Sprintf("%s:%d -- file to import not found (%s)", p.FileName, p.LineNumber, scriptLocationInProject))
			}
		}

		node = tp.MakeImport(scriptLocationInProject, token.LineNumber)
		if layered {
			node.Namespace = proto.String(appliedLayers) // re-use this slot to specify which layer the import is targeting
		}

	case STRING, REGEXP, POS, READ, ID, TYPE, GVAR, LVAR, LPAREN:
		node = p.expression()
	case NAMESPACE:
		p.error("`@namespace` directive must occur at the top of a file or code block")
	default:
		p.error("statement must consist of import or expression")
	}
	return node
}