270 lines
5.7 KiB
Ruby
270 lines
5.7 KiB
Ruby
|
# This file is part of Metasm, the Ruby assembly manipulation suite
|
||
|
# Copyright (C) 2007 Yoann GUILLOT
|
||
|
#
|
||
|
# Licence is LGPL, see LICENCE in the top-level directory
|
||
|
|
||
|
|
||
|
require 'test/unit'
|
||
|
require 'metasm/preprocessor'
|
||
|
require 'metasm/parse'
|
||
|
|
||
|
|
||
|
# BEWARE OF TEH RUBY PARSER
|
||
|
# use single-quoted source strings
|
||
|
class TestPreproc < Test::Unit::TestCase
|
||
|
def load txt, bt = caller.first
|
||
|
p = Metasm::Preprocessor.new
|
||
|
bt =~ /^(.*?):(\d+)/
|
||
|
p.feed txt, $1, $2.to_i+1
|
||
|
p
|
||
|
end
|
||
|
|
||
|
def test_gettok
|
||
|
p = load <<'EOS'
|
||
|
test boo
|
||
|
" bla bla\
|
||
|
\"\\" \
|
||
|
xx
|
||
|
EOS
|
||
|
assert_equal \
|
||
|
['test', :space, :string, :eol, :quoted, :space, 'xx', :eol, true],
|
||
|
[p.readtok.raw, p.readtok.type, p.readtok.type, p.readtok.type, p.readtok.type, p.readtok.type, p.readtok.raw, p.readtok.type, p.eos?]
|
||
|
end
|
||
|
|
||
|
def test_comment
|
||
|
p = load <<'EOS'
|
||
|
foo /*/ bar ' " * /*/ baz
|
||
|
kikoo // lol \
|
||
|
asv
|
||
|
EOS
|
||
|
toks = []
|
||
|
nil while tok = p.readtok and (tok.type == :space or tok.type == :eol)
|
||
|
until p.eos?
|
||
|
toks << tok.raw
|
||
|
nil while tok = p.readtok and (tok.type == :space or tok.type == :eol)
|
||
|
end
|
||
|
assert_equal %w[foo baz kikoo], toks
|
||
|
end
|
||
|
|
||
|
def helper_preparse(text, result)
|
||
|
p = load(text, caller.first)
|
||
|
yield p if block_given?
|
||
|
txt = ''
|
||
|
until p.eos? or not t = p.readtok
|
||
|
txt << t.raw
|
||
|
end
|
||
|
assert_equal(result, txt.strip)
|
||
|
end
|
||
|
|
||
|
def test_preproc
|
||
|
# ignores eol/space at begin/end
|
||
|
helper_preparse(<<EOS, '')
|
||
|
#if 0 // test # as first char
|
||
|
toto
|
||
|
#endif
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'coucou')
|
||
|
#define tutu
|
||
|
#if defined ( tutu )
|
||
|
tutu coucou
|
||
|
#endif
|
||
|
EOS
|
||
|
helper_preparse('a #define b', 'a #define b')
|
||
|
helper_preparse(<<EOS, "// true !\nblu")
|
||
|
#ifdef toto // this is false
|
||
|
bla
|
||
|
#elif .2_3e-4 > 2 /* this one too */
|
||
|
blo
|
||
|
#elif (1+1)*2 > 2 // true !
|
||
|
blu
|
||
|
#elif 4 > 2 // not you
|
||
|
ble
|
||
|
#else
|
||
|
bli
|
||
|
#endif
|
||
|
EOS
|
||
|
helper_preparse(<<'EOS', 'ab#define x')
|
||
|
a\
|
||
|
b\
|
||
|
#define x
|
||
|
EOS
|
||
|
p = load('__LINE__,__DATE__,__TIME__')
|
||
|
assert_equal(__LINE__, p.readtok.value) ; p.readtok
|
||
|
assert_not_equal('__DATE__', p.readtok.raw) ; p.readtok
|
||
|
assert_not_equal('__TIME__', p.readtok.raw)
|
||
|
|
||
|
helper_preparse(<<EOS, 'toto 1 toto 12 toto 3+(3-2) otot hoho')
|
||
|
#define azer(k) 12
|
||
|
# define xxx azer(7)
|
||
|
#define macro(a, b, c) toto a toto b toto c otot
|
||
|
macro(1, xxx, 3+(3-2)) hoho
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'c')
|
||
|
#define a b
|
||
|
#define d c
|
||
|
#define c d
|
||
|
#define b c
|
||
|
a
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'b')
|
||
|
#define b c
|
||
|
#define a b
|
||
|
#undef b
|
||
|
a
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'toto tutu huhu()')
|
||
|
#define toto() abcd
|
||
|
#define tata huhu
|
||
|
toto tutu tata()
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, '"haha"')
|
||
|
#define d(a) #a
|
||
|
d(haha)
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, '{')
|
||
|
#define toto(a) a
|
||
|
toto({)
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'x(, 1)')
|
||
|
#define d(a,b) x(a, b)
|
||
|
d(,1)
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, '"foo" "4"')
|
||
|
#define str(x) #x
|
||
|
#define xstr(x) str(x)
|
||
|
#define foo 4
|
||
|
str(foo) xstr(foo)
|
||
|
EOS
|
||
|
begin
|
||
|
File.open('tests/prepro_testinclude.asm', 'w') { |fd| fd.puts '#define out in' }
|
||
|
helper_preparse(<<EOS, 'in')
|
||
|
#pragma include_path "."
|
||
|
#include <tests/prepro_testinclude.asm>
|
||
|
out
|
||
|
EOS
|
||
|
ensure
|
||
|
File.unlink('tests/prepro_testinclude.asm') rescue nil
|
||
|
end
|
||
|
|
||
|
helper_preparse(<<EOS, 'in') { |p_| p_.hooked_include['bla.h'] = '#define out in' }
|
||
|
#include <bla.h>
|
||
|
out
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'in') { |p_| p_.define('out', 'in') }
|
||
|
out
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'in') { |p_| p_.define_strong('out', 'in') }
|
||
|
out
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'in') { |p_| p_.define_strong('out', 'in') }
|
||
|
#define out poil
|
||
|
out
|
||
|
EOS
|
||
|
helper_preparse(<<EOS, 'in') { |p_| p_.define('out', 'in') ; p_.define_weak('out', 'poil') }
|
||
|
out
|
||
|
EOS
|
||
|
p = load <<EOS
|
||
|
#define cct(a, b) a ## _ ## b
|
||
|
cct(toto, tutu)
|
||
|
EOS
|
||
|
nil while tok = p.readtok and (tok.type == :space or tok.type == :eol)
|
||
|
assert_equal('toto_tutu', tok.raw) # check we get only 1 token back
|
||
|
|
||
|
# assert_outputs_a_warning ?
|
||
|
helper_preparse(<<EOS, <<EOS.strip)
|
||
|
#define va1(a, b...) toto(a, ##b)
|
||
|
#define va3(a, ...) toto(a, __VA_ARGS__)
|
||
|
va1(1, 2);
|
||
|
va1(1,2);
|
||
|
va1(1);
|
||
|
va3(1, 2);
|
||
|
va3(1);
|
||
|
EOS
|
||
|
toto(1, 2);
|
||
|
toto(1,2);
|
||
|
toto(1);
|
||
|
toto(1, 2);
|
||
|
toto(1, );
|
||
|
EOS
|
||
|
|
||
|
helper_preparse(<<EOS, "#define a c\n#define b d\na b")
|
||
|
#define x(z) z
|
||
|
#define y #define
|
||
|
x(#)define a c
|
||
|
y b d
|
||
|
a b
|
||
|
EOS
|
||
|
helper_preparse("#define a(a) a(a)\na(1)", '1(1)')
|
||
|
helper_preparse("#if 0\n#endif", '')
|
||
|
helper_preparse("#if 0U\n#endif", '')
|
||
|
helper_preparse("#if 0L\n#endif", '')
|
||
|
helper_preparse("#if 0LLU\n#endif", '')
|
||
|
end
|
||
|
|
||
|
def test_floats
|
||
|
t_float = lambda { |txt|
|
||
|
text = <<EOS
|
||
|
#if #{txt}
|
||
|
1
|
||
|
#endif
|
||
|
EOS
|
||
|
p = load text, caller.first
|
||
|
txt = ''
|
||
|
t = nil
|
||
|
txt << t.raw until p.eos? or not t = p.readtok
|
||
|
assert_equal('1', txt.strip)
|
||
|
}
|
||
|
t_float['1 > 0']
|
||
|
t_float['1.0 > 0']
|
||
|
t_float['1e2 > 10 && 1.0e2 < 1000']
|
||
|
t_float['1.0e+2 > 10']
|
||
|
t_float['10_00e-2 > 1 && 10_00e-2 < 100']
|
||
|
t_float['.1e2 > 1']
|
||
|
#t_float['0x1.p2L > 1 && 0x1p2f < 5']
|
||
|
t_float['0x1.p2L > 1']
|
||
|
t_float['0x1p2f < 5']
|
||
|
end
|
||
|
|
||
|
def test_errors
|
||
|
test_err = lambda { |txt| assert_raise(Metasm::ParseError) { p = load(txt, caller.first) ; p.readtok until p.eos? } }
|
||
|
t_float = lambda { |txt| assert_raise(Metasm::ParseError) { p = load("#if #{txt}\n#endif", caller.first) ; p.readtok } }
|
||
|
test_err["\"abc\n\""]
|
||
|
test_err['"abc\x"']
|
||
|
test_err['/*']
|
||
|
test_err['#if 0']
|
||
|
test_err["#define toto(tutu,"]
|
||
|
test_err["#define toto( (tutu, tata)"]
|
||
|
test_err['#error bla']
|
||
|
test_err[<<EOS]
|
||
|
#if 0
|
||
|
#elif 1
|
||
|
#else
|
||
|
#if 2
|
||
|
#endif
|
||
|
EOS
|
||
|
test_err[<<EOS]
|
||
|
#define abc(def)
|
||
|
abc (1, 3)
|
||
|
EOS
|
||
|
test_err["#if 0LUL\n#endif"]
|
||
|
# warnings only
|
||
|
#test_err["#define aa\n#define aa"]
|
||
|
#test_err['#define a(b) #c']
|
||
|
#test_err['#define a(b, b)']
|
||
|
#test_err['#define a ##z']
|
||
|
t_float['1e++4']
|
||
|
t_float['1.0e 4']
|
||
|
t_float['_1.0']
|
||
|
t_float['.e2']
|
||
|
t_float['1.1e+_1']
|
||
|
t_float['.2e']
|
||
|
t_float['.']
|
||
|
t_float['1.2e*4']
|
||
|
t_float['0x1.e4']
|
||
|
t_float['0x1.p4a']
|
||
|
t_float['0x.p1']
|
||
|
t_float['0x.1lp1']
|
||
|
end
|
||
|
end
|
||
|
|