Compare commits
No commits in common. "d7f87a37e85ad2ccaebe28a001e788ff422d2308" and "00f39ac9cadf49e184c4bf309172155ac276a0cb" have entirely different histories.
d7f87a37e8
...
00f39ac9ca
12 changed files with 39 additions and 496 deletions
|
|
@ -1,410 +0,0 @@
|
||||||
use crate::ast::{ASTNode, ASTNodeKind, Span};
|
|
||||||
use crate::lexer::Token;
|
|
||||||
use crate::parser::Parser;
|
|
||||||
use logos::Logos;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
const EXTENSION: &str = ".sui";
|
|
||||||
|
|
||||||
/// Error types for import resolution
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ImportError {
|
|
||||||
DuplicateSymbol {
|
|
||||||
symbol: String,
|
|
||||||
locations: Vec<String>,
|
|
||||||
},
|
|
||||||
FileNotFound {
|
|
||||||
path: String,
|
|
||||||
reason: String,
|
|
||||||
},
|
|
||||||
LexError {
|
|
||||||
file: String,
|
|
||||||
position: usize,
|
|
||||||
},
|
|
||||||
ParseError {
|
|
||||||
file: String,
|
|
||||||
message: String,
|
|
||||||
span: (usize, usize),
|
|
||||||
},
|
|
||||||
CircularDependency {
|
|
||||||
files: Vec<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ImportError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ImportError::DuplicateSymbol { symbol, locations } => {
|
|
||||||
write!(f, "Duplicate global symbol '{}' defined in:\n", symbol)?;
|
|
||||||
for (i, loc) in locations.iter().enumerate() {
|
|
||||||
write!(f, " [{}] {}", i + 1, loc)?;
|
|
||||||
if i < locations.len() - 1 {
|
|
||||||
writeln!(f)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ImportError::FileNotFound { path, reason } => {
|
|
||||||
write!(f, "Module not found '{}': {}", path, reason)
|
|
||||||
}
|
|
||||||
ImportError::LexError { file, position } => {
|
|
||||||
write!(f, "Lexer error at position {} in {}", position, file)
|
|
||||||
}
|
|
||||||
ImportError::ParseError {
|
|
||||||
file,
|
|
||||||
message,
|
|
||||||
span,
|
|
||||||
} => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Parse error in {}: {} (at {}..{})",
|
|
||||||
file, message, span.0, span.1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ImportError::CircularDependency { files } => {
|
|
||||||
write!(f, "Circular dependency detected: {}", files.join(" -> "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ImportError {}
|
|
||||||
|
|
||||||
/// Cached parse output for a file
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct ParsedFile {
|
|
||||||
/// AST nodes from this file (before import collection)
|
|
||||||
nodes: Vec<ASTNode>,
|
|
||||||
/// Hash of the file content at parse time
|
|
||||||
content_hash: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tracks global symbols and their definitions
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct GlobalSymbolRegistry {
|
|
||||||
/// Maps symbol name to (file, span) where it was defined
|
|
||||||
symbols: HashMap<String, Vec<(String, Span)>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlobalSymbolRegistry {
|
|
||||||
fn register(&mut self, symbol: String, file: String, span: Span) -> Result<(), ImportError> {
|
|
||||||
self.symbols.entry(symbol.clone()).or_insert_with(Vec::new);
|
|
||||||
let locations = self.symbols.get_mut(&symbol).unwrap();
|
|
||||||
|
|
||||||
locations.push((file.clone(), span));
|
|
||||||
|
|
||||||
// Check for duplicates
|
|
||||||
if locations.len() > 1 {
|
|
||||||
let location_strs = locations
|
|
||||||
.iter()
|
|
||||||
.map(|(f, s)| format!("{}:{}..{}", f, s.start, s.end))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
return Err(ImportError::DuplicateSymbol {
|
|
||||||
symbol,
|
|
||||||
locations: location_strs,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Manages file dependency resolution and caching
|
|
||||||
pub struct ImportResolver {
|
|
||||||
/// Cache of parsed files keyed by path
|
|
||||||
parse_cache: HashMap<String, ParsedFile>,
|
|
||||||
/// Global symbol registry for duplicate detection
|
|
||||||
symbol_registry: GlobalSymbolRegistry,
|
|
||||||
/// Set of files currently being processed (for cycle detection)
|
|
||||||
processing_stack: HashSet<String>,
|
|
||||||
/// Dependency graph: file -> list of files it depends on
|
|
||||||
dependency_graph: HashMap<String, HashSet<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImportResolver {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
ImportResolver {
|
|
||||||
parse_cache: HashMap::new(),
|
|
||||||
symbol_registry: GlobalSymbolRegistry::default(),
|
|
||||||
processing_stack: HashSet::new(),
|
|
||||||
dependency_graph: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute a simple hash of file content
|
|
||||||
fn hash_content(content: &str) -> u64 {
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
content.hash(&mut hasher);
|
|
||||||
hasher.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lex source code into tokens
|
|
||||||
fn lex_source(source: &str, filename: &str) -> Result<Vec<(Token, logos::Span)>, ImportError> {
|
|
||||||
let mut tokens = Vec::new();
|
|
||||||
let mut lexer = Token::lexer(source);
|
|
||||||
|
|
||||||
while let Some(token_result) = lexer.next() {
|
|
||||||
match token_result {
|
|
||||||
Ok(token) => {
|
|
||||||
tokens.push((token, lexer.span()));
|
|
||||||
}
|
|
||||||
Err(()) => {
|
|
||||||
return Err(ImportError::LexError {
|
|
||||||
file: filename.to_string(),
|
|
||||||
position: lexer.span().start,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse tokens into AST
|
|
||||||
fn parse_tokens(
|
|
||||||
tokens: Vec<(Token, logos::Span)>,
|
|
||||||
filename: String,
|
|
||||||
) -> Result<Vec<ASTNode>, ImportError> {
|
|
||||||
let mut parser = Parser::new(filename.clone(), tokens);
|
|
||||||
parser.parse().map_err(|e| ImportError::ParseError {
|
|
||||||
file: filename,
|
|
||||||
message: e.message.clone(),
|
|
||||||
span: (e.span.start, e.span.end),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a file (using cache if content hash matches)
|
|
||||||
fn parse_file(&mut self, filename: &str) -> Result<Vec<ASTNode>, ImportError> {
|
|
||||||
let source = fs::read_to_string(filename).map_err(|e| ImportError::FileNotFound {
|
|
||||||
path: filename.to_string(),
|
|
||||||
reason: e.to_string(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let content_hash = Self::hash_content(&source);
|
|
||||||
|
|
||||||
// Check if we have a valid cached version
|
|
||||||
if let Some(cached) = self.parse_cache.get(filename) {
|
|
||||||
if cached.content_hash == content_hash {
|
|
||||||
println!(" [cache hit] {}", filename);
|
|
||||||
return Ok(cached.nodes.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the file
|
|
||||||
println!(" [parsing] {}", filename);
|
|
||||||
let tokens = Self::lex_source(&source, filename)?;
|
|
||||||
let nodes = Self::parse_tokens(tokens, filename.to_string())?;
|
|
||||||
|
|
||||||
// Cache the result
|
|
||||||
self.parse_cache.insert(
|
|
||||||
filename.to_string(),
|
|
||||||
ParsedFile {
|
|
||||||
nodes: nodes.clone(),
|
|
||||||
content_hash,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect all dependencies from a file (via Use/Load statements)
|
|
||||||
fn collect_dependencies(&self, filename: &str, nodes: &[ASTNode]) -> Vec<String> {
|
|
||||||
let mut deps = Vec::new();
|
|
||||||
let mut seen = HashSet::new();
|
|
||||||
|
|
||||||
for node in nodes {
|
|
||||||
if let ASTNodeKind::Use(use_stmt) = &node.kind {
|
|
||||||
let path = &use_stmt.path;
|
|
||||||
if !seen.contains(path) {
|
|
||||||
seen.insert(path.clone());
|
|
||||||
deps.push(path.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve relative paths
|
|
||||||
let importing_dir = Path::new(filename).parent().unwrap_or(Path::new(""));
|
|
||||||
|
|
||||||
deps.iter()
|
|
||||||
.filter_map(|path| {
|
|
||||||
let resolved = if path.starts_with("std/") {
|
|
||||||
Some(format!("src/{}{}", path, EXTENSION))
|
|
||||||
} else if path.starts_with("@") {
|
|
||||||
// Package manager support - TODO
|
|
||||||
None
|
|
||||||
} else if path.starts_with(".") || path.starts_with("~") {
|
|
||||||
let resolved_path = importing_dir.join(path);
|
|
||||||
Some(resolved_path.to_string_lossy().to_string() + EXTENSION)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
resolved
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check for circular dependencies
|
|
||||||
fn detect_cycles(&self, file: &str, target: &str) -> Result<(), ImportError> {
|
|
||||||
if file == target {
|
|
||||||
return Err(ImportError::CircularDependency {
|
|
||||||
files: vec![file.to_string()],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple cycle detection: if target depends on file, we have a cycle
|
|
||||||
if let Some(target_deps) = self.dependency_graph.get(target) {
|
|
||||||
if target_deps.contains(file) {
|
|
||||||
return Err(ImportError::CircularDependency {
|
|
||||||
files: vec![file.to_string(), target.to_string()],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Recursively resolve imports for a file
|
|
||||||
fn resolve_imports_recursive(&mut self, filename: &str) -> Result<Vec<ASTNode>, ImportError> {
|
|
||||||
// Check for cycles in processing
|
|
||||||
if self.processing_stack.contains(filename) {
|
|
||||||
return Err(ImportError::CircularDependency {
|
|
||||||
files: self
|
|
||||||
.processing_stack
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.chain(std::iter::once(filename.to_string()))
|
|
||||||
.collect(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.processing_stack.insert(filename.to_string());
|
|
||||||
|
|
||||||
// Parse the file
|
|
||||||
let nodes = self.parse_file(filename)?;
|
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
// Collect dependencies
|
|
||||||
let deps = self.collect_dependencies(filename, &nodes);
|
|
||||||
self.dependency_graph
|
|
||||||
.insert(filename.to_string(), deps.iter().cloned().collect());
|
|
||||||
|
|
||||||
// Process each node
|
|
||||||
for node in nodes {
|
|
||||||
match &node.kind {
|
|
||||||
ASTNodeKind::Use(use_stmt) => {
|
|
||||||
let path = &use_stmt.path;
|
|
||||||
|
|
||||||
// Resolve path
|
|
||||||
let dep_file = if path.starts_with("std/") {
|
|
||||||
format!("src/{}{}", path, EXTENSION)
|
|
||||||
} else if path.starts_with("@") {
|
|
||||||
// Package manager support - TODO
|
|
||||||
todo!("Package manager imports not yet supported")
|
|
||||||
} else if path.starts_with(".") || path.starts_with("~") {
|
|
||||||
let importing_dir = Path::new(filename).parent().unwrap_or(Path::new(""));
|
|
||||||
let resolved_path = importing_dir.join(path);
|
|
||||||
resolved_path.to_string_lossy().to_string() + EXTENSION
|
|
||||||
} else {
|
|
||||||
return Err(ImportError::FileNotFound {
|
|
||||||
path: path.clone(),
|
|
||||||
reason: "Invalid import path format".to_string(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check for cycles
|
|
||||||
self.detect_cycles(filename, &dep_file)?;
|
|
||||||
|
|
||||||
// Recursively resolve the imported file
|
|
||||||
let imported_nodes = self.resolve_imports_recursive(&dep_file)?;
|
|
||||||
result.extend(imported_nodes);
|
|
||||||
}
|
|
||||||
ASTNodeKind::Struct(s) => {
|
|
||||||
// Register the symbol
|
|
||||||
self.symbol_registry.register(
|
|
||||||
s.name.clone(),
|
|
||||||
filename.to_string(),
|
|
||||||
node.span.clone(),
|
|
||||||
)?;
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
ASTNodeKind::Enum(e) => {
|
|
||||||
self.symbol_registry.register(
|
|
||||||
e.name.clone(),
|
|
||||||
filename.to_string(),
|
|
||||||
node.span.clone(),
|
|
||||||
)?;
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
ASTNodeKind::Function(f) => {
|
|
||||||
self.symbol_registry.register(
|
|
||||||
f.name.clone(),
|
|
||||||
filename.to_string(),
|
|
||||||
node.span.clone(),
|
|
||||||
)?;
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
ASTNodeKind::Impl(_) => {
|
|
||||||
// Impl blocks don't have names but are still tracked
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
ASTNodeKind::Extern(e) => {
|
|
||||||
self.symbol_registry.register(
|
|
||||||
e.name.clone(),
|
|
||||||
filename.to_string(),
|
|
||||||
node.span.clone(),
|
|
||||||
)?;
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
ASTNodeKind::Load(_) => {
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
ASTNodeKind::Trait(t) => {
|
|
||||||
self.symbol_registry.register(
|
|
||||||
t.name.clone(),
|
|
||||||
filename.to_string(),
|
|
||||||
node.span.clone(),
|
|
||||||
)?;
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.processing_stack.remove(filename);
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolve all imports starting from the given file
|
|
||||||
pub fn resolve(&mut self, filename: &str) -> Result<Vec<ASTNode>, ImportError> {
|
|
||||||
println!("Starting import resolution...");
|
|
||||||
self.resolve_imports_recursive(filename)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hash_content() {
|
|
||||||
let content1 = "fn main() do end";
|
|
||||||
let content2 = "fn main() do end";
|
|
||||||
let content3 = "fn main() { x }";
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
ImportResolver::hash_content(content1),
|
|
||||||
ImportResolver::hash_content(content2)
|
|
||||||
);
|
|
||||||
assert_ne!(
|
|
||||||
ImportResolver::hash_content(content1),
|
|
||||||
ImportResolver::hash_content(content3)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@ pub mod ast;
|
||||||
pub mod c_ir;
|
pub mod c_ir;
|
||||||
pub mod c_lowerer;
|
pub mod c_lowerer;
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
pub mod import_resolver;
|
|
||||||
pub mod lambda_lower;
|
pub mod lambda_lower;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod monomorphize;
|
pub mod monomorphize;
|
||||||
|
|
|
||||||
39
src/main.rs
39
src/main.rs
|
|
@ -1,9 +1,11 @@
|
||||||
|
use logos::Logos;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use suicmez::{
|
use suicmez::{
|
||||||
codegen::transpiler::Transpiler,
|
codegen::transpiler::Transpiler,
|
||||||
import_resolver::ImportResolver,
|
|
||||||
lambda_lower::LambdaLowerer,
|
lambda_lower::LambdaLowerer,
|
||||||
|
lexer::Token,
|
||||||
monomorphize::{Monomorphizer, check_no_typevars},
|
monomorphize::{Monomorphizer, check_no_typevars},
|
||||||
|
parser::Parser,
|
||||||
typechecker::TypeChecker,
|
typechecker::TypeChecker,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -162,19 +164,34 @@ fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> S
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_file(filename: &str) -> Result<(), String> {
|
fn run_file(filename: &str) -> Result<(), String> {
|
||||||
// ========== IMPORT RESOLUTION PHASE ==========
|
// Read the source file
|
||||||
println!("\n=== Import Resolution Phase ===");
|
|
||||||
let mut resolver = ImportResolver::new();
|
|
||||||
let ast_nodes = resolver
|
|
||||||
.resolve(filename)
|
|
||||||
.map_err(|e| format!("Import resolution error: {}", e))?;
|
|
||||||
|
|
||||||
println!("Import resolution complete! {} total nodes loaded", ast_nodes.len());
|
|
||||||
|
|
||||||
// Read the source file for error reporting
|
|
||||||
let source = fs::read_to_string(filename)
|
let source = fs::read_to_string(filename)
|
||||||
.map_err(|e| format!("Error reading file {}: {}", filename, e))?;
|
.map_err(|e| format!("Error reading file {}: {}", filename, e))?;
|
||||||
|
|
||||||
|
// First, we need to parse the source code
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
let mut lexer = Token::lexer(&source);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match lexer.next() {
|
||||||
|
Some(Ok(token)) => {
|
||||||
|
let span = lexer.span();
|
||||||
|
tokens.push((token, span));
|
||||||
|
}
|
||||||
|
Some(Err(_)) => {
|
||||||
|
return Err("Lexing error".to_string());
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parser = Parser::new(filename.to_string(), tokens);
|
||||||
|
let ast_nodes = parser
|
||||||
|
.parse()
|
||||||
|
.map_err(|e| format_parse_error(&source, &e))?;
|
||||||
|
|
||||||
|
println!("Parsed {} AST nodes successfully", ast_nodes.len());
|
||||||
|
|
||||||
// Lower lambdas to generated functions
|
// Lower lambdas to generated functions
|
||||||
let lowerer = LambdaLowerer::new();
|
let lowerer = LambdaLowerer::new();
|
||||||
let lowered_nodes = lowerer
|
let lowered_nodes = lowerer
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
int add(int x, int y);
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int add(int x, int y) {
|
|
||||||
return (x + y);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
int sum = add(5, 3);
|
|
||||||
struct T id = identity(42);
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
use "./module_a"
|
|
||||||
use "./module_b"
|
|
||||||
|
|
||||||
fn main() -> int do
|
|
||||||
0
|
|
||||||
end
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
struct Point {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
int helper_func(void);
|
|
||||||
int main(void);
|
|
||||||
|
|
||||||
|
|
||||||
int helper_func(void) {
|
|
||||||
return 42;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
return helper_func();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
use "./util"
|
|
||||||
|
|
||||||
fn main() -> int do
|
|
||||||
helper_func()
|
|
||||||
end
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn test_func() -> int do
|
|
||||||
42
|
|
||||||
end
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn test_func() -> int do
|
|
||||||
99
|
|
||||||
end
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
fn helper_func() -> int do
|
|
||||||
42
|
|
||||||
end
|
|
||||||
|
|
||||||
struct Point
|
|
||||||
x: int,
|
|
||||||
y: int,
|
|
||||||
end
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(void);
|
int main(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct Number {
|
struct Number {
|
||||||
int value;
|
int value;
|
||||||
};
|
};
|
||||||
|
|
||||||
char *Number_show(struct Number self);
|
char *Number_show(struct Number self);
|
||||||
int main(void);
|
int main(void);
|
||||||
|
|
||||||
|
char *Number_show(struct Number self) { return "number"; }
|
||||||
char* Number_show(struct Number self) {
|
|
||||||
return "number";
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct Number number = (struct Number){.value = 40};
|
struct Number number = (struct Number){.value = 40};
|
||||||
Number_show(number);
|
Number_show(number);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue