blob: 60efac2758a5c236079aea84abe8c508f5da102d [file] [log] [blame]
Colin Cross3f40fa42015-01-30 17:27:36 -08001package parser
2
Jeff Gastonf3ccfa92017-08-23 17:03:42 -07003import (
4 "strings"
5)
Colin Cross3f40fa42015-01-30 17:27:36 -08006
7type Scope interface {
8 Get(name string) string
9 Set(name, value string)
10 Call(name string, args []string) string
11 SetFunc(name string, f func([]string) string)
12}
13
14type scope struct {
15 variables map[string]string
16 functions map[string]func([]string) string
17 parent Scope
18}
19
20func (s *scope) Get(name string) string {
21 if val, ok := s.variables[name]; ok {
22 return val
23 } else if s.parent != nil {
24 return s.parent.Get(name)
25 } else if val, ok := builtinScope[name]; ok {
26 return val
27 } else {
28 return "<'" + name + "' unset>"
29 }
30}
31
32func (s *scope) Set(name, value string) {
33 s.variables[name] = value
34}
35
36func (s *scope) Call(name string, args []string) string {
37 if f, ok := s.functions[name]; ok {
38 return f(args)
39 }
40
41 return "<func:'" + name + "' unset>"
42}
43
44func (s *scope) SetFunc(name string, f func([]string) string) {
45 s.functions[name] = f
46}
47
48func NewScope(parent Scope) Scope {
49 return &scope{
50 variables: make(map[string]string),
51 functions: make(map[string]func([]string) string),
52 parent: parent,
53 }
54}
55
56var builtinScope map[string]string
57
58func init() {
59 builtinScope := make(map[string]string)
60 builtinScope["__builtin_dollar"] = "$"
61}
62
Colin Cross16daa922015-04-03 16:51:45 -070063func (v Variable) EvalFunction(scope Scope) (string, bool) {
Colin Cross3f40fa42015-01-30 17:27:36 -080064 f := v.Name.SplitN(" \t", 2)
65 if len(f) > 1 && f[0].Const() {
66 fname := f[0].Value(nil)
67 if isFunctionName(fname) {
68 args := f[1].Split(",")
69 argVals := make([]string, len(args))
70 for i, a := range args {
71 argVals[i] = a.Value(scope)
72 }
73
74 if fname == "call" {
Colin Cross16daa922015-04-03 16:51:45 -070075 return scope.Call(argVals[0], argVals[1:]), true
Colin Cross3f40fa42015-01-30 17:27:36 -080076 } else {
Colin Cross16daa922015-04-03 16:51:45 -070077 return "__builtin_func:" + fname + " " + strings.Join(argVals, " "), true
Colin Cross3f40fa42015-01-30 17:27:36 -080078 }
79 }
80 }
81
Colin Cross16daa922015-04-03 16:51:45 -070082 return "", false
83}
84
85func (v Variable) Value(scope Scope) string {
86 if ret, ok := v.EvalFunction(scope); ok {
87 return ret
88 }
Jeff Gastonf3ccfa92017-08-23 17:03:42 -070089 if scope == nil {
90 panic("Cannot take the value of a variable in a nil scope")
91 }
Colin Cross3f40fa42015-01-30 17:27:36 -080092 return scope.Get(v.Name.Value(scope))
93}
94
95func toVariable(ms *MakeString) (Variable, bool) {
96 if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
97 return ms.Variables[0], true
98 }
99 return Variable{}, false
100}