├── LICENSE ├── README.md ├── build.cfg ├── buildjs.sh ├── compiler ├── build.sh ├── core.lua ├── emitter.lua ├── lexer.lua ├── main.lua ├── parser.lua ├── statestack.lua └── transpiler.lua ├── index.html ├── manual.css ├── manual.html ├── playground ├── build.sh ├── console.css ├── console.html ├── console.lua ├── demo.lua ├── document.lua ├── editor.css ├── editor.html ├── editor.lua ├── finder.css ├── finder.html ├── finder.lua ├── handlebars.lua ├── index.css ├── index.html ├── mysql_compat.php ├── raytracer │ ├── 00_main.lua │ ├── build.sh │ ├── compile.sh │ ├── driver.lua │ ├── group.lua │ ├── index.html │ ├── light.lua │ ├── sphere.lua │ ├── tracer.lua │ ├── vector.lua │ └── worker.js ├── samples │ ├── hello_html.lua │ ├── hello_html_doc.html │ ├── hello_io.lua │ ├── hello_world.lua │ ├── plot2d.lua │ ├── print_self.lua │ └── simple_canvas.lua ├── schema.sql ├── serverdb.php └── storage.lua └── tests ├── build.sh ├── compile.sh ├── runtest.sh ├── test-bits.lua ├── test-compiler.lua ├── test-coroutine.lua ├── test-datetime.lua ├── test-fastcall.lua ├── test-goto.lua ├── test-load.lua ├── test-manual.lua ├── test-math.lua ├── test-misc.lua ├── test-next-pairs.lua ├── test-pcall.lua ├── test-string-basic.lua ├── test-string-format.lua ├── test-string-regex.lua ├── test-table-lib.lua └── test-vector.lua /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 spaceflint7 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Loulabelle version 5203, released May 2018. 3 | 4 | Compiler/transpiler from Lua 5.2 to JavaScript. 5 | Written in Lua 5.2 and can be compiled to JavaScript. 6 | 7 | Project home page: 8 | https://www.spaceflint.com/loulabelle/ 9 | https://www.spaceflint.com/?p=161 10 | 11 | Online playground: 12 | https://www.spaceflint.com/loulabelle/playground/ 13 | 14 | Source code download: 15 | https://www.spaceflint.com/loulabelle/loulabelle-5203.zip 16 | 17 | GitHub page: 18 | https://github.com/spaceflint7/loulabelle 19 | 20 | For documentation see the file manual.html, 21 | or visit the project home page. 22 | -------------------------------------------------------------------------------- /build.cfg: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # TARGET - select target directory, 4 | # for the following directory hierarchy: 5 | # 6 | # $TARGET - compiler and core library, documentation 7 | # $TARGET/playground - playground demo 8 | # 9 | 10 | TARGET=~/scoop/persist/apache/htdocs/loulabelle 11 | 12 | # 13 | # JSOBJ - select name for the JavaScript Lua object. 14 | # compiler generates $lua by default, but this must 15 | # be specified explicitly here, for compiler/build.sh 16 | # 17 | 18 | JSOBJ="\$lua" 19 | 20 | # 21 | # LFLAGS - compilation flags for Loulabelle compiler. 22 | # 23 | 24 | LFLAGS="-L $JSOBJ" 25 | # LFLAGS="-L $JSOBJ -o" 26 | -------------------------------------------------------------------------------- /buildjs.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | TARGET=Loulabelle.js 4 | JSOBJ="\$lua" 5 | # LFLAGS="-o" 6 | 7 | pushd compiler > /dev/null 8 | TARGET=../$TARGET 9 | 10 | echo "$JSOBJ.preload_chunk('Loulabelle*statestack'," > $TARGET 11 | lua main.lua $LFLAGS statestack.lua | sed 1,1d >> $TARGET 12 | 13 | echo "$JSOBJ.preload_chunk('Loulabelle*lexer'," >> $TARGET 14 | lua main.lua $LFLAGS lexer.lua | sed 1,1d >> $TARGET 15 | 16 | echo "$JSOBJ.preload_chunk('Loulabelle*parser'," >> $TARGET 17 | lua main.lua $LFLAGS parser.lua | sed 1,1d >> $TARGET 18 | 19 | echo "$JSOBJ.preload_chunk('Loulabelle*emitter'," >> $TARGET 20 | lua main.lua $LFLAGS emitter.lua | sed 1,1d >> $TARGET 21 | 22 | echo "$JSOBJ.preload_chunk('Loulabelle'," >> $TARGET 23 | lua main.lua $LFLAGS transpiler.lua | sed 1,1d >> $TARGET 24 | -------------------------------------------------------------------------------- /compiler/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | source ../build.cfg 4 | if [ -n "$1" ]; then 5 | TARGET=$1 6 | else 7 | echo "Usage: ./build.sh directory" 8 | echo "Compiles Loulabelle compiler and core library" 9 | echo " into Loulabelle.js and core.js files" 10 | echo " in the specified directory" 11 | echo "Build flags are taken from ../build.cfg" 12 | exit 1 13 | fi 14 | 15 | # 16 | # if need to compile any one of the modules making up 17 | # the compiler, compile all of them, and merge into 18 | # the single file $TARGET/Loulabelle.js 19 | # 20 | # this file is designed to be preloaded via a SCRIPT 21 | # tag, and it is then available to Lua using 22 | # require "Loulabelle". 23 | # see Loulabelle manual, sections 4 and 6.3, and also 24 | # playground/console.html and playground/console.lua 25 | # 26 | 27 | for f in {statestack,lexer,parser,emitter,transpiler}.lua; do 28 | 29 | if [ $f -nt $TARGET/Loulabelle.js ]; then 30 | 31 | echo Compiling compiler 32 | 33 | echo "$JSOBJ.preload_chunk('Loulabelle*statestack'," > $TARGET/Loulabelle.js 34 | lua main.lua $LFLAGS statestack.lua | sed 1,1d >> $TARGET/Loulabelle.js 35 | 36 | echo "$JSOBJ.preload_chunk('Loulabelle*lexer'," >> $TARGET/Loulabelle.js 37 | lua main.lua $LFLAGS lexer.lua | sed 1,1d >> $TARGET/Loulabelle.js 38 | 39 | echo "$JSOBJ.preload_chunk('Loulabelle*parser'," >> $TARGET/Loulabelle.js 40 | lua main.lua $LFLAGS parser.lua | sed 1,1d >> $TARGET/Loulabelle.js 41 | 42 | echo "$JSOBJ.preload_chunk('Loulabelle*emitter'," >> $TARGET/Loulabelle.js 43 | lua main.lua $LFLAGS emitter.lua | sed 1,1d >> $TARGET/Loulabelle.js 44 | 45 | echo "$JSOBJ.preload_chunk('Loulabelle'," >> $TARGET/Loulabelle.js 46 | lua main.lua $LFLAGS transpiler.lua | sed 1,1d >> $TARGET/Loulabelle.js 47 | 48 | break 49 | fi 50 | done 51 | 52 | # 53 | # compile the core library 54 | # 55 | 56 | if [ core.lua -nt $TARGET/core.js ]; then 57 | 58 | echo Compiling core library 59 | 60 | lua main.lua $LFLAGS core.lua > $TARGET/core.js 61 | fi 62 | -------------------------------------------------------------------------------- /compiler/lexer.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | local class = {} 4 | 5 | 6 | function class.new(text) 7 | 8 | local self = { 9 | 10 | text = text, 11 | text_pos = 0, 12 | text_len = #text, 13 | 14 | line = 1, 15 | column = 1, 16 | token_line = nil, 17 | token_column = nil, 18 | } 19 | 20 | for k,_ in pairs(class) do self[k] = class[k] end 21 | 22 | return self 23 | 24 | end 25 | 26 | 27 | local function isalpha_(c) return c and (c == '_' or (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z')) end 28 | local function isspace(c) return c and (c == ' ' or c == '\t' or c == '\n' or c == '\r') end 29 | local function isdigit(c) return c and (c >= '0' and c <= '9') end 30 | local function isxdigit(c) return c and ((c >= '0' and c <= '9') or (c >= 'a' and c <= 'f') or (c >= 'A' and c <= 'F')) end 31 | 32 | 33 | function class:next_token() 34 | 35 | while true do 36 | 37 | local c 38 | while true do 39 | c = self:get_char() 40 | if not isspace(c) then 41 | if c then break 42 | else -- end of file 43 | self.token_line, self.token_column = self.line, self.column 44 | return c 45 | end 46 | end 47 | end 48 | 49 | self.token_line, self.token_column = self.line, self.column 50 | 51 | if c == '-' and self:peek_chars(1) == '-' then 52 | 53 | self:get_char() 54 | c = self:get_char() 55 | local lvl = (c == '[') and self:check_long_bracket() 56 | if lvl then 57 | local _, err = self:next_long_string(lvl, 'comment') 58 | if err then return nil, err end 59 | else 60 | while c and c ~= '\n' do c = self:get_char() end 61 | end 62 | 63 | elseif not isalpha_(c) then 64 | 65 | if isdigit(c) then return self:next_number(c) end 66 | if c == '\'' or c == '\"' then return self:next_short_string(c) end 67 | if c == '[' then 68 | local lvl = self:check_long_bracket() 69 | if lvl then return self:next_long_string(lvl, 'string') end 70 | end 71 | 72 | return self:next_symbol(c) 73 | 74 | else 75 | 76 | local s = '' 77 | while true do 78 | s = s .. c 79 | c = self:get_char() 80 | if not (isalpha_(c) or isdigit(c)) then 81 | if c and not isspace(c) then 82 | self.text_pos = self.text_pos - 1 83 | self.column = self.column - 1 84 | end 85 | break 86 | end 87 | end 88 | 89 | return s 90 | end 91 | 92 | end 93 | 94 | end 95 | 96 | 97 | local lower_a_byte = ('a'):byte() 98 | local upper_A_byte = ('A'):byte() 99 | 100 | function class:next_short_string(quote_char) 101 | 102 | local t, n = { quote_char }, 2 103 | 104 | local skip_ws = false 105 | while true do 106 | local c = self:get_char() 107 | while skip_ws do 108 | if isspace(c) then c = self:get_char() else skip_ws = false end 109 | end 110 | if c == quote_char then break end 111 | if c == '\n' or c == '\r' or (not c) then 112 | return nil, 'unfinished string' 113 | end 114 | if c == '\\' then 115 | c = self:get_char() 116 | -- backslash followed by newline: always generates "\n" 117 | if c == '\r' or c == '\n' then 118 | self:skip_newline() 119 | c = '\n' 120 | -- backslash followed by 'z': skip to next non-whitespace character 121 | elseif c == 'z' then 122 | c = '' 123 | skip_ws = true 124 | -- backslash followed by a C escape character 125 | elseif c == 'a' then c = '\x07' 126 | elseif c == 'b' then c = '\x08' 127 | elseif c == 't' then c = '\x09' 128 | elseif c == 'n' then c = '\x0A' 129 | elseif c == 'v' then c = '\x0B' 130 | elseif c == 'f' then c = '\x0C' 131 | elseif c == 'r' then c = '\x0D' 132 | elseif c == '"' or c == "'" or c == '\\' then ; 133 | -- character code in hex 134 | elseif c == 'x' then 135 | local x1 = self:get_char() 136 | local x2 = self:get_char() 137 | if not isxdigit(x1) or not isxdigit(x2) then 138 | return nil, 'hexadecimal digit expected in string escape sequence' 139 | end 140 | if x1 >= 'a' then x1 = 10 + x1:byte() - lower_a_byte 141 | elseif x1 >= 'A' then x1 = 10 + x1:byte() - upper_A_byte end 142 | if x2 >= 'a' then x2 = 10 + x2:byte() - lower_a_byte 143 | elseif x2 >= 'A' then x2 = 10 + x2:byte() - upper_A_byte end 144 | c = string.char(x1 * 16 + x2) 145 | -- character code in decimal 146 | elseif isdigit(c) then 147 | local d1, d2, d3 = 0, 0, c 148 | if isdigit(self:peek_chars(1)) then 149 | d2 = d3 150 | d3 = self:get_char() 151 | if isdigit(self:peek_chars(1)) then 152 | d1 = d2 153 | d2 = d3 154 | d3 = self:get_char() 155 | end 156 | end 157 | c = string.char(d1 * 100 + d2 * 10 + d3) 158 | else 159 | return nil, 'invalid escape sequence' 160 | end 161 | end 162 | t[n] = c 163 | n = n + 1 164 | end 165 | 166 | t[n] = quote_char 167 | return table.concat(t) 168 | end 169 | 170 | 171 | function class:next_long_string(lvl, string_or_comment) 172 | 173 | local t, n = { "'" }, 2 174 | 175 | local chk = string.rep('=', lvl) .. ']' 176 | local c = self:peek_chars(1) 177 | if c == '\r' or c == '\n' then 178 | self:get_char() 179 | self:skip_newline() -- skip initial newline 180 | end 181 | while true do 182 | c = self:get_char() 183 | if c == '\r' or c == '\n' then 184 | self:skip_newline() 185 | c = '\n' 186 | else 187 | if c == ']' and self:peek_chars(lvl + 1) == chk then 188 | for i = 0, lvl do self:get_char() end 189 | break 190 | elseif (not c) then 191 | return nil, 'unfinished long ' .. string_or_comment 192 | end 193 | end 194 | t[n] = c 195 | n = n + 1 196 | end 197 | 198 | t[n] = "'" 199 | return table.concat(t) 200 | end 201 | 202 | 203 | function class:check_long_bracket() 204 | 205 | local c = self:peek_chars(1) 206 | if c == '[' then 207 | self:get_char() 208 | return 0 209 | end 210 | if c == '=' then 211 | local lvl = 1 212 | local chk = '=' 213 | while true do 214 | local s = self:peek_chars(lvl + 1) 215 | if s == chk .. '[' then 216 | --print ('Compared', s, 'with', chk..'[', 'and selecting level', lvl) 217 | for i = 0, lvl do self:get_char() end 218 | return lvl 219 | elseif s == chk .. '=' then 220 | lvl = lvl + 1 221 | chk = chk .. '=' 222 | --print ('Compared', s, 'with', chk, 'and advancing level') 223 | else 224 | break 225 | end 226 | end 227 | end 228 | return nil 229 | end 230 | 231 | 232 | function class:next_number(c) 233 | 234 | local initial_pos = self.text_pos 235 | 236 | local x, exp1, exp2 = self:peek_chars(1), 'e', 'E' 237 | x = (c == '0' and (x == 'x' or x == 'X')) 238 | if x then 239 | self:get_char() 240 | c = self:peek_chars(1) 241 | if isxdigit(c) then exp1, exp2 = 'p', 'P' 242 | else exp1 = nil end 243 | end 244 | 245 | if exp1 then 246 | while true do 247 | c = self:peek_chars(1) 248 | if c == exp1 or c == exp2 then 249 | exp1, exp2 = nil, nil 250 | self:get_char() 251 | c = self:get_char() 252 | if not (c == '-' or c == '+' or isdigit(c)) then break end 253 | elseif isdigit(c) or isalpha_(c) or c == '.' then self:get_char() 254 | else break end 255 | end 256 | end 257 | 258 | local num = tonumber(self.text:sub(initial_pos, self.text_pos)) 259 | if num then return string.format("%.17g", num) else return nil, "invalid number" end 260 | 261 | end 262 | 263 | 264 | function class:next_symbol(c) 265 | 266 | -- we can convert a minus token followed by digits (i.e. a number token) 267 | -- into a negative) number token, but only if the minus token is separated 268 | -- from the previous token by a space character. 269 | if c == '-' then 270 | if (not isdigit(self:peek_chars(1))) 271 | or (self.text_pos > 0 and 272 | (not isspace(self.text:sub(self.text_pos - 1, self.text_pos - 1)))) 273 | then return c 274 | else 275 | local num, err = self:next_number(self:get_char()) 276 | --if type(num) == "number" then num = -num end 277 | if num then num = "-" .. num end 278 | return num, err 279 | end 280 | end 281 | 282 | if c == '+' or c == '*' or c == '/' or c == '%' or c == '^' or c == '#' 283 | or c == '(' or c == ')' or c == '{' or c == '}' or c == '[' or c == ']' 284 | or c == ';' or c == ',' then return c end 285 | 286 | if c == '.' then 287 | local c2 = self:peek_chars(1) 288 | if isdigit(c2) then return self:next_number(c) 289 | elseif c2 ~= '.' then return '.' end 290 | self:get_char(1) 291 | if self:peek_chars(1) ~= '.' then return '..' end 292 | self:get_char(1) 293 | return '...' 294 | end 295 | 296 | if c == ':' or c == '=' then 297 | if self:peek_chars(1) ~= c then return c end 298 | self:get_char() 299 | return c .. c 300 | end 301 | 302 | if c == '<' or c == '>' then 303 | if self:peek_chars(1) ~= '=' then return c end 304 | self:get_char() 305 | return c .. '=' 306 | end 307 | 308 | if c == '~' and self:peek_chars(1) == '=' then 309 | self:get_char() 310 | return c .. '=' 311 | end 312 | 313 | return nil, 'unrecognized symbol ' .. c 314 | 315 | end 316 | 317 | 318 | function class:get_char() 319 | 320 | local c 321 | if (self.text_pos < self.text_len) then 322 | self.text_pos = self.text_pos + 1 323 | c = self.text:sub(self.text_pos, self.text_pos) 324 | if c == '\n' then 325 | self.line = self.line + 1 326 | self.column = 1 327 | else 328 | self.column = self.column + 1 329 | end 330 | end 331 | return c 332 | 333 | end 334 | 335 | 336 | function class:peek_chars(n) 337 | 338 | local s 339 | if (self.text_pos + n <= self.text_len) then 340 | s = self.text:sub(self.text_pos + 1, self.text_pos + n) 341 | end 342 | return s 343 | 344 | end 345 | 346 | 347 | function class:skip_newline() 348 | 349 | local n = self.text_pos 350 | local c1 = self.text:sub(n, n) 351 | if c1 == '\r' or c1 == '\n' then 352 | local c2 = self.text:sub(n + 1, n + 1) 353 | if (c2 == '\r' or c2 == '\n') and (c2 ~= c1) then 354 | self:get_char() 355 | end 356 | end 357 | 358 | end 359 | 360 | 361 | return class 362 | -------------------------------------------------------------------------------- /compiler/main.lua: -------------------------------------------------------------------------------- 1 | 2 | local ok, transpiler = pcall(function() return require("transpiler") end) 3 | if not ok then 4 | local idx = arg[0]:reverse():find('/', 1, true) or 0 5 | package.path = arg[0]:sub(1, -idx) .. "?.lua;" .. (package.path or "") 6 | transpiler = require("transpiler") 7 | end 8 | 9 | local pgmname = arg[0] or "Loulabelle" 10 | 11 | local function usage() 12 | local version = transpiler(nil, nil, {version=true}) 13 | print ("Loulabelle, Lua to JavaScript compiler, version " .. version) 14 | print ("usage: " .. pgmname .. " [-h] [-o] [-J] [-L var] [-n chunkname] inputfile") 15 | print ("-h: display usage") 16 | print ("-o: optimize function calls and turn off call stack") 17 | print ("-G: disallow assignment to globals without table reference") 18 | print ("-J: disallow JavaScript statements") 19 | print ("-A: disallow assume keywords") 20 | print ("-L: set object name for JavaScript (default $lua)") 21 | print ("-n: set chunk name (default same as inputfile argument)") 22 | return 1 23 | end 24 | 25 | local debug, jsallow, globals, assumes = true, true, true, true 26 | local jsobject, chunkname 27 | local i = 1 28 | while true do 29 | local s = arg[i] 30 | if not s or s:sub(1,1) ~= '-' then break end 31 | if s == '-o' then 32 | debug = false 33 | table.remove(arg, i) 34 | elseif s == '-G' then 35 | globals = false 36 | table.remove(arg, i) 37 | elseif s == '-J' then 38 | jsallow = false 39 | table.remove(arg, i) 40 | elseif s == '-A' then 41 | assumes = false 42 | table.remove(arg, i) 43 | elseif s == '-L' then 44 | table.remove(arg, i) 45 | jsobject = arg[i] 46 | if not jsobject then return usage() end 47 | table.remove(arg, i) 48 | elseif s == '-n' then 49 | table.remove(arg, i) 50 | chunkname = arg[i] 51 | if not chunkname then return usage() end 52 | table.remove(arg, i) 53 | else return usage() end 54 | end 55 | 56 | local infile, filename 57 | if #arg == 0 then 58 | infile = io.stdin 59 | filename = "(stdin)" 60 | elseif #arg <= 1 then 61 | filename = arg[i] 62 | infile = assert(io.open(arg[i], "r")) 63 | i = i + 1 64 | else return usage() end 65 | 66 | if not chunkname then 67 | local idx = filename:reverse():find('/', 1, true) or 0 68 | chunkname = filename:sub(-idx + 1) 69 | end 70 | 71 | local source = infile:read("*all") 72 | local javascript, errmsg = transpiler( 73 | chunkname, source, { 74 | debug = debug, 75 | jsobject = jsobject, 76 | globals = globals, 77 | assumes = assumes, 78 | JavaScript = jsallow 79 | }) 80 | if errmsg then 81 | io.stderr:write(filename..': '..errmsg..'\n') 82 | os.exit(2) 83 | end 84 | 85 | print (javascript) 86 | 87 | return 0 88 | -------------------------------------------------------------------------------- /compiler/statestack.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | local class = {} 4 | 5 | 6 | function class.new() 7 | 8 | local self = { } 9 | for k,_ in pairs(class) do self[k] = class[k] end 10 | return self 11 | 12 | end 13 | 14 | 15 | function class:push() 16 | 17 | local n = self.n or 0 18 | local new_st = {} 19 | if n > 0 then 20 | local old_st = self[self.n] 21 | for k,v in pairs(old_st) do new_st[k] = v end 22 | end 23 | n = n + 1 24 | self[n] = new_st 25 | self.n = n 26 | 27 | end 28 | 29 | 30 | function class:pop() 31 | 32 | local n = self.n or 0 33 | if n > 0 then 34 | self[n] = nil 35 | n = n - 1 36 | self.n = n 37 | end 38 | 39 | end 40 | 41 | 42 | function class:set(k, v) 43 | 44 | local n = self.n or 0 45 | if n > 0 then self[n][k] = v end 46 | 47 | end 48 | 49 | 50 | function class:get(k) 51 | 52 | local n = self.n or 0 53 | if n > 0 then return self[n][k] else return nil end 54 | 55 | end 56 | 57 | 58 | function class:count() 59 | 60 | local n = self.n or 0 61 | if n > 0 then return #self[n] else return 0 end 62 | 63 | end 64 | 65 | 66 | return class 67 | -------------------------------------------------------------------------------- /compiler/transpiler.lua: -------------------------------------------------------------------------------- 1 | 2 | local jspfx = package.JavaScript and "Loulabelle*" or "" 3 | local Parser = require(jspfx.."parser") 4 | local Emitter = require(jspfx.."emitter") 5 | 6 | return function(source_name, source_text, source_options) 7 | 8 | if source_options then 9 | assert(type(source_options) == "table") 10 | if source_options.version then 11 | return "1.0", 1.0 12 | end 13 | end 14 | assert(type(source_name) == "string") 15 | assert(type(source_text) == "string") 16 | 17 | local result 18 | local tree, err = Parser.parse(source_text, source_name) 19 | if not err then 20 | 21 | source_options = source_options or {} 22 | tree.jsobject = source_options.jsobject 23 | tree.debug = source_options.debug == nil and true or source_options.debug 24 | tree.JavaScript = source_options.JavaScript == nil and true or source_options.JavaScript 25 | tree.globals = source_options.globals == nil and true or source_options.globals 26 | tree.assumes = source_options.assumes == nil and true or source_options.assumes 27 | 28 | result, err = Emitter.generate(tree) 29 | end 30 | 31 | if (not err) and source_options.env then 32 | 33 | local idx1 = result:index("function*func") 34 | local idx2 = result:rindex("}") 35 | if idx1 and idx2 then 36 | 37 | result = result:sub(idx1, idx2) 38 | JavaScript("try{var f=Function('return '+$1)()}catch(e){$2=e.toString()}", result, err) 39 | if not err then 40 | JavaScript("$3=$L.func(f,$1,undefined,$2,1)", source_options.env, source_name, result) 41 | end 42 | 43 | else 44 | result = nil 45 | err = "Internal error in compiler output" 46 | end 47 | end 48 | 49 | return result, err 50 | 51 | end 52 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
https://www.spaceflint.com/loulabelle
10 |https://www.spaceflint.com/?p=161 11 | 12 |
https://www.spaceflint.com/loulabelle/playground
14 | 15 |https://www.spaceflint.com/loulabelle/manual.html
17 | 18 |https://www.spaceflint.com/loulabelle/loulabelle-5203.zip
20 | 21 |https://github.com/spaceflint7/loulabelle
23 | -------------------------------------------------------------------------------- /manual.css: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * from Lua 5.2 lua.css 4 | */ 5 | 6 | html { 7 | background-color: #F8F8F8 ; 8 | } 9 | 10 | body { 11 | border: solid #a0a0a0 1px ; 12 | border-radius: 20px ; 13 | padding: 26px ; 14 | margin: 16px ; 15 | color: #000000 ; 16 | background-color: #FFFFFF ; 17 | font-family: Helvetica, Arial, sans-serif ; 18 | text-align: justify ; 19 | line-height: 1.25 ; 20 | } 21 | 22 | h1, h2, h3, h4 { 23 | font-family: Verdana, Geneva, sans-serif ; 24 | font-weight: normal ; 25 | font-style: normal ; 26 | } 27 | 28 | h2 { 29 | padding-top: 0.4em ; 30 | padding-bottom: 0.4em ; 31 | padding-left: 0.8em ; 32 | padding-right: 0.8em ; 33 | background-color: #D0D0FF ; 34 | border-radius: 8px ; 35 | border: solid #a0a0a0 1px ; 36 | } 37 | 38 | h3 { 39 | padding-left: 0.5em ; 40 | border-left: solid #D0D0FF 1em ; 41 | } 42 | 43 | table h3 { 44 | padding-left: 0px ; 45 | border-left: none ; 46 | } 47 | 48 | a:link { 49 | color: #000080 ; 50 | background-color: inherit ; 51 | text-decoration: none ; 52 | } 53 | 54 | a:visited { 55 | background-color: inherit ; 56 | text-decoration: none ; 57 | } 58 | 59 | a:link:hover, a:visited:hover { 60 | color: #000080 ; 61 | background-color: #D0D0FF ; 62 | border-radius: 4px ; 63 | } 64 | 65 | a:link:active, a:visited:active { 66 | color: #FF0000 ; 67 | } 68 | 69 | h1 a img { 70 | vertical-align: text-bottom ; 71 | } 72 | 73 | hr { 74 | border: 0 ; 75 | height: 1px ; 76 | color: #a0a0a0 ; 77 | background-color: #a0a0a0 ; 78 | display: none ; 79 | } 80 | 81 | table hr { 82 | display: block ; 83 | } 84 | 85 | :target { 86 | background-color: #F8F8F8 ; 87 | padding: 8px ; 88 | border: solid #a0a0a0 2px ; 89 | border-radius: 8px ; 90 | } 91 | 92 | .footer { 93 | color: gray ; 94 | font-size: x-small ; 95 | } 96 | 97 | input[type=text] { 98 | border: solid #a0a0a0 2px ; 99 | border-radius: 2em ; 100 | background-image: url('images/search.png') ; 101 | background-repeat: no-repeat ; 102 | background-position: 4px center ; 103 | padding-left: 20px ; 104 | height: 2em ; 105 | } 106 | 107 | pre.session { 108 | background-color: #F8F8F8 ; 109 | padding: 1em ; 110 | border-radius: 8px ; 111 | } 112 | 113 | /* 114 | * from Lua 5.2 manual.css 115 | */ 116 | 117 | h3 code { 118 | font-family: inherit ; 119 | font-size: inherit ; 120 | } 121 | 122 | pre, code { 123 | font-size: 12pt ; 124 | } 125 | 126 | span.apii { 127 | float: right ; 128 | font-family: inherit ; 129 | font-style: normal ; 130 | font-size: small ; 131 | color: gray ; 132 | } 133 | 134 | p+h1, ul+h1 { 135 | font-style: normal ; 136 | padding-top: 0.4em ; 137 | padding-bottom: 0.4em ; 138 | padding-left: 16px ; 139 | margin-left: -16px ; 140 | background-color: #D0D0FF ; 141 | border-radius: 8px ; 142 | border: solid #000080 1px ; 143 | } 144 | 145 | /* 146 | * some additions 147 | */ 148 | 149 | pre { 150 | overflow-x: auto; 151 | background-color: lightyellow; 152 | border: solid black 1px; 153 | padding: 3px; 154 | } 155 | -------------------------------------------------------------------------------- /playground/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | source ../build.cfg 4 | 5 | pushd ../compiler > /dev/null 6 | ./build.sh $TARGET 7 | popd > /dev/null 8 | 9 | MYDIR=`pwd` 10 | TARGET=$TARGET/playground 11 | 12 | function copy() { 13 | while [ -n "$1" ]; do 14 | if [ ${1: -4} == ".lua" ]; then 15 | if [ $1 -nt $TARGET/${1%.lua}.js ]; then 16 | echo Compiling $1 17 | pushd ../compiler > /dev/null 18 | lua main.lua $LFLAGS $MYDIR/$1 > $TARGET/${1%.lua}.js 19 | if [ $? != 0 ]; then rm $TARGET/${1%.lua}.js; fi 20 | popd > /dev/null 21 | fi 22 | else 23 | if [ $1 -nt $TARGET/$1 ]; then 24 | echo Copying $1 25 | cp $1 $TARGET 26 | fi 27 | fi 28 | shift 29 | done 30 | } 31 | 32 | copy handlebars.lua document.lua storage.lua 33 | 34 | copy index.html index.css demo.lua 35 | 36 | copy finder.html finder.css finder.lua 37 | copy editor.html editor.css editor.lua 38 | copy console.html console.css console.lua 39 | 40 | copy serverdb.php mysql_compat.php 41 | -------------------------------------------------------------------------------- /playground/console.css: -------------------------------------------------------------------------------- 1 | 2 | #console-section { 3 | height: 100%; 4 | width: 80vw; 5 | font-family: Lucida Console, monaco, Courier New, monospace; 6 | font-size: 16pt; 7 | } 8 | 9 | #console-placeholder { 10 | display: none; 11 | } 12 | 13 | #console-iframe { 14 | position: absolute; 15 | left: 20vw; 16 | width: 80vw; 17 | top: 0; 18 | z-index: 99; 19 | } 20 | 21 | #console-section .CodeMirror { 22 | font-family: inherit; 23 | font-size: inherit; 24 | height: 100%; 25 | width: 100%; 26 | } 27 | 28 | #console-input { 29 | display: block; 30 | font-family: inherit; 31 | font-size: inherit; 32 | flex-grow: 20; 33 | width: 95%; 34 | background: yellow; 35 | } 36 | 37 | /* 38 | * mobile 39 | */ 40 | 41 | @media only screen and (max-device-width: 768px) { 42 | 43 | #console-section { 44 | width: 100vw; 45 | } 46 | 47 | #console-iframe { 48 | left: 0vw; 49 | width: 100vw; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /playground/console.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |