mirror of
https://github.com/Noratrieb/uwucc.git
synced 2026-01-17 01:55:07 +01:00
some stuff works
This commit is contained in:
parent
4b646b9128
commit
79ab4bbb75
7 changed files with 239 additions and 100 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
use dbg_pls::DebugPls;
|
use dbg_pls::DebugPls;
|
||||||
|
|
||||||
use crate::{Span, Spanned};
|
use crate::Spanned;
|
||||||
|
|
||||||
#[derive(Debug, DebugPls)]
|
#[derive(Debug, DebugPls)]
|
||||||
pub enum TypeSpecifier {
|
pub enum TypeSpecifier {
|
||||||
|
|
@ -24,13 +26,45 @@ pub enum TypeSpecifier {
|
||||||
|
|
||||||
pub type Ident = Spanned<String>;
|
pub type Ident = Spanned<String>;
|
||||||
|
|
||||||
#[derive(Debug, Default, DebugPls)]
|
#[derive(Default)]
|
||||||
pub struct DeclAttr {
|
pub struct DeclAttr {
|
||||||
pub is_extern: bool,
|
pub is_extern: bool,
|
||||||
pub is_static: bool,
|
pub is_static: bool,
|
||||||
pub is_thread_local: bool,
|
pub is_thread_local: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for DeclAttr {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut d = f.debug_set();
|
||||||
|
if self.is_extern {
|
||||||
|
d.entry(&"extern");
|
||||||
|
}
|
||||||
|
if self.is_static {
|
||||||
|
d.entry(&"static");
|
||||||
|
}
|
||||||
|
if self.is_thread_local {
|
||||||
|
d.entry(&"thread_local");
|
||||||
|
}
|
||||||
|
d.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebugPls for DeclAttr {
|
||||||
|
fn fmt(&self, f: dbg_pls::Formatter<'_>) {
|
||||||
|
let mut d = f.debug_set();
|
||||||
|
if self.is_extern {
|
||||||
|
d = d.entry(&"extern");
|
||||||
|
}
|
||||||
|
if self.is_static {
|
||||||
|
d = d.entry(&"static");
|
||||||
|
}
|
||||||
|
if self.is_thread_local {
|
||||||
|
d = d.entry(&"thread_local");
|
||||||
|
}
|
||||||
|
d.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, DebugPls)]
|
#[derive(Debug, DebugPls)]
|
||||||
pub struct DeclSpec {
|
pub struct DeclSpec {
|
||||||
pub ty: TypeSpecifier,
|
pub ty: TypeSpecifier,
|
||||||
|
|
@ -55,12 +89,18 @@ pub struct NormalDecl {
|
||||||
pub init_declarators: Vec<Spanned<InitDecl>>,
|
pub init_declarators: Vec<Spanned<InitDecl>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, DebugPls)]
|
||||||
|
pub struct FunctionParamDecl {
|
||||||
|
pub decl_spec: Spanned<DeclSpec>,
|
||||||
|
pub declarator: Spanned<Declarator>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, DebugPls)]
|
#[derive(Debug, DebugPls)]
|
||||||
pub enum DirectDeclarator {
|
pub enum DirectDeclarator {
|
||||||
Ident(Ident),
|
Ident(Ident),
|
||||||
WithParams {
|
WithParams {
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
params: Vec<NormalDecl>,
|
params: Vec<FunctionParamDecl>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,18 +110,6 @@ pub struct Declarator {
|
||||||
pub pointer: bool,
|
pub pointer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, DebugPls)]
|
|
||||||
pub struct FunctionParamDecl {
|
|
||||||
pub decl_spec: DeclSpec,
|
|
||||||
pub declarator: Declarator,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, DebugPls)]
|
|
||||||
pub enum FunctionParams {
|
|
||||||
Void(Span),
|
|
||||||
List(Vec<Spanned<NormalDecl>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, DebugPls)]
|
#[derive(Debug, DebugPls)]
|
||||||
pub struct FunctionDef {
|
pub struct FunctionDef {
|
||||||
pub declaration: Decl,
|
pub declaration: Decl,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use peekmore::PeekMoreIterator;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Decl, DeclAttr, DeclSpec, Declarator, DirectDeclarator, ExternalDecl, FunctionDef,
|
Decl, DeclAttr, DeclSpec, Declarator, DirectDeclarator, ExternalDecl, FunctionDef,
|
||||||
FunctionParams, Ident, InitDecl, NormalDecl, TypeSpecifier,
|
FunctionParamDecl, Ident, InitDecl, NormalDecl, TypeSpecifier,
|
||||||
},
|
},
|
||||||
pre::Punctuator as Punct,
|
pre::Punctuator as Punct,
|
||||||
token::{Keyword as Kw, Token as Tok},
|
token::{Keyword as Kw, Token as Tok},
|
||||||
|
|
@ -154,14 +154,14 @@ where
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
|
||||||
/// (6.7) declaration:
|
/// (6.7) declaration:
|
||||||
/// declaration-specifiers init-declarator-listopt ;
|
/// declaration-specifiers init-declarator-list.opt ;
|
||||||
/// static_assert-declaration
|
/// static_assert-declaration
|
||||||
fn declaration(&mut self) -> Result<Spanned<Decl>> {
|
fn declaration(&mut self) -> Result<Spanned<Decl>> {
|
||||||
if let Some((tok, span)) = eat!(self, Tok::Kw(Kw::StaticAssert)) {
|
if let Some((tok, span)) = eat!(self, Tok::Kw(Kw::StaticAssert)) {
|
||||||
return Err(ParserError::unsupported(span, &tok));
|
return Err(ParserError::unsupported(span, &tok));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (decl_spec, span) = self.declaration_specifiers()?;
|
let (decl_spec, span) = self.decl_specifiers()?;
|
||||||
|
|
||||||
let init_declarators = self.init_declarator_list()?;
|
let init_declarators = self.init_declarator_list()?;
|
||||||
let init_declarators_span = Span::span_of_spanned_list(&init_declarators);
|
let init_declarators_span = Span::span_of_spanned_list(&init_declarators);
|
||||||
|
|
@ -187,11 +187,7 @@ where
|
||||||
fn init_declarator_list(&mut self) -> Result<Vec<Spanned<InitDecl>>> {
|
fn init_declarator_list(&mut self) -> Result<Vec<Spanned<InitDecl>>> {
|
||||||
let mut init_decls = Vec::new();
|
let mut init_decls = Vec::new();
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
loop {
|
while self.is_peek_tok_start_of_declarator() || self.is_peek_comma() {
|
||||||
println!("LOOOOP");
|
|
||||||
if !self.is_peek_tok_start_of_declarator() && !self.is_peek_comma() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if !first {
|
if !first {
|
||||||
expect!(self, Tok::Punct(Punct::Comma));
|
expect!(self, Tok::Punct(Punct::Comma));
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +213,7 @@ where
|
||||||
/// type-qualifier declaration-specifiers.opt
|
/// type-qualifier declaration-specifiers.opt
|
||||||
/// function-specifier declaration-specifiers.opt
|
/// function-specifier declaration-specifiers.opt
|
||||||
/// alignment-specifier declaration-specifiers.opt
|
/// alignment-specifier declaration-specifiers.opt
|
||||||
fn declaration_specifiers(&mut self) -> Result<Spanned<DeclSpec>> {
|
fn decl_specifiers(&mut self) -> Result<Spanned<DeclSpec>> {
|
||||||
let mut decl_attr = DeclAttr::default();
|
let mut decl_attr = DeclAttr::default();
|
||||||
let &(_, initial_span) = self.peek_t()?;
|
let &(_, initial_span) = self.peek_t()?;
|
||||||
let (ty, span) = loop {
|
let (ty, span) = loop {
|
||||||
|
|
@ -299,48 +295,72 @@ where
|
||||||
/// facing. For example: `int uwu` vs `int uwu()`. The parentheses indicate a function
|
/// facing. For example: `int uwu` vs `int uwu()`. The parentheses indicate a function
|
||||||
/// declaration. Therefore, we have no idea what we're parsing before entering this function.
|
/// declaration. Therefore, we have no idea what we're parsing before entering this function.
|
||||||
fn declarator(&mut self) -> Result<Spanned<Declarator>> {
|
fn declarator(&mut self) -> Result<Spanned<Declarator>> {
|
||||||
if let Some((_, span)) = eat!(self, Tok::Punct(Punct::Asterisk)) {
|
let pointer_span = if let Some((_, span)) = eat!(self, Tok::Punct(Punct::Asterisk)) {
|
||||||
let (decl, span2) = self.direct_declarator()?;
|
Some(span)
|
||||||
|
|
||||||
Ok((
|
|
||||||
Declarator {
|
|
||||||
decl,
|
|
||||||
pointer: true,
|
|
||||||
},
|
|
||||||
span.extend(span2),
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let (decl, span) = self.direct_declarator()?;
|
let (decl, span) = self.direct_declarator()?;
|
||||||
|
|
||||||
Ok((
|
let declarator = Declarator {
|
||||||
Declarator {
|
|
||||||
decl,
|
decl,
|
||||||
pointer: false,
|
pointer: pointer_span.is_some(),
|
||||||
},
|
};
|
||||||
span,
|
|
||||||
))
|
let span = pointer_span.map(|s| s.extend(span)).unwrap_or(span);
|
||||||
}
|
|
||||||
|
Ok((declarator, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// direct-declarator:
|
/// direct-declarator:
|
||||||
/// identifier
|
/// identifier
|
||||||
/// ( declarator )
|
/// ( declarator )
|
||||||
/// direct-declarator \[ type-qualifier-listopt assignment-expressionopt ]
|
/// direct-declarator \[ type-qualifier-list.opt assignment-expression.opt ]
|
||||||
/// direct-declarator \[ static type-qualifier-listopt assignment-expression ]
|
/// direct-declarator \[ static type-qualifier-list.opt assignment-expression ]
|
||||||
/// direct-declarator \[ type-qualifier-list static assignment-expression ]
|
/// direct-declarator \[ type-qualifier-list static assignment-expression ]
|
||||||
/// direct-declarator \[ type-qualifier-listopt * ]
|
/// direct-declarator \[ type-qualifier-list.opt * ]
|
||||||
/// direct-declarator ( parameter-type-list )
|
/// direct-declarator ( parameter-type-list )
|
||||||
/// direct-declarator ( identifier-listopt )
|
/// direct-declarator ( identifier-list.opt )
|
||||||
fn direct_declarator(&mut self) -> Result<Spanned<DirectDeclarator>> {
|
fn direct_declarator(&mut self) -> Result<Spanned<DirectDeclarator>> {
|
||||||
let (ident, span) = self.ident()?;
|
let (ident, span) = self.ident()?;
|
||||||
|
|
||||||
if (eat!(self, Tok::Punct(Punct::ParenOpen))).is_some() {
|
if (eat!(self, Tok::Punct(Punct::ParenOpen))).is_some() {
|
||||||
|
let mut params = Vec::new();
|
||||||
|
let mut first = true;
|
||||||
|
|
||||||
|
while self.is_peek_tok_start_of_ty() || self.is_peek_comma() {
|
||||||
|
if first {
|
||||||
|
// the wrong way around because borrowing
|
||||||
|
if let (Tok::Punct(Punct::ParenClose), _) = self.peek_t_n(1)? {
|
||||||
|
if let &(ref tok @ Tok::Kw(Kw::Void), span) = self.peek_t()? {
|
||||||
|
return Err(ParserError::unsupported(span, tok));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !first {
|
||||||
|
expect!(self, Tok::Punct(Punct::Comma));
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
let decl_spec = self.decl_specifiers()?;
|
||||||
|
// abstract declarator actually
|
||||||
|
let declarator = self.declarator()?;
|
||||||
|
|
||||||
|
let function_param_decl = FunctionParamDecl {
|
||||||
|
decl_spec,
|
||||||
|
declarator,
|
||||||
|
};
|
||||||
|
params.push(function_param_decl);
|
||||||
|
}
|
||||||
|
|
||||||
// nothing in the params supported yet.
|
// nothing in the params supported yet.
|
||||||
expect!(self, Tok::Punct(Punct::ParenClose));
|
expect!(self, Tok::Punct(Punct::ParenClose));
|
||||||
return Ok((
|
return Ok((
|
||||||
DirectDeclarator::WithParams {
|
DirectDeclarator::WithParams {
|
||||||
ident: (ident, span),
|
ident: (ident, span),
|
||||||
params: Vec::new(),
|
params,
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
));
|
));
|
||||||
|
|
@ -384,22 +404,6 @@ where
|
||||||
}
|
}
|
||||||
Ok(decls)
|
Ok(decls)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_param_declaration_list(&mut self) -> Result<FunctionParams> {
|
|
||||||
// If the declarator includes a parameter type list, the declaration of each parameter shall
|
|
||||||
// include an identifier, except for the special case of a parameter list consisting of a single
|
|
||||||
// parameter of type void, in which case there shall not be an identifier. No declaration list
|
|
||||||
// shall follow.
|
|
||||||
if let &(Tok::Kw(Kw::Void), span) = self.peek_t()? {
|
|
||||||
if let (Tok::Punct(Punct::ParenClose), _) = self.peek_t_n(1)? {
|
|
||||||
self.next_t()?;
|
|
||||||
self.next_t()?;
|
|
||||||
return Ok(FunctionParams::Void(span));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
---
|
||||||
|
source: parser/src/parser/tests.rs
|
||||||
|
expression: parsed
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
FunctionDef(
|
||||||
|
FunctionDef {
|
||||||
|
declaration: Normal(
|
||||||
|
NormalDecl {
|
||||||
|
decl_spec: DeclSpec {
|
||||||
|
ty: Int,
|
||||||
|
attrs: {},
|
||||||
|
},
|
||||||
|
init_declarators: [
|
||||||
|
(
|
||||||
|
InitDecl {
|
||||||
|
declarator: Declarator {
|
||||||
|
decl: WithParams {
|
||||||
|
ident: (
|
||||||
|
"uwu",
|
||||||
|
5..8,
|
||||||
|
),
|
||||||
|
params: [
|
||||||
|
FunctionParamDecl {
|
||||||
|
decl_spec: (
|
||||||
|
DeclSpec {
|
||||||
|
ty: Long,
|
||||||
|
attrs: {},
|
||||||
|
},
|
||||||
|
9..13,
|
||||||
|
),
|
||||||
|
declarator: (
|
||||||
|
Declarator {
|
||||||
|
decl: Ident(
|
||||||
|
(
|
||||||
|
"owo",
|
||||||
|
14..17,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pointer: false,
|
||||||
|
},
|
||||||
|
14..17,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
FunctionParamDecl {
|
||||||
|
decl_spec: (
|
||||||
|
DeclSpec {
|
||||||
|
ty: Int,
|
||||||
|
attrs: {},
|
||||||
|
},
|
||||||
|
19..22,
|
||||||
|
),
|
||||||
|
declarator: (
|
||||||
|
Declarator {
|
||||||
|
decl: Ident(
|
||||||
|
(
|
||||||
|
"qwq",
|
||||||
|
23..26,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pointer: false,
|
||||||
|
},
|
||||||
|
23..26,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
pointer: false,
|
||||||
|
},
|
||||||
|
init: None,
|
||||||
|
},
|
||||||
|
5..8,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
body: [],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
1..30,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
@ -11,10 +11,9 @@ Ok(
|
||||||
NormalDecl {
|
NormalDecl {
|
||||||
decl_spec: DeclSpec {
|
decl_spec: DeclSpec {
|
||||||
ty: Int,
|
ty: Int,
|
||||||
attrs: DeclAttr {
|
attrs: {
|
||||||
is_extern: true,
|
"extern",
|
||||||
is_static: false,
|
"thread_local",
|
||||||
is_thread_local: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
init_declarators: [
|
init_declarators: [
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,7 @@ Ok(
|
||||||
NormalDecl {
|
NormalDecl {
|
||||||
decl_spec: DeclSpec {
|
decl_spec: DeclSpec {
|
||||||
ty: Void,
|
ty: Void,
|
||||||
attrs: DeclAttr {
|
attrs: {},
|
||||||
is_extern: false,
|
|
||||||
is_static: false,
|
|
||||||
is_thread_local: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
init_declarators: [
|
init_declarators: [
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,7 @@ Ok(
|
||||||
NormalDecl {
|
NormalDecl {
|
||||||
decl_spec: DeclSpec {
|
decl_spec: DeclSpec {
|
||||||
ty: Int,
|
ty: Int,
|
||||||
attrs: DeclAttr {
|
attrs: {},
|
||||||
is_extern: false,
|
|
||||||
is_static: false,
|
|
||||||
is_thread_local: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
init_declarators: [
|
init_declarators: [
|
||||||
(
|
(
|
||||||
|
|
@ -44,10 +40,8 @@ Ok(
|
||||||
NormalDecl {
|
NormalDecl {
|
||||||
decl_spec: DeclSpec {
|
decl_spec: DeclSpec {
|
||||||
ty: Double,
|
ty: Double,
|
||||||
attrs: DeclAttr {
|
attrs: {
|
||||||
is_extern: false,
|
"thread_local",
|
||||||
is_static: false,
|
|
||||||
is_thread_local: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
init_declarators: [
|
init_declarators: [
|
||||||
|
|
@ -87,5 +81,36 @@ Ok(
|
||||||
),
|
),
|
||||||
11..40,
|
11..40,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
Decl(
|
||||||
|
Normal(
|
||||||
|
NormalDecl {
|
||||||
|
decl_spec: DeclSpec {
|
||||||
|
ty: Int,
|
||||||
|
attrs: {},
|
||||||
|
},
|
||||||
|
init_declarators: [
|
||||||
|
(
|
||||||
|
InitDecl {
|
||||||
|
declarator: Declarator {
|
||||||
|
decl: WithParams {
|
||||||
|
ident: (
|
||||||
|
"function",
|
||||||
|
68..76,
|
||||||
|
),
|
||||||
|
params: [],
|
||||||
|
},
|
||||||
|
pointer: false,
|
||||||
|
},
|
||||||
|
init: None,
|
||||||
|
},
|
||||||
|
68..76,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
64..76,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::fmt::Debug;
|
||||||
use super::{Parser, Tok};
|
use super::{Parser, Tok};
|
||||||
use crate::Span;
|
use crate::Span;
|
||||||
|
|
||||||
fn lex_and_pre<'src>(src: &'src str) -> impl Iterator<Item = (Tok<'src>, Span)> + 'src {
|
fn lex_and_pre(src: &str) -> impl Iterator<Item = (Tok<'_>, Span)> + '_ {
|
||||||
let pre_tokens = crate::pre::preprocess_tokens(src);
|
let pre_tokens = crate::pre::preprocess_tokens(src);
|
||||||
crate::token::pre_tokens_to_tokens(pre_tokens)
|
crate::token::pre_tokens_to_tokens(pre_tokens)
|
||||||
}
|
}
|
||||||
|
|
@ -28,38 +28,40 @@ macro_rules! parse_test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_void_function() {
|
fn empty_void_function() {
|
||||||
let src = r#"
|
parse_test!(
|
||||||
|
r#"
|
||||||
void uwu() {}
|
void uwu() {}
|
||||||
"#;
|
"#
|
||||||
|
);
|
||||||
parse_test!(src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_funky_attributes_no_params_function() {
|
fn empty_funky_attributes_no_params_function() {
|
||||||
let src = r#"
|
parse_test!(
|
||||||
|
r#"
|
||||||
extern volatile _Thread_local int uwu() {}
|
extern volatile _Thread_local int uwu() {}
|
||||||
"#;
|
"#
|
||||||
|
);
|
||||||
parse_test!(src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "fix declarator mess"]
|
|
||||||
fn empty_function_with_params() {
|
fn empty_function_with_params() {
|
||||||
let src = r#"
|
parse_test!(
|
||||||
int uwu(long owo, unsigned qwq) {}
|
r#"
|
||||||
"#;
|
int uwu(long owo, int qwq) {}
|
||||||
|
"#
|
||||||
parse_test!(src);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn global_variable_declarations() {
|
fn global_variable_declarations() {
|
||||||
let src = r#"
|
parse_test!(
|
||||||
|
r#"
|
||||||
int test;
|
int test;
|
||||||
_Thread_local double uwu, owo;
|
_Thread_local double uwu, owo;
|
||||||
"#;
|
|
||||||
|
|
||||||
parse_test!(src);
|
// oh no, a function
|
||||||
|
int function();
|
||||||
|
"#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue