func (l *Lexer) VariableSimple() { // When we enter this state we know we have at least one readable // char for the varname. That means that any character not valid // just terminates the parsing and we dont have to // worry about the case of an empty varname l.buffer.WriteRune(SentinalSubstitution) sv := SubVariable{SubType: VarSubNormal} varbuf := bytes.Buffer{} c := l.nextChar() switch { case char.IsSpecial(c): varbuf.WriteRune(c) case char.IsDigit(c): // Positional argv for { varbuf.WriteRune(c) c = l.nextChar() if !char.IsDigit(c) { l.backup() break } } case char.IsFirstInVarName(c): for { varbuf.WriteRune(c) c = l.nextChar() if !char.IsInVarName(c) { l.backup() break } } default: l.backup() } sv.VarName = varbuf.String() l.subs = append(l.subs, sv) return }
func (l *Lexer) Substitution() { // Upon entering we have only read the '$' // Perform the lex of a single complete substitution before returning // control to the calling location c := l.nextChar() switch { default: l.buffer.WriteRune('$') l.backup() case c == '(': if l.hasNext('(') { l.Arith() } else { l.Subshell() } case char.IsFirstInVarName(c), char.IsDigit(c), char.IsSpecial(c): l.backup() l.VariableSimple() case c == '{': l.VariableComplex() } }
func (l *Lexer) VariableComplex() { // Upon entering we have read the opening '{' l.buffer.WriteRune(SentinalSubstitution) sv := SubVariable{} varbuf := bytes.Buffer{} defer func() { // We defer this as there are multiple return points sv.VarName = varbuf.String() l.subs = append(l.subs, sv) }() if l.hasNext('#') { // The NParam Special Var if l.hasNext('}') { varbuf.WriteRune('#') return } // Length variable operator sv.SubType = VarSubLength } c := l.nextChar() switch { case char.IsSpecial(c): varbuf.WriteRune(c) case char.IsDigit(c): for { varbuf.WriteRune(c) c = l.nextChar() if !char.IsDigit(c) { l.backup() break } } case char.IsFirstInVarName(c): for { varbuf.WriteRune(c) c = l.nextChar() if !char.IsInVarName(c) { l.backup() break } } case c == EOFRune: l.backup() return } // Either a Enclosed variable '${foo}' or a length operation '${#foo}' if l.hasNext('}') { return } // Length operator should have returned since only ${#varname} is valid if sv.SubType == VarSubLength { l.log.Error("Line %d: Bad substitution (%s)", l.lineNo, l.input[l.lastPosition:l.position]) os.Exit(1) } if l.hasNext(':') { sv.CheckNull = true } switch l.nextChar() { case '-': sv.SubType = VarSubMinus case '+': sv.SubType = VarSubPlus case '?': sv.SubType = VarSubQuestion case '=': sv.SubType = VarSubAssign case '#': if l.hasNext('#') { sv.SubType = VarSubTrimLeftMax } else { sv.SubType = VarSubTrimLeft } case '%': if l.hasNext('%') { sv.SubType = VarSubTrimRightMax } else { sv.SubType = VarSubTrimRight } default: l.log.Error("Line %d: Bad substitution (%s)", l.lineNo, l.input[l.lastPosition:l.position]) os.Exit(1) } // Read until '}' // In the future to support Nested vars etc create new sublexer from // l.input[l.pos:] and take the first lexitem as the sub val then adjust // this lexer's position and trash sublexer c = l.nextChar() subValBuf := bytes.Buffer{} for { if c == '}' { break } subValBuf.WriteRune(c) c = l.nextChar() } sv.SubVal = subValBuf.String() }