huginn - programming language with no quirks, so simple every child can master it.

git clone https://codestation.org/repo/huginn.git
git clone https://github.com/AmokHuginnsson/huginn.git
git clone https://bitbucket.org/huginn/huginn.git
Sample of real life application of Huginn language.
Some of those samples are inspired by Rosetta Code project,
other are inspired by list of popular problems found on esolang page.
Solution for 100 doors problem.
#! /bin/sh exec huginn --no-argv -E "${0}" #! huginn import Algorithms as algo; main() { doorCount = 100; doors = [].resize( doorCount, false ); for ( pass : algo.range( doorCount ) ) { i = 0; step = pass + 1; while ( i < doorCount ) { doors[i] = ! doors[i]; i += step; } } for ( i : algo.range( doorCount ) ) { if ( doors[i] ) { print( "door {} is open\n".format( i ) ); } } return ( 0 ); }
Implementaion of a 24 game.
#! /bin/sh exec huginn --no-argv -E "${0}" #! huginn import Algorithms as algo; import Mathematics as math; import RegularExpressions as re; make_game( rndGen_ ) { board = ""; for ( i : algo.range( 4 ) ) { board += ( " " + string( character( rndGen_.next() + integer( '1' ) ) ) ); } return ( board.strip() ); } main() { rndGen = math.randomizer( 9 ); no = 0; dd = re.compile( "\\d\\d" ); while ( true ) { no += 1; board = make_game( rndGen ); print( "Your four digits: {}\nExpression {}: ".format( board, no ) ); line = input(); if ( line == none ) { print( "\n" ); break; } line = line.strip(); try { if ( line == "q" ) { break; } if ( ( pos = line.find_other_than( "{}+-*/() ".format( board ) ) ) >= 0 ) { print( "Invalid input found at: {}, `{}`\n".format( pos, line ) ); continue; } if ( dd.match( line ).matched() ) { print( "Digit concatenation is forbidden.\n" ); continue; } res = real( line ); if ( res == 24.0 ) { print( "Thats right!\n" ); } else { print( "Bad answer!\n" ); } } catch ( Exception e ) { print( "Not an expression: {}\n".format( e.what() ) ); } } return ( 0 ); }
Print the text of the "99 bottles of beer" song.
#! /bin/sh exec huginn --no-argv -E "${0}" "${@}" #! huginn import Algorithms as algo; main() { x = "{} bottle{} of beer on the wall,\n" "{} bottle{} of beer.\n" "Take one down, pass it around,\n" "{} bottle{} of beer on the wall.\n\n"; for ( n : algo.range( 99, 0, -1 ) ) { bot = n > 0 ? n : "No"; plu = n != 1 ? "s" : ""; print( x.format( bot, plu, bot, plu, n > 1 ? n - 1 : "No", n != 2 ? "s" : "" ) ); } print( "No bottles of beer on the wall,\n" "No bottles of beer.\n" "Go to the store, buy some more,\n" "99 bottles of beer on the wall.\n" ); return ( 0 ); }
Following sample is implementation of a trie based solver for popular board game named Boggle.
#! /bin/sh exec huginn --rapid-start -E "${0}" "${@}" #! huginn import Algorithms as algo; import Mathematics as math; import FileSystem as fs; class DictionaryNode { _finished = false; _next = lookup(); add_suffix( suffix_ ) { if ( size( suffix_ ) == 0 ) { _finished = true; } else { char = suffix_[0]; if ( ! _next.has_key( char ) ) { _next[char] = DictionaryNode(); } _next[char].add_suffix( suffix_[1:] ); } } get( char_ ) { return ( _next.get( char_, none ) ); } print_all( prefix_ = "" ) { if ( _finished ) { print( prefix_ + "\n" ); } for ( char : _next ) { _next[char].print_all( prefix_ + string( char ) ); } } } class Cube { _letter = none; _visited = false; _neighbors = []; constructor( letter_ ) { _letter = letter_; } } class Board { _cubes = none; _size = 0; constructor( letters_ ) { len = integer( math.square_root( real( size( letters_ ) ) ) ); if ( len * len != size( letters_ ) ) { throw Exception( "Bad board definition!" ); } _size = len; _cubes = []; for ( l : letters_ ) { _cubes.push( Cube( l ) ); } deltas = [ ( -1, -1 ), ( -1, 0 ), ( -1, 1 ), ( 0, -1 ), ( 0, 1 ), ( 1, -1 ), ( 1, 0 ), ( 1, 1 ) ]; for ( x : algo.range( len ) ) { for ( y : algo.range( len ) ) { for ( d : deltas ) { nx = x + d[0]; ny = y + d[1]; if ( ( nx >= 0 ) && ( nx < len ) && ( ny >= 0 ) && ( ny < len ) ) { get_cube( x, y )._neighbors.push( observe( get_cube( nx, ny ) ) ); } } } } } get_cube( x_, y_ ) { return ( _cubes[y_ * _size + x_] ); } solve( dictionary_ ) { result = set(); for ( cube : _cubes ) { solve_recursive( result, "", cube, dictionary_ ); } return ( algo.sorted( result, size ) ); } solve_recursive( result, prefix_, cube_, dictNode_ ) { nextNode = dictNode_.get( cube_._letter ); if ( nextNode == none ) { return; } cube_._visited = true; newPrefix = prefix_ + string( cube_._letter ); if ( nextNode._finished && ( size( newPrefix ) >= 3 ) ) { result.insert( newPrefix ); } for ( n : cube_._neighbors ) { neighbor = use( n ); if ( ! neighbor._visited ) { solve_recursive( result, newPrefix, neighbor, nextNode ); } } cube_._visited = false; } } main( argv_ ) { dictionaryRoot = DictionaryNode(); dictFile = fs.open( argv_[1], fs.reading() ); for ( line : dictFile ) { dictionaryRoot.add_suffix( line.strip() ); } // dictionaryRoot.print_all(); print( "[ OK ] Ready\n" ); letters = ""; while ( ( line = input() ) != none ) { line = line.strip(); if ( ( size( letters ) > 0 ) && ( size( line ) == 0 ) ) { print( "[ OK ] Solving\n" ); try { board = Board( letters ); for ( word : board.solve( dictionaryRoot ) ) { print( "(" + string( size( word ) ) + ") " + word + "\n" ); } print( "[ OK ] Solved\n" ); } catch ( Exception e ) { print( e.message() + "\n" ); } letters = ""; } else { letters += line; } } return ( 0 ); } /* vim: set ft=huginn: */
Implementaion of Deadfish interpreter.
#! /bin/sh exec huginn --no-argv -E "${0}" "${@}" #! huginn main() { accumulator = 0; print( ">> " ); while ( ( line = input() ) != none ) { for ( c : line.strip() ) { if ( ( accumulator == -1 ) || ( accumulator == 256 ) ) { /* now, that's just a crazy talk */ accumulator = 0; } switch ( c ) { case ( 'i' ): { accumulator += 1; } break; case ( 'd' ): { accumulator -= 1; } break; case ( 's' ): { accumulator *= accumulator; } break; case ( 'o' ): { print( "{}\n".format( accumulator ) ); } break; default: { print( "Unrecognized command.\n" ); } } } print( ">> " ); } print( "\n" ); return ( accumulator ); }
Calculate a Digital root of a number.
#! /bin/sh exec huginn -E "${0}" "${@}" #! huginn main( argv_ ) { if ( size( argv_ ) < 2 ) { throw Exception( "usage: digital-root {NUM}" ); } n = argv_[1]; if ( ( size( n ) == 0 ) || ( n.find_other_than( "0123456789" ) >= 0 ) ) { throw Exception( "{} is not a number".format( n ) ); } shift = integer( '0' ) + 1; acc = 0; for ( d : n ) { acc = 1 + ( acc + integer( d ) - shift ) % 9; } print( "{}\n".format( acc ) ); return ( 0 ); }
Calculate n! for an arbitrary n, or output the sequence (1!, 2!, 3!,...), where n! = 1*2*...*n.
#! /bin/sh exec huginn -E "${0}" "${@}" #! huginn import Algorithms as algo; main( argv_ ) { if ( size( argv_ ) < 2 ) { print( "Not enough arguments," " you have to specify a number\n" ); return ( 1 ); } n = integer( argv_[1] ); if ( n < 0 ) { print( "you must specify non-negative number\n" ); return ( 2 ); } for ( n : algo.range( 0, n + 1 ) ) { print( "{}! = {}\n".format( n, number( n )! ) ); } return ( 0 ); }
Output the Fibonacci numbers (1, 1, 2, 3, 5, 8, 13...) using either recursion or iteration.
#! /bin/sh exec huginn -E "${0}" "${@}" #! huginn import Algorithms as algo; fib( n_ ) { a = number( 0 ); b = number( 1 ); for ( _ : algo.range( algo.max( n_ - 1, 0 ) ) ) { a, b = ( b, a + b ); } if ( n_ == 0 ) { b = 0; } return ( b ); } main( argv_ ) { if ( size( argv_ ) < 2 ) { print( "Not enough arguments," " you have to specify a number\n" ); return ( 1 ); } n = integer( argv_[1] ); if ( n < 0 ) { print( "you must specify non-negative number\n" ); return ( 2 ); } for ( n : algo.range( 0, n + 1 ) ) { print( "fib({}) = {}\n".format( n, fib( n ) ) ); } return ( 0 ); }
Solution for Fizz Buzz problem.
#! /bin/sh exec huginn -E "${0}" "${@}" #! huginn import Algorithms as algo; main( argv_ ) { if ( size( argv_ ) < 2 ) { throw Exception( "usage: fizzbuzz {NUM}" ); } top = integer( argv_[1] ); for ( i : algo.range( 1, top + 1 ) ) { by3 = ( i % 3 ) == 0; by5 = ( i % 5 ) == 0; if ( by3 ) { print( "fizz" ); } if ( by5 ) { print( "buzz" ); } if ( ! ( by3 || by5 ) ) { print( i ); } print( "\n" ); } return ( 0 ); }
Print "Hello, world!".
#! /bin/sh exec huginn --no-argv -E "${0}" "${@}" #! huginn main() { print( "Hello World!\n" ); return ( 0 ); }
Print word histogram of stream read from standard input.
#! /bin/sh exec huginn -E --no-argv "${0}" "${@}" #! huginn import Algorithms as algo; import Text as text; main() { hist = {}; while ( ( line = input() ) != none ) { for ( w : text.split( line.strip().to_lower(), " " ) ) { v = hist.ensure( w, 0 ); v += 1; } } hist = algo.sorted( hist.values(), @(_){ -_[1]; } ); for ( h : hist ) { print( "{:6d} {}\n".format( h[1], h[0] ) ); } return ( 0 ); }
Print a Mandelbrot set on the console.
#! /bin/sh exec huginn -E "${0}" "${@}" #! huginn import Algorithms as algo; import Mathematics as math; import OperatingSystem as os; mandelbrot( x, y ) { c = math.Complex( x, y ); z = math.Complex( 0., 0. ); s = -1; for ( i : algo.range( 50 ) ) { z = z * z + c; if ( | z | > 2. ) { s = i; break; } } return ( s ); } main( argv_ ) { imgSize = term_size( argv_ ); yRad = 1.2; yScale = 2. * yRad / real( imgSize[0] ); xScale = 3.3 / real( imgSize[1] ); glyphTab = [ ".", ":", "-", "+", "+" ].resize( 12, "*" ).resize( 26, "%" ).resize( 50, "@" ).push( " " ); for ( y : algo.range( imgSize[0] ) ) { line = ""; for ( x : algo.range( imgSize[1] ) ) { line += glyphTab[ mandelbrot( xScale * real( x ) - 2.3, yScale * real( y ) - yRad ) ]; } print( line + "\n" ); } return ( 0 ); } term_size( argv_ ) { lines = 25; columns = 80; if ( size( argv_ ) == 3 ) { lines = integer( argv_[1] ); columns = integer( argv_[2] ); } else { envLines = os.env( "LINES" ); envColumns = os.env( "COLUMNS" ); if ( ( envLines != none ) && ( envColumns != none ) ) { lines = integer( envLines ); columns = integer( envColumns ); } else { lines = integer( invoke( ( "/usr/bin/tput", "lines" ) ) ) - 1; columns = integer( invoke( ( "/usr/bin/tput", "cols" ) ) ) - 1; } } lines -= 1; columns -= 1; return ( ( lines, columns ) ); } invoke( cmd_ ) { p = os.spawn( cmd_... ); res = ""; out = p.out(); while ( ( line = out.read_line() ) != none ) { res += line; } return ( res ); }
Soution for Narcissist problem.
#! /bin/sh exec huginn --no-argv -E "${0}" #! huginn main() { c = "#! /bin/sh{1}~" "exec huginn --no-argv -E {3}${{0}}{3}{1}#! huginn{1}{1}~" "main() {{{1}{2}c = {3}{0}{3};{1}~" "{2}s = {3}{3};{1}~" "{2}while ( ( line = input() ) != none ) {{{1}~" "{2}{2}s += line;{1}~" "{2}}}{1}~" "{2}self = copy( c ).replace( {3}{5}{3}, {3}{3} )~" ".format({1}{2}{2}c.replace( {3}{5}{3}, ~" "{3}{5}{4}{3}{4}n{4}t{4}t{4}{3}{3} ), ~" "{3}{4}n{3}, {3}{4}t{3}, {3}{4}{3}{3}, {3}{4}{4}{3}, ~" "{3}{5}{3}{1}{2});{1}~" "{2}print( s == self ? {3}1{4}n{3} : {3}0{4}n{3} );{1}}}{1}{1}"; s = ""; while ( ( line = input() ) != none ) { s += line; } self = copy( c ).replace( "~", "" ).format( c.replace( "~", "~\"\n\t\t\"" ), "\n", "\t", "\"", "\\", "~" ); print( s == self ? "1\n" : "0\n" ); }
Calculate π to an arbitrary precision.
#! /bin/sh exec huginn -E "${0}" "${@}" #! huginn import Mathematics as math; main( argv_ ) { if ( size( argv_ ) >= 2 ) { prec = integer( argv_[1] ); print( "pi = {}\n".format( math.pi( number, prec ) ) ); } else { print( "you must specify a positive number\n" ); } return ( 0 ); }
Program that can be read in reverse and still have exactly the same meaning (Palindrome).
#! /bin/sh exec huginn --no-argv -E "${0}" #! huginn main() { print( "Hello World!\n" ); return ( 0 ); } //*/ /*// } ;) 0 ( nruter ;) "n\!dlroW olleH" (tnirp { )(niam nniguh !# "}0{$" E- vgra-on-- nniguh cexe hs/nib/ !#
Output program's own source code.
#! /bin/sh exec huginn --no-argv -E "${0}" #! huginn main() { c = "#! /bin/sh{1}~" "exec huginn --no-argv -E {3}${{0}}{3}{1}#! huginn{1}{1}~" "main() {{{1}{2}c = {3}{0}{3};{1}{2}print({1}~" "{2}{2}copy( c ).replace( {3}{5}{3}, {3}{3} )~" ".format({1}{2}{2}{2}c.replace( {3}{5}{3}, ~" "{3}{5}{4}{3}{4}n{4}t{4}t{4}{3}{3} ), ~" "{3}{4}n{3}, {3}{4}t{3}, {3}{4}{3}{3}, {3}{4}{4}{3}, ~" "{3}{5}{3}{1}{2}{2}){1}{2});{1}}}{1}{1}"; print( copy( c ).replace( "~", "" ).format( c.replace( "~", "~\"\n\t\t\"" ), "\n", "\t", "\"", "\\", "~" ) ); }
Apply the ROT13 cipher to the input text.
#! /bin/sh exec huginn --no-argv -E "${0}" "${@}" #! huginn import Algorithms as algo; rot13char( c ) { if ( c.is_alpha() ) { x = integer( c ); m = integer( c.is_upper() ? 'A' : 'a' ); x -= m; x += 13; x %= 26; x += m; c = character( x ); } return ( c ); } rot13( s ) { return ( algo.reduce( algo.map( algo.map( s, rot13char ), string ), @( r, c ) { r + c; } ) ); } main() { while ( ( line = input() ) != none ) { print( rot13( line.strip() ) + "\n" ); } }
Implementation of minimalistic Scheme interpreter.
#! /bin/sh exec huginn -E "${0}" "${@}" #! huginn import Algorithms as algo; import Mathematics as math; import Text as text; import FileSystem as fs; class Env { _data = {}; _outer = none; constructor( params_ = (), args_ = (), outer_ = none ) { for ( i : algo.range( algo.min( size( params_ ), size( args_ ) ) ) ) { _data[params_[i]] = args_[i]; } _outer = outer_; } update( dct_ ) { _data.update( dct_ ); } set_kv( k, v ) { _data[k] = v; } find( var_ ) { return ( _data.has_key( var_ ) ? _data : _outer.find( var_ ) ); } } cc( x, y ) { tx = type( x ); if ( tx == boolean ) { x = x ? 1 : 0; tx = integer; } ty = type( y ); return ( ( ty == real ) && ( tx == integer ) ? real( x ) : x ); } standard_env() { env = Env(); bc = type( @[env](){} ); env.update( { "+": @( x, y ){ cc( x, y ) + cc( y, x ); }, "-": @( x, y ){ cc( x, y ) - cc( y, x ); }, "*": @( x, y ){ cc( x, y ) * cc( y, x ); }, "/": @( x, y ){ cc( x, y ) / cc( y, x ); }, ">": @( x, y ){ cc( x, y ) > cc( y, x ); }, "<": @( x, y ){ cc( x, y ) < cc( y, x ); }, ">=": @( x, y ){ cc( x, y ) >= cc( y, x ); }, "<=": @( x, y ){ cc( x, y ) <= cc( y, x ); }, "=": @( x, y ){ cc( x, y ) == cc( y, x ); }, "pow": @( x, y ){ real( x ) ^ real( y ); }, "abs": @( x ) { | x |; }, "append": @( x, y ){ cc( x, y ) + cc( y, x ); }, "apply": @( x, y ){ x( y... ); }, "begin": @( x... ){ x[-1]; }, "car": @( x ) { x[0]; }, "cdr": @( x ) { x[1:]; }, "cons": @( x, y ){ [x] + y; }, "eq?": @( x, y ){ cc( x, y ) == cc( y, x ); }, "equal?": @( x, y ){ cc( x, y ) == cc( y, x ); }, "length": @( x ) { size( x ); }, "list": @( x... ){ algo.materialize( x, list ); }, "list?": @( x ) { type( x ) == list; }, "map": @( x, y ){ algo.materialize( algo.map( y, x ), list ); }, "max": algo.max, "min": algo.min, "not": @( x ) { !x; }, "null?": @( x ) { x == []; }, "number?": @( x ) { t = type( x ); ( t == real ) || ( t == integer ); }, "procedure?": @[bc]( x ) { type( x ) == bc; }, "round": @( x ) { type( x ) == real ? math.round( x ) : x; }, "symbol?": @( x ) { type( x ) == string; }, "sqrt": @( x ) { math.square_root( real( x ) ); }, "sin": @( x ) { math.sinus( real( x ) ); }, "cos": @( x ) { math.cosinus( real( x ) ); }, "pi": math.pi( real ), "π": math.pi( real ) } ); return ( env ); } to_bool( v ) { switch ( type( v ) ) { case ( integer ): { return ( v != 0 ); } case ( list ): {} case ( string ): { return ( size( v ) > 0 ); } } return ( v ); } eval( x, env ) { if ( type( x ) == string ) { return ( env.find( x )[x] ); } else if ( type( x ) != list ) { return ( x ); } h = x[0]; his = type( h ) == string; if ( his ) { switch ( h ) { case ( "quote" ): { return ( x[1] ); } case ( "if" ): { test = x[1]; conseq = x[2]; alt = x[3]; exp = to_bool( eval( test, env ) ) ? conseq : alt; return ( eval( exp, env ) ); } case ( "lambda" ): { return ( @[_params: x[1], _body: x[2], _env: env] ( args_... ) { eval( _body, Env( _params, args_, _env ) ); } ); } case ( "define" ): { env.set_kv( x[1], eval( x[2], env ) ); return ( none ); } case ( "set!" ): { env.find( x[1] )[ x[1] ] = eval( x[2], env ); return ( none ); } } } proc = eval( h, env ); args = algo.materialize( algo.map( x[1:], @[env]( _ ){ eval( _, env ); } ), tuple ); return ( proc( args... ) ); } atom( token ) { a = token; try { return ( token.find( "." ) >= 0 ? real( token ) : integer( token ) ); } catch ( Exception e ) { } return ( a ); } parse( tokens ) { if ( size( tokens ) == 0 ) { throw Exception( "unexpected EOF" ); } token = tokens[0]; tokens.pop_front(); if ( token == "(" ) { exprs = []; while ( tokens[0] != ")" ) { exprs.push( parse( tokens ) ); } tokens.pop_front(); return ( exprs ); } else if ( token == ")" ) { throw Exception( "unexpected )" ); } else { return ( atom( token ) ); } } is_quoted( token_ ) { isQuoted = false; l = size( token_ ); if ( l >= 2 ) { h = token_[0]; isQuoted = ( h == token_[-1] ) && ( ( h == '"' ) || ( h == '\'' ) ); } return ( isQuoted ); } split_quotes( str_ ) { tokens = deque(); singleQuoteCount = 0; doubleQuoteCount = 0; escaped = false; token = ""; white = " \t\r\n\v\f\a\b"; for ( c : str_ ) { if ( escaped ) { token += string( c ); escaped = false; continue; } paren = ( c == '(' ) || ( c == ')' ); if ( ( white.find( string( c ) ) >= 0 ) || paren ) { inQuotes = ( ( doubleQuoteCount % 2 ) != 0 ) || ( ( singleQuoteCount % 2 ) != 0 ); if ( ! inQuotes ) { if ( size( token ) != 0 ) { t = copy( token ).replace( "\\ ", " " ).replace( "\\\t", "\t" ); comented = false; if ( ! is_quoted( t ) ) { comentPos = t.find( ";" ); if ( comentPos >= 0 ) { t = t[:comentPos]; comented = true; } } if ( size( t ) > 0 ) { tokens.push( t ); } token.clear(); if ( comented ) { break; } } if ( paren ) { tokens.push( string( c ) ); } continue; } } token += string( c ); if ( c == '\\' ) { escaped = true; } else if ( ( c == '"' ) && ( ( singleQuoteCount % 2 ) == 0 ) ) { doubleQuoteCount += 1; } else if ( ( c == '\'' ) && ( ( doubleQuoteCount % 2 ) == 0 ) ) { singleQuoteCount += 1; } } if ( size( token ) != 0 ) { tokens.push( token.replace( "\\ ", " " ).replace( "\\\t", "\t" ) ); } if ( ( ( doubleQuoteCount % 2 ) != 0 ) || ( ( singleQuoteCount % 2 ) != 0 ) ) { throw Exception( "Unterminated quotes" ); } return ( tokens ); } interpret( lineFeed, env ) { while ( ( line = lineFeed() ) != none ) { t = split_quotes( line ); if ( size( t ) == 0 ) { continue; } e = parse( t ); /* print( "{}\n".format( e ) ); */ r = eval( e, env ); if ( r != none ) { print( "{}\n".format( scm_to_string( r ) ) ); } } } scm_to_string( e ) { if ( type( e ) == list ) { e = "({})".format( text.join( algo.materialize( algo.map( e, string ), list ), " " ) ); } return ( e ); } main( argv_ ) { env = standard_env(); if ( size( argv_ ) > 1 ) { for ( a : argv_[1:] ) { f = fs.open( a, fs.reading() ); interpret( @[f](){ f.read_line(); }, env ); } } else { interpret( input, env ); } return ( 0 ); }