Skip to content

Commit

Permalink
Elminate loadstring. Do parsing manually in complex.to.
Browse files Browse the repository at this point in the history
String syntax has changed slightly.
Suggested by Satheesh.
  • Loading branch information
davidm committed Dec 14, 2011
1 parent 58790b6 commit 8dd7aa2
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 62 deletions.
4 changes: 4 additions & 0 deletions doc/complex_changelog.txt
@@ -1,5 +1,9 @@
complex changelog

v 0.3.3: 2011-12-13
- Elminate loadstring. Do parsing manually in complex.to.
String syntax has changed slightly.

v 0.3.2: 2011-12-03
- Add _VERSION

Expand Down
108 changes: 53 additions & 55 deletions lua/complex.lua
Expand Up @@ -59,16 +59,61 @@ DOWNLOAD/INSTALL
--/////////////--
-- link to complex table
local complex = {_TYPE='module', _NAME='complex', _VERSION='0.3.2.20111203'}
local complex = {_TYPE='module', _NAME='complex', _VERSION='0.3.3.20111212'}
-- link to complex metatable
local complex_meta = {}
-- helper functions for parsing complex number strings.
local function parse_scalar(s, pos0)
local x, n, pos = s:match('^([+-]?[%d%.]+)(.?)()', pos0)
if not x then return end
if n == 'e' or n == 'E' then
local x2, n2, pos2 = s:match('^([+-]?%d+)(.?)()', pos)
if not x2 then error 'number format error' end
x = tonumber(x..n..x2)
if not x then error 'number format error' end
return x, n2, pos2
else
x = tonumber(x)
if not x then error 'number format error' end
return x, n, pos
end
end
local function parse_component(s, pos0)
local x, n, pos = parse_scalar(s, pos0)
if not x then
local x2, n2, pos2 = s:match('^([+-]?)(i)()$', pos0)
if not x2 then error 'number format error' end
return (x2=='-' and -1 or 1), n2, pos2
end
if n == '/' then
local x2, n2, pos2 = parse_scalar(s, pos)
x = x / x2
return x, n2, pos2
end
return x, n, pos
end
local function parse_complex(s)
local x, n, pos = parse_component(s, 1)
if n == '+' or n == '-' then
local x2, n2, pos2 = parse_component(s, pos)
if n2 ~= 'i' or pos2 ~= #s+1 then error 'number format error' end
if n == '-' then x2 = - x2 end
return x, x2
elseif n == '' then
return x, 0
elseif n == 'i' then
if pos ~= #s+1 then error 'number format error' end
return 0, x
else
error 'number format error'
end
end
-- complex.to( arg )
-- return a complex number on success
-- return nil on failure
local _retone = function() return 1 end
local _retminusone = function() return -1 end
function complex.to( num )
-- check for table type
if type( num ) == "table" then
Expand All @@ -88,55 +133,8 @@ function complex.to( num )
return setmetatable( { isnum,0 }, complex_meta )
end
if type( num ) == "string" then
-- check for real and complex
-- number chars [%-%+%*%^%d%./Ee]
local real,sign,imag = string.match( num, "^([%-%+%*%^%d%./Ee]*%d)([%+%-])([%-%+%*%^%d%./Ee]*)i$" )
if real then
if string.lower(string.sub(real,1,1)) == "e"
or string.lower(string.sub(imag,1,1)) == "e" then
return
end
if imag == "" then
if sign == "+" then
imag = _retone
else
imag = _retminusone
end
elseif sign == "+" then
imag = loadstring("return tonumber("..imag..")")
else
imag = loadstring("return tonumber("..sign..imag..")")
end
real = loadstring("return tonumber("..real..")")
if real and imag then
return setmetatable( { real(),imag() }, complex_meta )
end
return
end
-- check for complex
local imag = string.match( num,"^([%-%+%*%^%d%./Ee]*)i$" )
if imag then
if imag == "" then
return setmetatable( { 0,1 }, complex_meta )
elseif imag == "-" then
return setmetatable( { 0,-1 }, complex_meta )
end
if string.lower(string.sub(imag,1,1)) ~= "e" then
imag = loadstring("return tonumber("..imag..")")
if imag then
return setmetatable( { 0,imag() }, complex_meta )
end
end
return
end
-- should be real
local real = string.match( num,"^(%-*[%d%.][%-%+%*%^%d%./Ee]*)$" )
if real then
real = loadstring( "return tonumber("..real..")" )
if real then
return setmetatable( { real(),0 }, complex_meta )
end
end
local real, imag = parse_complex(num)
return setmetatable( { real, imag }, complex_meta )
end
end
Expand Down Expand Up @@ -170,7 +168,7 @@ end
-- complex.convpolardeg( r, phi )
-- convert polar coordinates ( r*e^(i*phi) ) to carthesic complex number
-- r (radius) is a number
-- phi must be in degrees; e.g. [0° - 360°]
-- phi must be in degrees; e.g. [0 - 360 deg]
function complex.convpolardeg( radius, phi )
phi = phi/180 * math.pi
return setmetatable( { radius * math.cos( phi ), radius * math.sin( phi ) }, complex_meta )
Expand Down Expand Up @@ -219,7 +217,7 @@ end
-- complex.polardeg( cx )
-- from complex number to polar coordinates
-- output in degrees; [-180°,180°]
-- output in degrees; [-180, 180 deg]
-- returns r (radius), phi (angle)
function complex.polardeg( cx )
return math.sqrt( cx[1]^2 + cx[2]^2 ), math.atan2( cx[2], cx[1] ) / math.pi * 180
Expand Down Expand Up @@ -421,4 +419,4 @@ return complex
--///////////////--
--// chillcode //--
--///////////////--
--///////////////--
33 changes: 26 additions & 7 deletions test/test_complex.lua
Expand Up @@ -8,16 +8,35 @@ assert( tostring( cx ) == "2+3i" )
cx = complex ( 2 )
assert( tostring( cx ) == "2" )
assert( cx:tostring() == 2 )
cx = complex "2^2+3/2i"
assert( tostring( cx ) == "4+1.5i" )
cx = complex ".5-2E-3i"
assert( tostring( cx ) == "0.5-0.002i" )
cx = complex "3i"
assert( tostring( cx ) == "3i" )
--old:no longer supported: assert( tostring(complex '2^2+3/2i') == '4+1.5i' )
assert( tostring( complex '.5-2E-3i' ) == '0.5-0.002i' )
assert( tostring( complex '0' ) == '0' )
assert( tostring( complex '10' ) == '10' )
assert( tostring( complex '10.2' ) == '10.2' )
assert( tostring( complex '-10.2' ) == '-10.2' )
assert( tostring( complex '-10.2e2' ) == '-1020' )
assert( tostring( complex '-10.2E+02' ) == '-1020' )
assert( tostring( complex 'i' ) == 'i' )
assert( tostring( complex '-i' ) == '-i' )
assert( tostring( complex '3i' ) == '3i' )
assert( tostring( complex '-3i' ) == '-3i' )
assert( tostring( complex '0-3i' ) == '-3i' )
assert( tostring( complex '1/+2+3/-4i' ) == '0.5-0.75i' )
assert( tostring( complex '1e+0/2.0-1e0/2.0i' ) == '0.5-0.5i' )
-- bad formatting
assert( not pcall(complex, '') )
assert( not pcall(complex, '2 + 4i') ) -- space
assert( not pcall(complex, '1+2i ') ) -- space
assert( not pcall(complex, 'i+1') ) -- reversed
assert( tostring(complex'2 ') == '2' ) --ok (space, not invoking tonumber directly)
assert( not pcall(complex, '-') )
assert( not pcall(complex, '++1') )
assert( not pcall(complex, '1/2/3') ) -- multiple operations

--
cx = complex "2"
assert( tostring( cx ) == "2" )
assert( cx:tostring() == 2 )
assert( complex "2 + 4i" == nil )

-- complex.new
cx = complex.new( 2,3 )
Expand Down

0 comments on commit 8dd7aa2

Please sign in to comment.