~ubuntu-branches/ubuntu/wily/julia/wily

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
## Julia Webrepl ##
const DEBUG = false
import Base.show

###########################################
# protocol
###########################################

###### the julia<-->server protocol #######

# the message type is sent as a byte
# the next byte indicates how many arguments there are
# each argument is four bytes indicating the size of the argument, then the data for that argument

###### the server<-->browser protocol #####

# messages are sent as arrays of arrays (json)
# the outer array is an "array of messages"
# each message is itself an array:
# [message_type::number, arg0::string, arg1::string, ...]

# import the message types
include("webrepl_msgtypes_h.jl")

###########################################
# set up the socket connection
###########################################

# open a socket on any port
(port,sock) = Base.open_any_tcp_port(4444)

# print the socket number so the server knows what it is
println(STDOUT,int16(port))

# wait for the server to connect to the socket
__io = Base.wait_accept(sock)
Base.start_reading(__io)

###########################################
# protocol implementation
###########################################

# a message
type __Message
    msg_type::Uint8
    args::Array{Any, 1}
end
show(m::__Message) = __print_message(m)

# read a message
function __read_message()
    msg_type = read(__io, Uint8)
    args = {}
    num_args = read(__io, Uint8)
    for i=1:num_args
        arg_length = read(__io, Uint32)
        arg = ASCIIString(read(__io, Uint8, arg_length))
        push!(args, arg)
    end
    return __Message(msg_type, args)
end

# send a message
const __io_out_buf = PipeString()
function __write_message(msg)
    if DEBUG show(msg); println() end
    write(__io, uint8(msg.msg_type))
    write(__io, uint8(length(msg.args)))
    for arg=msg.args
        write(__io_out_buf, arg)
        data = takebuf_array(__io_out_buf)
        write(__io, uint32(length(data)))
        write(__io, data)
    end
    #flush(__io)
end

# print a message (useful for debugging)
function __print_message(msg)
    print(msg.msg_type)
    print(": [ ")
    for arg=msg.args
        print("\"")
        print(arg)
        print("\" ")
    end
    println("]")
end

###########################################
# standard web library
###########################################

# load the special functions available to the web repl
include("julia_web.jl")

###########################################
# input event handler
###########################################

# store the result of the previous input
ans = nothing

# callback for that event handler
function __socket_callback(fd)
    # read the message
    __msg = __read_message()
    if DEBUG show(__msg); println() end
    
    # MSG_INPUT_EVAL
    if __msg.msg_type == __MSG_INPUT_EVAL && length(__msg.args) == 3
        # parse the arguments
        __user_name = __msg.args[1]
        __user_id = __msg.args[2]
        __input = __msg.args[3]

        # split the input into lines
        __lines = split(__input, '\n')

        # try to parse each line incrementally
        __parsed_exprs = {}
        __input_so_far = ""
        __all_nothing = true

        for i=1:length(__lines)
            # add the next line of input
            __input_so_far = string(__input_so_far, __lines[i], "\n")

            # try to parse it
            __expr = parse_input_line(__input_so_far)
            
            # if there was nothing to parse, just keep going
            if __expr == nothing
                continue
            end
            __all_nothing = false
            __expr_multitoken = isa(__expr, Expr)

            # stop now if there was a parsing error
            if __expr_multitoken && __expr.head == :error
                # send everyone the input
                __write_message(__Message(__MSG_OUTPUT_EVAL_INPUT, {__user_id, __user_name, __input}))
                return __write_message(__Message(__MSG_OUTPUT_EVAL_ERROR, {__user_id, __expr.args[1]}))
            end
            
            # if the expression was incomplete, just keep going
            if __expr_multitoken && __expr.head == :continue
                continue
            end

            # add the parsed expression to the list
            __input_so_far = ""
            __parsed_exprs = [__parsed_exprs, {(__user_id, __expr)}]
        end

        # if the input was empty, stop early
        if __all_nothing
            # send everyone the input
            __write_message(__Message(__MSG_OUTPUT_EVAL_INPUT, {__user_id, __user_name, __input}))
            return __write_message(__Message(__MSG_OUTPUT_EVAL_RESULT, {__user_id, ""}))
        end

        # tell the browser if we didn't get a complete expression
        if length(__parsed_exprs) == 0
            return __write_message(__Message(__MSG_OUTPUT_EVAL_INCOMPLETE, {__user_id}))
        end

        # send everyone the input
        __write_message(__Message(__MSG_OUTPUT_EVAL_INPUT, {__user_id, __user_name, __input}))

        put(__eval_channel, __parsed_exprs)
    end
end

# event handler for socket input
enq_work(@task while true __socket_callback(__io) end)

web_show(user_id, ans) =
    __Message(__MSG_OUTPUT_EVAL_RESULT, {user_id, sprint(repl_show, ans)})

function __eval_exprs(__parsed_exprs)
    global ans
    user_id = ""

    # try to evaluate the expressions
    for i=1:length(__parsed_exprs)
        # evaluate the expression and stop if any exceptions happen
        user_id = __parsed_exprs[i][1]
        try
            ans = eval(__parsed_exprs[i][2])
        catch __error
            return __write_message(__Message(__MSG_OUTPUT_EVAL_ERROR, {user_id, sprint(show, __error)}))
        end
    end
    
    # send the result of the last expression
    if isa(ans,Nothing)
        return __write_message(__Message(__MSG_OUTPUT_EVAL_RESULT, {user_id, ""}))
    else
        return __write_message(web_show(user_id, ans))
    end
end

# print version info
println("Julia ", Base.version_string)
println(Base.commit_string, "\n")

# work around bug displaying "\n "
#print("  ",replace(Base.banner_plain, "\n", "\n  "))

###########################################
# wait forever while asynchronous processing happens
###########################################

__eval_channel = RemoteRef()

while true
    __eval_exprs(take(__eval_channel))
end