ini_parser.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. package ini
  2. import (
  3. "fmt"
  4. "io"
  5. )
  6. // State enums for the parse table
  7. const (
  8. InvalidState = iota
  9. // stmt -> value stmt'
  10. StatementState
  11. // stmt' -> MarkComplete | op stmt
  12. StatementPrimeState
  13. // value -> number | string | boolean | quoted_string
  14. ValueState
  15. // section -> [ section'
  16. OpenScopeState
  17. // section' -> value section_close
  18. SectionState
  19. // section_close -> ]
  20. CloseScopeState
  21. // SkipState will skip (NL WS)+
  22. SkipState
  23. // SkipTokenState will skip any token and push the previous
  24. // state onto the stack.
  25. SkipTokenState
  26. // comment -> # comment' | ; comment'
  27. // comment' -> MarkComplete | value
  28. CommentState
  29. // MarkComplete state will complete statements and move that
  30. // to the completed AST list
  31. MarkCompleteState
  32. // TerminalState signifies that the tokens have been fully parsed
  33. TerminalState
  34. )
  35. // parseTable is a state machine to dictate the grammar above.
  36. var parseTable = map[ASTKind]map[TokenType]int{
  37. ASTKindStart: map[TokenType]int{
  38. TokenLit: StatementState,
  39. TokenSep: OpenScopeState,
  40. TokenWS: SkipTokenState,
  41. TokenNL: SkipTokenState,
  42. TokenComment: CommentState,
  43. TokenNone: TerminalState,
  44. },
  45. ASTKindCommentStatement: map[TokenType]int{
  46. TokenLit: StatementState,
  47. TokenSep: OpenScopeState,
  48. TokenWS: SkipTokenState,
  49. TokenNL: SkipTokenState,
  50. TokenComment: CommentState,
  51. TokenNone: MarkCompleteState,
  52. },
  53. ASTKindExpr: map[TokenType]int{
  54. TokenOp: StatementPrimeState,
  55. TokenLit: ValueState,
  56. TokenSep: OpenScopeState,
  57. TokenWS: ValueState,
  58. TokenNL: SkipState,
  59. TokenComment: CommentState,
  60. TokenNone: MarkCompleteState,
  61. },
  62. ASTKindEqualExpr: map[TokenType]int{
  63. TokenLit: ValueState,
  64. TokenWS: SkipTokenState,
  65. TokenNL: SkipState,
  66. },
  67. ASTKindStatement: map[TokenType]int{
  68. TokenLit: SectionState,
  69. TokenSep: CloseScopeState,
  70. TokenWS: SkipTokenState,
  71. TokenNL: SkipTokenState,
  72. TokenComment: CommentState,
  73. TokenNone: MarkCompleteState,
  74. },
  75. ASTKindExprStatement: map[TokenType]int{
  76. TokenLit: ValueState,
  77. TokenSep: OpenScopeState,
  78. TokenOp: ValueState,
  79. TokenWS: ValueState,
  80. TokenNL: MarkCompleteState,
  81. TokenComment: CommentState,
  82. TokenNone: TerminalState,
  83. TokenComma: SkipState,
  84. },
  85. ASTKindSectionStatement: map[TokenType]int{
  86. TokenLit: SectionState,
  87. TokenOp: SectionState,
  88. TokenSep: CloseScopeState,
  89. TokenWS: SectionState,
  90. TokenNL: SkipTokenState,
  91. },
  92. ASTKindCompletedSectionStatement: map[TokenType]int{
  93. TokenWS: SkipTokenState,
  94. TokenNL: SkipTokenState,
  95. TokenLit: StatementState,
  96. TokenSep: OpenScopeState,
  97. TokenComment: CommentState,
  98. TokenNone: MarkCompleteState,
  99. },
  100. ASTKindSkipStatement: map[TokenType]int{
  101. TokenLit: StatementState,
  102. TokenSep: OpenScopeState,
  103. TokenWS: SkipTokenState,
  104. TokenNL: SkipTokenState,
  105. TokenComment: CommentState,
  106. TokenNone: TerminalState,
  107. },
  108. }
  109. // ParseAST will parse input from an io.Reader using
  110. // an LL(1) parser.
  111. func ParseAST(r io.Reader) ([]AST, error) {
  112. lexer := iniLexer{}
  113. tokens, err := lexer.Tokenize(r)
  114. if err != nil {
  115. return []AST{}, err
  116. }
  117. return parse(tokens)
  118. }
  119. // ParseASTBytes will parse input from a byte slice using
  120. // an LL(1) parser.
  121. func ParseASTBytes(b []byte) ([]AST, error) {
  122. lexer := iniLexer{}
  123. tokens, err := lexer.tokenize(b)
  124. if err != nil {
  125. return []AST{}, err
  126. }
  127. return parse(tokens)
  128. }
  129. func parse(tokens []Token) ([]AST, error) {
  130. start := Start
  131. stack := newParseStack(3, len(tokens))
  132. stack.Push(start)
  133. s := newSkipper()
  134. loop:
  135. for stack.Len() > 0 {
  136. k := stack.Pop()
  137. var tok Token
  138. if len(tokens) == 0 {
  139. // this occurs when all the tokens have been processed
  140. // but reduction of what's left on the stack needs to
  141. // occur.
  142. tok = emptyToken
  143. } else {
  144. tok = tokens[0]
  145. }
  146. step := parseTable[k.Kind][tok.Type()]
  147. if s.ShouldSkip(tok) {
  148. // being in a skip state with no tokens will break out of
  149. // the parse loop since there is nothing left to process.
  150. if len(tokens) == 0 {
  151. break loop
  152. }
  153. step = SkipTokenState
  154. }
  155. switch step {
  156. case TerminalState:
  157. // Finished parsing. Push what should be the last
  158. // statement to the stack. If there is anything left
  159. // on the stack, an error in parsing has occurred.
  160. if k.Kind != ASTKindStart {
  161. stack.MarkComplete(k)
  162. }
  163. break loop
  164. case SkipTokenState:
  165. // When skipping a token, the previous state was popped off the stack.
  166. // To maintain the correct state, the previous state will be pushed
  167. // onto the stack.
  168. stack.Push(k)
  169. case StatementState:
  170. if k.Kind != ASTKindStart {
  171. stack.MarkComplete(k)
  172. }
  173. expr := newExpression(tok)
  174. stack.Push(expr)
  175. case StatementPrimeState:
  176. if tok.Type() != TokenOp {
  177. stack.MarkComplete(k)
  178. continue
  179. }
  180. if k.Kind != ASTKindExpr {
  181. return nil, NewParseError(
  182. fmt.Sprintf("invalid expression: expected Expr type, but found %T type", k),
  183. )
  184. }
  185. k = trimSpaces(k)
  186. expr := newEqualExpr(k, tok)
  187. stack.Push(expr)
  188. case ValueState:
  189. // ValueState requires the previous state to either be an equal expression
  190. // or an expression statement.
  191. //
  192. // This grammar occurs when the RHS is a number, word, or quoted string.
  193. // equal_expr -> lit op equal_expr'
  194. // equal_expr' -> number | string | quoted_string
  195. // quoted_string -> " quoted_string'
  196. // quoted_string' -> string quoted_string_end
  197. // quoted_string_end -> "
  198. //
  199. // otherwise
  200. // expr_stmt -> equal_expr (expr_stmt')*
  201. // expr_stmt' -> ws S | op S | MarkComplete
  202. // S -> equal_expr' expr_stmt'
  203. switch k.Kind {
  204. case ASTKindEqualExpr:
  205. // assiging a value to some key
  206. k.AppendChild(newExpression(tok))
  207. stack.Push(newExprStatement(k))
  208. case ASTKindExpr:
  209. k.Root.raw = append(k.Root.raw, tok.Raw()...)
  210. stack.Push(k)
  211. case ASTKindExprStatement:
  212. root := k.GetRoot()
  213. children := root.GetChildren()
  214. if len(children) == 0 {
  215. return nil, NewParseError(
  216. fmt.Sprintf("invalid expression: AST contains no children %s", k.Kind),
  217. )
  218. }
  219. rhs := children[len(children)-1]
  220. if rhs.Root.ValueType != QuotedStringType {
  221. rhs.Root.ValueType = StringType
  222. rhs.Root.raw = append(rhs.Root.raw, tok.Raw()...)
  223. }
  224. children[len(children)-1] = rhs
  225. k.SetChildren(children)
  226. stack.Push(k)
  227. }
  228. case OpenScopeState:
  229. if !runeCompare(tok.Raw(), openBrace) {
  230. return nil, NewParseError("expected '['")
  231. }
  232. stmt := newStatement()
  233. stack.Push(stmt)
  234. case CloseScopeState:
  235. if !runeCompare(tok.Raw(), closeBrace) {
  236. return nil, NewParseError("expected ']'")
  237. }
  238. k = trimSpaces(k)
  239. stack.Push(newCompletedSectionStatement(k))
  240. case SectionState:
  241. var stmt AST
  242. switch k.Kind {
  243. case ASTKindStatement:
  244. // If there are multiple literals inside of a scope declaration,
  245. // then the current token's raw value will be appended to the Name.
  246. //
  247. // This handles cases like [ profile default ]
  248. //
  249. // k will represent a SectionStatement with the children representing
  250. // the label of the section
  251. stmt = newSectionStatement(tok)
  252. case ASTKindSectionStatement:
  253. k.Root.raw = append(k.Root.raw, tok.Raw()...)
  254. stmt = k
  255. default:
  256. return nil, NewParseError(
  257. fmt.Sprintf("invalid statement: expected statement: %v", k.Kind),
  258. )
  259. }
  260. stack.Push(stmt)
  261. case MarkCompleteState:
  262. if k.Kind != ASTKindStart {
  263. stack.MarkComplete(k)
  264. }
  265. if stack.Len() == 0 {
  266. stack.Push(start)
  267. }
  268. case SkipState:
  269. stack.Push(newSkipStatement(k))
  270. s.Skip()
  271. case CommentState:
  272. if k.Kind == ASTKindStart {
  273. stack.Push(k)
  274. } else {
  275. stack.MarkComplete(k)
  276. }
  277. stmt := newCommentStatement(tok)
  278. stack.Push(stmt)
  279. default:
  280. return nil, NewParseError(fmt.Sprintf("invalid state with ASTKind %v and TokenType %v", k, tok))
  281. }
  282. if len(tokens) > 0 {
  283. tokens = tokens[1:]
  284. }
  285. }
  286. // this occurs when a statement has not been completed
  287. if stack.top > 1 {
  288. return nil, NewParseError(fmt.Sprintf("incomplete expression: %v", stack.container))
  289. }
  290. // returns a sublist which excludes the start symbol
  291. return stack.List(), nil
  292. }
  293. // trimSpaces will trim spaces on the left and right hand side of
  294. // the literal.
  295. func trimSpaces(k AST) AST {
  296. // trim left hand side of spaces
  297. for i := 0; i < len(k.Root.raw); i++ {
  298. if !isWhitespace(k.Root.raw[i]) {
  299. break
  300. }
  301. k.Root.raw = k.Root.raw[1:]
  302. i--
  303. }
  304. // trim right hand side of spaces
  305. for i := len(k.Root.raw) - 1; i >= 0; i-- {
  306. if !isWhitespace(k.Root.raw[i]) {
  307. break
  308. }
  309. k.Root.raw = k.Root.raw[:len(k.Root.raw)-1]
  310. }
  311. return k
  312. }