diff --git a/bin-compiler/src/main.rs b/bin-compiler/src/main.rs index f9d1b46c4bb3f139e60139fc4f4394361433a731..9ab3e1d1bce01f4594b1f0452e1c7c71935b75c2 100644 --- a/bin-compiler/src/main.rs +++ b/bin-compiler/src/main.rs @@ -63,7 +63,7 @@ fn main() { let input_files = input_files.unwrap(); assert!(input_files.len() > 0); // because arg is required - let mut builder = rw::ProtocolDescriptionBuilder::new(); + let mut builder = rw::ProtocolDescriptionBuilder::new().expect("create protocol description builder"); let mut file_buffer = Vec::with_capacity(4096); for input_file in input_files { diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index e137985fa110c6e2cea9a0f83be7cedf79aef88d..6ad5f1ca98cfa39f02cad26fa0314df2c6f14413 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -1071,6 +1071,36 @@ impl ExpressionInfoVariant { } } +#[derive(Debug)] +pub enum ProcedureSource { + FuncUserDefined, + CompUserDefined, + // Builtin functions, available to user + FuncGet, + FuncPut, + FuncFires, + FuncCreate, + FuncLength, + FuncAssert, + FuncPrint, + // Buitlin functions, not available to user + FuncSelectStart, + FuncSelectRegisterCasePort, + FuncSelectWait, + // Builtin components, available to user + CompRandomU32, // TODO: Remove, temporary thing +} + +impl ProcedureSource { + pub(crate) fn is_builtin(&self) -> bool { + match self { + ProcedureSource::FuncUserDefined | ProcedureSource::CompUserDefined => false, + _ => true, + } + } +} + + /// Generic storage for functions, primitive components and composite /// components. // Note that we will have function definitions for builtin functions as well. In @@ -1080,12 +1110,12 @@ pub struct ProcedureDefinition { pub this: ProcedureDefinitionId, pub defined_in: RootId, // Symbol scanning - pub builtin: bool, pub kind: ProcedureKind, pub span: InputSpan, pub identifier: Identifier, pub poly_vars: Vec, // Parser + pub source: ProcedureSource, pub return_type: Option, // present on functions, not components pub parameters: Vec, pub scope: ScopeId, @@ -1101,9 +1131,9 @@ impl ProcedureDefinition { ) -> Self { Self { this, defined_in, - builtin: false, span, kind, identifier, poly_vars, + source: ProcedureSource::FuncUserDefined, return_type: None, parameters: Vec::new(), scope: ScopeId::new_invalid(), diff --git a/src/protocol/ast_printer.rs b/src/protocol/ast_printer.rs index 22a5db0376dda2d918d174df982d37d19cec4970..6411ae43cdff50018a3480bfe57184099ff812aa 100644 --- a/src/protocol/ast_printer.rs +++ b/src/protocol/ast_printer.rs @@ -770,7 +770,7 @@ impl ASTWriter { self.kv(indent2).with_s_key("Method").with_debug_val(&expr.method); if !expr.procedure.is_invalid() { let definition = &heap[expr.procedure]; - self.kv(indent2).with_s_key("BuiltIn").with_disp_val(&definition.builtin); + self.kv(indent2).with_s_key("Source").with_debug_val(&definition.source); self.kv(indent2).with_s_key("Variant").with_debug_val(&definition.kind); self.kv(indent2).with_s_key("MethodName").with_identifier_val(&definition.identifier); self.kv(indent2).with_s_key("ParserType") diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 45f880fbe64dec700ef72086e324877299969e5b..b60caaf6e913262eeb6de01dc0cdc5f74a85aac7 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -10,7 +10,7 @@ pub(crate) mod ast_printer; use std::sync::Mutex; use crate::collections::{StringPool, StringRef}; -use crate::protocol::ast::*; +pub use crate::protocol::ast::*; use crate::protocol::eval::*; use crate::protocol::input_source::*; use crate::protocol::parser::*; @@ -51,7 +51,7 @@ pub enum ComponentCreationError { impl ProtocolDescription { pub fn parse(buffer: &[u8]) -> Result { let source = InputSource::new(String::new(), Vec::from(buffer)); - let mut parser = Parser::new(); + let mut parser = Parser::new()?; parser.feed(source).expect("failed to feed source"); if let Err(err) = parser.parse() { @@ -223,10 +223,10 @@ pub struct ProtocolDescriptionBuilder { } impl ProtocolDescriptionBuilder { - pub fn new() -> Self { - return Self{ - parser: Parser::new(), - } + pub fn new() -> Result { + return Ok(Self{ + parser: Parser::new()?, + }) } pub fn add(&mut self, filename: String, buffer: Vec) -> Result<(), ParseError> { diff --git a/src/protocol/parser/mod.rs b/src/protocol/parser/mod.rs index e55bf6b31adb04d40e19891ed39a219762f616dd..50f002e4de01949cb9db20e76a9ee5d088d49688 100644 --- a/src/protocol/parser/mod.rs +++ b/src/protocol/parser/mod.rs @@ -33,6 +33,9 @@ use crate::protocol::input_source::*; use crate::protocol::ast_printer::ASTWriter; use crate::protocol::parser::type_table::PolymorphicVariable; +const REOWOLF_PATH_ENV: &'static str = "REOWOLF_ROOT"; // first lookup reowolf path +const REOWOLF_PATH_DIR: &'static str = "std"; // then try folder in current working directory + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum ModuleCompilationPhase { Tokenized, // source is tokenized @@ -134,7 +137,7 @@ pub struct Parser { } impl Parser { - pub fn new() -> Self { + pub fn new() -> Result { let mut parser = Parser{ heap: Heap::new(), string_pool: StringPool::new(), @@ -176,61 +179,9 @@ impl Parser { parser.arch.output_type_id = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Output, ConcreteTypePart::Void], true, 8, 8); parser.arch.pointer_type_id = insert_builtin_type(&mut parser.type_table, vec![ConcreteTypePart::Pointer, ConcreteTypePart::Void], true, 8, 8); - // Insert builtin functions - fn quick_type(variants: &[ParserTypeVariant]) -> ParserType { - let mut t = ParserType{ elements: Vec::with_capacity(variants.len()), full_span: InputSpan::new() }; - for variant in variants { - t.elements.push(ParserTypeElement{ element_span: InputSpan::new(), variant: variant.clone() }); - } - t - } + parser.feed_standard_library()?; - use ParserTypeVariant as PTV; - insert_builtin_function(&mut parser, "get", &["T"], |id| ( - vec![ - ("input", quick_type(&[PTV::Input, PTV::PolymorphicArgument(id.upcast(), 0)])) - ], - quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)]) - )); - insert_builtin_function(&mut parser, "put", &["T"], |id| ( - vec![ - ("output", quick_type(&[PTV::Output, PTV::PolymorphicArgument(id.upcast(), 0)])), - ("value", quick_type(&[PTV::PolymorphicArgument(id.upcast(), 0)])), - ], - quick_type(&[PTV::Void]) - )); - insert_builtin_function(&mut parser, "fires", &["T"], |id| ( - vec![ - ("port", quick_type(&[PTV::InputOrOutput, PTV::PolymorphicArgument(id.upcast(), 0)])) - ], - quick_type(&[PTV::Bool]) - )); - insert_builtin_function(&mut parser, "create", &["T"], |id| ( - vec![ - ("length", quick_type(&[PTV::IntegerLike])) - ], - quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)]) - )); - insert_builtin_function(&mut parser, "length", &["T"], |id| ( - vec![ - ("array", quick_type(&[PTV::ArrayLike, PTV::PolymorphicArgument(id.upcast(), 0)])) - ], - quick_type(&[PTV::UInt32]) // TODO: @PtrInt - )); - insert_builtin_function(&mut parser, "assert", &[], |_id| ( - vec![ - ("condition", quick_type(&[PTV::Bool])), - ], - quick_type(&[PTV::Void]) - )); - insert_builtin_function(&mut parser, "print", &[], |_id| ( - vec![ - ("message", quick_type(&[PTV::String])), - ], - quick_type(&[PTV::Void]) - )); - - parser + return Ok(parser) } pub fn feed(&mut self, mut source: InputSource) -> Result<(), ParseError> { @@ -337,6 +288,63 @@ impl Parser { Ok(()) } + + /// Tries to find the standard library and add the files for parsing. + fn feed_standard_library(&mut self) -> Result<(), String> { + use std::env; + use std::path::{Path, PathBuf}; + use std::fs; + + const FILES: [&'static str; 1] = [ + "std.global.pdl", + ]; + + // Determine base directory + let (base_path, from_env) = if let Ok(path) = env::var(REOWOLF_PATH_ENV) { + // Path variable is set + (path, true) + } else { + let mut path = String::with_capacity(REOWOLF_PATH_DIR.len() + 2); + path.push_str("./"); + path.push_str(REOWOLF_PATH_DIR); + (path, false) + }; + + // Make sure directory exists + let path = Path::new(&base_path); + if !path.exists() { + return Err(format!("std lib root directory '{}' does not exist", base_path)); + } + + // Try to load all standard library files. We might need a more unified + // way to do this in the future (i.e. a "std" package, containing all + // of the modules) + let mut file_path = PathBuf::new(); + for file in FILES { + file_path.push(path); + file_path.push(file); + + let source = fs::read(file_path.as_path()); + if let Err(err) = source { + return Err(format!( + "failed to read std lib file '{}' in root directory '{}', because: {}", + file, base_path, err + )); + } + + let source = source.unwrap(); + let input_source = InputSource::new(file.to_string(), source); + + if let Err(err) = self.feed(input_source) { + // A bit of a hack, but shouldn't really happen anyway: the + // compiler should ship with a decent standard library (at some + // point) + return Err(format!("{}", err)); + } + } + + return Ok(()) + } } fn insert_builtin_type(type_table: &mut TypeTable, parts: Vec, has_poly_var: bool, size: usize, alignment: usize) -> TypeId { @@ -353,79 +361,4 @@ fn insert_builtin_type(type_table: &mut TypeTable, parts: Vec, }; return type_table.add_builtin_data_type(concrete_type, poly_var, size, alignment); -} - -// Note: args and return type need to be a function because we need to know the function ID. -fn insert_builtin_function (Vec<(&'static str, ParserType)>, ParserType)> ( - p: &mut Parser, func_name: &str, polymorphic: &[&str], arg_and_return_fn: T -) { - // Insert into AST (to get an ID), also prepare the polymorphic variables - // we need later for the type table - let mut ast_poly_vars = Vec::with_capacity(polymorphic.len()); - let mut type_poly_vars = Vec::with_capacity(polymorphic.len()); - for poly_var in polymorphic { - let identifier = Identifier{ span: InputSpan::new(), value: p.string_pool.intern(poly_var.as_bytes()) } ; - ast_poly_vars.push(identifier.clone()); - type_poly_vars.push(PolymorphicVariable{ identifier, is_in_use: false }); - } - - let func_ident_ref = p.string_pool.intern(func_name.as_bytes()); - let procedure_id = p.heap.alloc_procedure_definition(|this| ProcedureDefinition { - this, - defined_in: RootId::new_invalid(), - builtin: true, - kind: ProcedureKind::Function, - span: InputSpan::new(), - identifier: Identifier{ span: InputSpan::new(), value: func_ident_ref.clone() }, - poly_vars: ast_poly_vars, - return_type: None, - parameters: Vec::new(), - scope: ScopeId::new_invalid(), - body: BlockStatementId::new_invalid(), - monomorphs: Vec::new(), - }); - - // Modify AST with more information about the procedure - let (arguments, return_type) = arg_and_return_fn(procedure_id); - - let mut parameters = Vec::with_capacity(arguments.len()); - for (arg_name, arg_type) in arguments { - let identifier = Identifier{ span: InputSpan::new(), value: p.string_pool.intern(arg_name.as_bytes()) }; - let param_id = p.heap.alloc_variable(|this| Variable{ - this, - kind: VariableKind::Parameter, - parser_type: arg_type.clone(), - identifier, - relative_pos_in_parent: 0, - unique_id_in_scope: 0 - }); - parameters.push(param_id); - } - - let func = &mut p.heap[procedure_id]; - func.parameters = parameters; - func.return_type = Some(return_type); - - // Insert into symbol table - p.symbol_table.insert_symbol(SymbolScope::Global, Symbol{ - name: func_ident_ref, - variant: SymbolVariant::Definition(SymbolDefinition{ - defined_in_module: RootId::new_invalid(), - defined_in_scope: SymbolScope::Global, - definition_span: InputSpan::new(), - identifier_span: InputSpan::new(), - imported_at: None, - class: DefinitionClass::Function, - definition_id: procedure_id.upcast(), - }) - }).unwrap(); - - // Insert into type table - // let mut concrete_type = ConcreteType::default(); - // concrete_type.parts.push(ConcreteTypePart::Function(procedure_id, type_poly_vars.len() as u32)); - // - // for _ in 0..type_poly_vars.len() { - // concrete_type.parts.push(ConcreteTypePart::Void); // doesn't matter (I hope...) - // } - // p.type_table.add_builtin_procedure_type(concrete_type, &type_poly_vars); } \ No newline at end of file diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index 8ce576bcfa720aef0e2e4d9701150fb216f14b7a..e9f1d96120d8954e41f43ac58d3050c0efdc57e8 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -279,12 +279,13 @@ impl PassDefinitions { module_scope, false, None )?; - // Consume block and the definition's scope - let body_id = self.consume_block_statement(module, iter, ctx)?; + // Consume body + let (body_id, source) = self.consume_procedure_body(module, iter, ctx, definition_id, ProcedureKind::Function)?; let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Definition(definition_id))); // Assign everything in the preallocated AST node let function = ctx.heap[definition_id].as_procedure_mut(); + function.source = source; function.return_type = Some(parser_type); function.parameters = parameters; function.scope = scope_id; @@ -316,13 +317,15 @@ impl PassDefinitions { )?; let parameters = parameter_section.into_vec(); - // Consume block - let body_id = self.consume_block_statement(module, iter, ctx)?; + // Consume body + let procedure_kind = ctx.heap[definition_id].as_procedure().kind; + let (body_id, source) = self.consume_procedure_body(module, iter, ctx, definition_id, procedure_kind)?; let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Definition(definition_id))); // Assign everything in the AST node let component = ctx.heap[definition_id].as_procedure_mut(); debug_assert!(component.return_type.is_none()); + component.source = source; component.parameters = parameters; component.scope = scope_id; component.body = body_id; @@ -330,6 +333,60 @@ impl PassDefinitions { Ok(()) } + /// Consumes a procedure's body: either a user-defined procedure, which we + /// parse as normal, or a builtin function, where we'll make sure we expect + /// the particular builtin. + /// + /// We expect that the procedure's name is already stored in the + /// preallocated AST node. + fn consume_procedure_body( + &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx, definition_id: DefinitionId, kind: ProcedureKind + ) -> Result<(BlockStatementId, ProcedureSource), ParseError> { + if iter.peek() == Some(TokenKind::Pragma) { + let (pragma, pragma_start, pragma_end) = consume_pragma(&module.source, iter)?; + if pragma != b"#builtin" { + return Err(ParseError::new_error_str_at_span( + &module.source, InputSpan::from_positions(pragma_start, pragma_end), + "expected a '#builtin' pragma, or a function body" + )); + } + + // Retrieve module and procedure name + assert!(module.name.is_some(), "compiler error: builtin procedure body in unnamed module"); + let (_, module_name) = module.name.as_ref().unwrap(); + let module_name = module_name.as_str(); + + let definition = ctx.heap[definition_id].as_procedure(); + let procedure_name = definition.identifier.value.as_str(); + + let source = match (module_name, procedure_name) { + ("std.global", "get") => ProcedureSource::FuncGet, + ("std.global", "put") => ProcedureSource::FuncPut, + ("std.global", "fires") => ProcedureSource::FuncFires, + ("std.global", "create") => ProcedureSource::FuncCreate, + ("std.global", "length") => ProcedureSource::FuncLength, + ("std.global", "assert") => ProcedureSource::FuncAssert, + ("std.global", "print") => ProcedureSource::FuncPrint, + _ => panic!( + "compiler error: unknown builtin procedure '{}' in module '{}'", + procedure_name, module_name + ), + }; + + return Ok((BlockStatementId::new_invalid(), source)); + } else { + let body_id = self.consume_block_statement(module, iter, ctx)?; + let source = match kind { + ProcedureKind::Function => + ProcedureSource::FuncUserDefined, + ProcedureKind::Primitive | ProcedureKind::Composite => + ProcedureSource::CompUserDefined, + }; + + return Ok((body_id, source)) + } + } + /// Consumes a statement and returns a boolean indicating whether it was a /// block or not. fn consume_statement(&mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx) -> Result { @@ -1579,22 +1636,19 @@ impl PassDefinitions { }, Definition::Procedure(proc_def) => { // Check whether it is a builtin function + // TODO: Once we start generating bytecode this is unnecessary let procedure_id = proc_def.this; - let method = if proc_def.builtin { - match proc_def.identifier.value.as_bytes() { - KW_FUNC_GET => Method::Get, - KW_FUNC_PUT => Method::Put, - KW_FUNC_FIRES => Method::Fires, - KW_FUNC_CREATE => Method::Create, - KW_FUNC_LENGTH => Method::Length, - KW_FUNC_ASSERT => Method::Assert, - KW_FUNC_PRINT => Method::Print, - _ => unreachable!(), - } - } else if proc_def.kind == ProcedureKind::Function { - Method::UserFunction - } else { - Method::UserComponent + let method = match proc_def.source { + ProcedureSource::FuncUserDefined => Method::UserFunction, + ProcedureSource::CompUserDefined => Method::UserComponent, + ProcedureSource::FuncGet => Method::Get, + ProcedureSource::FuncPut => Method::Put, + ProcedureSource::FuncFires => Method::Fires, + ProcedureSource::FuncCreate => Method::Create, + ProcedureSource::FuncLength => Method::Length, + ProcedureSource::FuncAssert => Method::Assert, + ProcedureSource::FuncPrint => Method::Print, + _ => todo!("other proc sources") }; // Function call: consume the arguments diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index e0a8f8cd4e6f8ef95c3c3668a7c97f4978515729..d5cb3b9311832333ccdf6af675baabc98e6c5b32 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -2074,7 +2074,7 @@ impl PassTyping { procedure_to_check.monomorphs.push(ProcedureDefinitionMonomorph::new_invalid()); let type_id = ctx.types.reserve_procedure_monomorph_type_id(&definition_id, signature_type, monomorph_index); - if !procedure_to_check.builtin { + if !procedure_to_check.source.is_builtin() { // Only perform typechecking on the user-defined // procedures queue.push_back(ResolveQueueElement{ diff --git a/src/protocol/parser/type_table.rs b/src/protocol/parser/type_table.rs index 97ee8c39d8258248411808abf16807bb44fa536f..9c9be71adaba0cfb9393672e228c95f7f1aeec63 100644 --- a/src/protocol/parser/type_table.rs +++ b/src/protocol/parser/type_table.rs @@ -969,7 +969,7 @@ impl TypeTable { // Check and construct return types and argument types. if let Some(return_type) = &definition.return_type { Self::check_member_parser_type( - modules, ctx, root_id, return_type, definition.builtin + modules, ctx, root_id, return_type, definition.source.is_builtin() )?; } @@ -977,7 +977,7 @@ impl TypeTable { for parameter_id in &definition.parameters { let parameter = &ctx.heap[*parameter_id]; Self::check_member_parser_type( - modules, ctx, root_id, ¶meter.parser_type, definition.builtin + modules, ctx, root_id, ¶meter.parser_type, definition.source.is_builtin() )?; arguments.push(ProcedureArgument{ diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index 7dff07dacf06403751b5379528373bca3cd94c92..735cbdc11c4a0e1c5eeb1c13f21d34f281ea3d49 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -59,7 +59,7 @@ impl Tester { } pub(crate) fn compile(self) -> AstTesterResult { - let mut parser = Parser::new(); + let mut parser = Parser::new().unwrap(); for source in self.sources.into_iter() { let source = source.into_bytes(); let input_source = InputSource::new(String::from(""), source); diff --git a/src/runtime2/component/component.rs b/src/runtime2/component/component.rs index 73ee7e4cc4ce5f8a272539990fa31a4ae357d8b0..0937682ed8884505429fe1e2ac2ef4671afe234f 100644 --- a/src/runtime2/component/component.rs +++ b/src/runtime2/component/component.rs @@ -1,6 +1,7 @@ -use crate::protocol::eval::EvalError; +use crate::protocol::eval::*; +use crate::protocol::*; use crate::runtime2::*; -use super::CompCtx; +use super::{CompCtx, CompPDL}; pub enum CompScheduling { Immediate, @@ -22,4 +23,30 @@ pub(crate) trait Component { /// Called if the component's routine should be executed. The return value /// can be used to indicate when the routine should be run again. fn run(&mut self, sched_ctx: &mut SchedulerCtx, comp_ctx: &mut CompCtx) -> Result; +} + +/// Creates a new component based on its definition. Meaning that if it is a +/// user-defined component then we set up the PDL code state. Otherwise we +/// construct a custom component. This does NOT take care of port and message +/// management. +pub(crate) fn create_component( + protocol: &ProtocolDescription, + definition_id: ProcedureDefinitionId, type_id: TypeId, + arguments: ValueGroup, num_ports: usize +) -> Box { + let definition = &protocol.heap[definition_id]; + debug_assert!(definition.kind == ProcedureKind::Primitive || definition.kind == ProcedureKind::Composite); + + if definition.source.is_builtin() { + // Builtin component + todo!("implement") + } else { + // User-defined component + let prompt = Prompt::new( + &protocol.types, &protocol.heap, + definition_id, type_id, arguments + ); + let component = CompPDL::new(prompt, num_ports); + return Box::new(component); + } } \ No newline at end of file diff --git a/src/runtime2/component/component_pdl.rs b/src/runtime2/component/component_pdl.rs index efd6a121c7e84aa016d9121884a82707feca2671..3f3450e88a7a301c828c75b40e73fa7568a59583 100644 --- a/src/runtime2/component/component_pdl.rs +++ b/src/runtime2/component/component_pdl.rs @@ -807,7 +807,7 @@ impl CompPDL { // Take all the ports ID that are in the `args` (and currently belong to // the creator component) and translate them into new IDs that are // associated with the component we're about to create - let mut arg_iter = ValueGroupIter::new(&mut arguments); + let mut arg_iter = ValueGroupPortIter::new(&mut arguments); while let Some(port_reference) = arg_iter.next() { // Create port entry for new component let creator_port_id = port_reference.id; @@ -1010,13 +1010,13 @@ pub(crate) fn find_ports_in_value_group(value_group: &ValueGroup, ports: &mut Ve } } -struct ValueGroupIter<'a> { +struct ValueGroupPortIter<'a> { group: &'a mut ValueGroup, heap_stack: Vec<(usize, usize)>, index: usize, } -impl<'a> ValueGroupIter<'a> { +impl<'a> ValueGroupPortIter<'a> { fn new(group: &'a mut ValueGroup) -> Self { return Self{ group, heap_stack: Vec::new(), index: 0 } } @@ -1028,7 +1028,7 @@ struct ValueGroupPortRef { index: usize, } -impl<'a> Iterator for ValueGroupIter<'a> { +impl<'a> Iterator for ValueGroupPortIter<'a> { type Item = ValueGroupPortRef; fn next(&mut self) -> Option { diff --git a/std/std.global.pdl b/std/std.global.pdl new file mode 100644 index 0000000000000000000000000000000000000000..f1cc7bfa898a5991b090afa445c9fb348ce9ebe5 --- /dev/null +++ b/std/std.global.pdl @@ -0,0 +1,9 @@ +#module std.global + +func get(in) -> T #builtin +func put(out, T value) -> #type_void #builtin +func fires(#type_portlike) -> bool #builtin +func create(#type_integerlike length) -> T[] #builtin +func length(#type_arraylike array) -> u32 #builtin +func assert(bool condition) -> #type_void #builtin +func print(string message) -> #type_void #builtin \ No newline at end of file