summaryrefslogtreecommitdiff
path: root/userland/minibox/utilities/sh/ast.c
blob: 2b9c1517f99a911346f3813828eed2e367f4a8cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include "ast.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void free_ast_command(struct AST *ast) {
  free_ast(ast->children);
  free_ast(ast->pipe_rhs);
}

void free_ast(struct AST *ast) {
  for (; ast;) {
    if (AST_COMMAND == ast->type)
      free_ast_command(ast);
    struct AST *old = ast;
    ast = ast->next;
    free(old);
  }
}

struct AST *allocate_ast(void) {
  struct AST *r = malloc(sizeof(struct AST));
  memset(r, 0, sizeof(struct AST));
  return r;
}

int parse_command(struct TOKEN **token_ptr, struct AST *cur) {
  struct TOKEN *token = *token_ptr;
  if (TOKEN_CHARS != token->type)
    return 0;
  cur->type = AST_COMMAND;
  cur->val.type = AST_VALUE_STRING;
  cur->val.string = token->string_rep;
  // Parse the arguments
  if (token->next && TOKEN_CHARS == token->next->type) {
    token = token->next;
    cur->children = allocate_ast();
    struct AST *child = cur->children;
    for (;;) {
      child->type = AST_EXPRESSION;
      child->val.type = AST_VALUE_STRING;
      child->val.string = token->string_rep;
      if (!token->next)
        break;
      if (TOKEN_CHARS != token->next->type)
        break;
      token = token->next;
      child->next = allocate_ast();
      child = child->next;
    }
  }
  token = token->next;
  // Parse the stream modifier "prog > file.txt"
  if (token &&
      (TOKEN_STREAM == token->type || TOKEN_STREAM_APPEND == token->type)) {
    cur->file_out_append = (TOKEN_STREAM_APPEND == token->type);
    // TODO: Allow it to be modified
    cur->file_out_fd_to_use = STDOUT_FILENO;
    token = token->next;
    cur->file_out = token->string_rep;
    token = token->next;
  }
  // Parse '&'
  if (token && TOKEN_BACKGROUND == token->type) {
    cur->should_background = 1;
    token = token->next;
  }
  // Parse pipe '|'
  if (token && TOKEN_PIPE == token->type) {
    cur->pipe_rhs = allocate_ast();
    token = token->next;
    if (!parse_command(&token, cur->pipe_rhs)) {
      fprintf(stderr, "Expected command after |.");
      exit(1);
    }
  }
  if (token && TOKEN_NEWLINE == token->type) {
    token = token->next;
  }
  *token_ptr = token;
  return 1;
}

struct AST *generate_ast(struct TOKEN *token) {
  struct AST *head = NULL;
  struct AST *prev = NULL;
  for (; token;) {
    struct AST *cur = allocate_ast();
    if (prev)
      prev->next = cur;
    if (parse_command(&token, cur)) {
    } else if (TOKEN_AND == token->type) {
      cur->type = AST_CONDITIONAL_AND;
      token = token->next;
    } else if (TOKEN_NOT == token->type) {
      cur->type = AST_CONDITIONAL_NOT;
      token = token->next;
    } else {
      token = token->next;
    }
    if (!head)
      head = cur;
    prev = cur;
  }
  return head;
}